How to Fix an Index Error in My Snake Game

When I started building my own version of the classic Snake game using Python and Tkinter, I was pretty excited. But like any good coding adventure, it wasn’t long before I hit my first real wall an IndexError. Specifically:

IndexError: list index out of range

I wasn’t sure what I had done wrong. I was trying to randomly place food on the 10×10 grid, but the game would crash before even starting. It took me a bit of digging, but here’s what I found and how I fixed it.

The Problem

I was getting the error at this line in my code:

self.square_list[self.foody][self.foodx].config(bg='red')

This line is supposed to place the red-colored food square on a random cell. But for some reason, it was trying to access a cell that didn’t exist.

The Cause

The issue was with how I built the self.square_list. It’s a 2D list meant to represent the grid, where each row is a list of square widgets. But here’s the mistake I made:

for row, rowList in enumerate(self.game):
for column, columnEntry in enumerate(self.game):
square = tkinter.Label(self.screen, text=' ', relief='raised')
square.grid(row=row, column=column)
square.config(bg='black')
self.square_list[row].append(square) # This line fails!

The problem? I never actually initialized self.square_list[row]. I had created an empty list of rows but didn’t fill them in correctly.

The Fix

Here’s how I corrected the logic:

for row in range(10):
square_row = []
for column in range(10):
square = tkinter.Label(self.screen, text=' ', relief='raised')
square.grid(row=row, column=column)
square.config(bg='black')
square_row.append(square)
self.square_list.append(square_row)

By properly building each square_row and appending it to self.square_list, I made sure every [row][column] reference would actually exist.

Other Issues I Found and Fix

Direction Binding Were Broken

Initially, I wrote:

self.screen.bind('w', self.up())  # 

This actually calls the function right away instead of assigning it to the keypress. The fix:

self.screen.bind('w', lambda e: self.up())
self.screen.bind('s', lambda e: self.down())
self.screen.bind('a', lambda e: self.left())
self.screen.bind('d', lambda e: self.right())

Now the functions are only called when the player presses the keys.

Infinite Loop in __init__ Freezes GUI

Another major issue was using a while True: loop in the constructor:

while True:
time.sleep(self.delay)
self.update()

This blocked the GUI thread, causing the window to freeze. Instead, I used after() to schedule updates:

self.run_game()

def run_game(self):
self.update()
self.screen.after(int(self.delay * 1000), self.run_game)

Practice Functionalities I Added

Once the basics were stable, I added a few features to improve the game and sharpen my Python skills.

Game Over on Wall Collision

import tkinter.messagebox

if self.head.x > 9 or self.head.x < 0 or self.head.y > 9 or self.head.y < 0:
tkinter.messagebox.showinfo("Game Over", "You hit the wall!")
self.screen.destroy()

Game Over on Self-Collision

for segment in self.segments:
if self.head.x == segment.x and self.head.y == segment.y:
tkinter.messagebox.showinfo("Game Over", "You hit yourself!")
self.screen.destroy()

Score Tracking

In the constructor:

self.score = 0
self.score_label = tkinter.Label(self.screen, text="Score: 0", fg="white", bg="black")
self.score_label.grid(row=0, column=10)

And when food is eaten:

self.score += 1
self.score_label.config(text=f"Score: {self.score}")

Final Thought

Fixing the IndexError was just the beginning of making my Snake game run properly. Along the way, I learned how easy it is to accidentally misuse lists, how to properly bind keys in Tkinter, and why event-driven programming is critical in GUI development.

Related blog posts