Skip to content

Instantly share code, notes, and snippets.

@aarroyoc
Created April 5, 2022 22:23
Show Gist options
  • Save aarroyoc/b4cdb3a660852e25570d8fb52348a000 to your computer and use it in GitHub Desktop.
Save aarroyoc/b4cdb3a660852e25570d8fb52348a000 to your computer and use it in GitHub Desktop.
Snake wxPython
import wx
from enum import Enum
import random
from collections import deque
import xml.etree.ElementTree as ET
GRID_SIZE = 20
WIDTH = 650
HEIGHT = 690
TABLE_SIZE = 30
class MapLoader:
def __init__(self):
self.root = ET.parse("snake.xml")
def list_levels(self):
return [map_data.get("name") for map_data in self.root.findall("map")]
def load_level(self, name):
map_data = self.root.find(f"./map[@name='{name}']")
def load_wall(wall):
x = int(wall.get("x"))
y = int(wall.get("y"))
return Cell(x, y)
cells= [load_wall(wall) for wall in map_data.findall("wall")]
return Walls(cells)
class Direction(Enum):
LEFT = 1
RIGHT = 2
UP = 3
DOWN = 4
class Cell:
def __init__(self, x, y):
self.x = x
self.y = y
def __hash__(self):
return hash((self.x, self.y))
def __eq__(self, other):
if other is None:
return False
return self.x == other.x and self.y == other.y
class Snake:
def __init__(self):
self._cells = deque([Cell(5,4), Cell(5,5)])
self._go_dir = {
Direction.RIGHT: self.go_right,
Direction.LEFT: self.go_left,
Direction.UP: self.go_up,
Direction.DOWN: self.go_down
}
self._grow = []
def head(self):
return self._cells[-1]
def length(self):
return len(self._cells)
def go(self, direction):
self._go_dir[direction]()
def grow(self):
self._grow.append(self.head())
def _check_grow(self):
if self._cells[0] in self._grow:
self._grow.remove(self._cells[0])
else:
self._cells.popleft()
def internal_collision(self):
return len(set(self._cells)) != len(self._cells)
def go_right(self):
self._check_grow()
front = self.head()
self._cells.append(Cell(front.x+1, front.y))
def go_left(self):
self._check_grow()
front = self.head()
self._cells.append(Cell(front.x-1, front.y))
def go_up(self):
self._check_grow()
front = self.head()
self._cells.append(Cell(front.x, front.y-1))
def go_down(self):
self._check_grow()
front = self.head()
self._cells.append(Cell(front.x, front.y+1))
def draw(self, dc):
dc.SetBrush(wx.Brush('#ff0000'))
for cell in self._cells:
dc.DrawRectangle(GRID_SIZE*cell.x, GRID_SIZE*cell.y, GRID_SIZE, GRID_SIZE)
class FoodBag:
def __init__(self):
self._foods = []
def generate_random_food(self, valid_cells):
cell = random.choice(list(set(valid_cells) - set(self._foods)))
if len(self._foods) < 10:
self._foods.append(cell)
def food_at(self, cell):
return cell in self._foods
def remove(self, cell):
self._foods.remove(cell)
def draw(self, dc):
dc.SetBrush(wx.Brush("#00ff00"))
for food in self._foods:
dc.DrawRectangle(GRID_SIZE*food.x, GRID_SIZE*food.y, GRID_SIZE, GRID_SIZE)
class Walls:
def __init__(self, walls = []):
self._walls = walls
def check_collision(self, cell):
return cell in self._walls
def empty_cells(self):
return [Cell(x, y) for x in range(TABLE_SIZE) for y in range(TABLE_SIZE) if Cell(x,y) not in self._walls]
def draw(self, dc):
dc.SetBrush(wx.Brush("#000000"))
for wall in self._walls:
dc.DrawRectangle(GRID_SIZE*wall.x, GRID_SIZE*wall.y, GRID_SIZE, GRID_SIZE)
class StartWindow(wx.Frame):
def __init__(self):
super(StartWindow, self).__init__(None, title="SNAKE", size=(200,200))
self.panel = wx.Panel(self)
self.box = wx.BoxSizer(wx.VERTICAL)
levels = MapLoader().list_levels()
self.list_box = wx.ListBox(self.panel, size = (75, 125), choices=levels, style= wx.LB_SINGLE)
self.play_button = wx.Button(self.panel, label = "Jugar")
self.play_button.Disable()
self.Bind(wx.EVT_LISTBOX, self.on_listbox, self.list_box)
self.Bind(wx.EVT_BUTTON, self.on_click, self.play_button)
self.Bind(wx.EVT_CLOSE, self.on_close)
self.box.Add(self.list_box, 0, wx.EXPAND)
self.box.Add(self.play_button, 1, wx.EXPAND)
self.panel.SetSizer(self.box)
self.panel.Fit()
self.selected = None
self.main_window = None
def on_close(self, event):
self.Destroy()
def on_click(self, event):
if self.selected is not None:
self.main_window = MainWindow(self.selected)
self.main_window.ShowModal()
def on_listbox(self, event):
self.selected = self.list_box.GetString(self.list_box.GetSelection())
self.play_button.Enable()
class MainWindow(wx.Dialog):
def __init__(self, level):
super(MainWindow, self).__init__(None, title="Snake", size=(WIDTH, HEIGHT))
self.panel = wx.Panel(self)
self.snake_timer = wx.Timer(self)
self.food_timer = wx.Timer(self)
self.foods = FoodBag()
self.snake = Snake()
self.walls = MapLoader().load_level(level)
self.direction = Direction.UP
self.look_at = Direction.UP
self.end = False
self.panel.SetFocus()
self.panel.Bind(wx.EVT_KEY_DOWN, self.on_key_down)
self.Bind(wx.EVT_TIMER, self.update, self.snake_timer)
self.Bind(wx.EVT_TIMER, self.food_update, self.food_timer)
self.panel.Bind(wx.EVT_PAINT, self.on_paint)
self.Bind(wx.EVT_CLOSE, self.on_close)
self.snake_timer.Start(250)
self.food_timer.Start(1500)
def on_close(self, event):
self.snake_timer.Stop()
self.food_timer.Stop()
self.Destroy()
def update(self, event):
if not self.end:
snake_head = self.snake.head()
if self.foods.food_at(snake_head):
self.foods.remove(snake_head)
self.snake.grow()
self.snake.go(self.direction)
self.look_at = self.direction
if self.snake.internal_collision() or self.walls.check_collision(snake_head):
self.end = True
score = self.snake.length()
wx.MessageBox(f"Fin del juego\nPuntuación: {score}", "Final", wx.OK)
self.Close()
self.panel.Refresh()
def on_paint(self, event):
dc = wx.PaintDC(self.panel)
dc.Clear()
self.walls.draw(dc)
self.foods.draw(dc)
self.snake.draw(dc)
def food_update(self, event):
if not self.end:
self.foods.generate_random_food(self.walls.empty_cells())
def on_key_down(self, event):
keycode = event.GetKeyCode()
if keycode == wx.WXK_LEFT and self.look_at != Direction.RIGHT:
self.direction = Direction.LEFT
elif keycode == wx.WXK_RIGHT and self.look_at != Direction.LEFT:
self.direction = Direction.RIGHT
elif keycode == wx.WXK_UP and self.look_at != Direction.DOWN:
self.direction = Direction.UP
elif keycode == wx.WXK_DOWN and self.look_at != Direction.UP:
self.direction = Direction.DOWN
def main():
app = wx.App()
win = StartWindow()
win.Show()
app.MainLoop()
if __name__ == "__main__":
main()
<?xml version="1.0"?>
<snake>
<map name="Basic">
<wall x="0" y="0"/>
<wall x="1" y="0"/>
<wall x="2" y="0"/>
<wall x="3" y="0"/>
<wall x="4" y="0"/>
<wall x="5" y="0"/>
<wall x="6" y="0"/>
<wall x="7" y="0"/>
<wall x="8" y="0"/>
<wall x="9" y="0"/>
<wall x="10" y="0"/>
<wall x="11" y="0"/>
<wall x="12" y="0"/>
<wall x="13" y="0"/>
<wall x="14" y="0"/>
<wall x="15" y="0"/>
<wall x="16" y="0"/>
<wall x="17" y="0"/>
<wall x="18" y="0"/>
<wall x="19" y="0"/>
<wall x="20" y="0"/>
<wall x="21" y="0"/>
<wall x="22" y="0"/>
<wall x="23" y="0"/>
<wall x="24" y="0"/>
<wall x="25" y="0"/>
<wall x="26" y="0"/>
<wall x="27" y="0"/>
<wall x="28" y="0"/>
<wall x="29" y="0"/>
<wall x="0" y="29"/>
<wall x="1" y="29"/>
<wall x="2" y="29"/>
<wall x="3" y="29"/>
<wall x="4" y="29"/>
<wall x="5" y="29"/>
<wall x="6" y="29"/>
<wall x="7" y="29"/>
<wall x="8" y="29"/>
<wall x="9" y="29"/>
<wall x="10" y="29"/>
<wall x="11" y="29"/>
<wall x="12" y="29"/>
<wall x="13" y="29"/>
<wall x="14" y="29"/>
<wall x="15" y="29"/>
<wall x="16" y="29"/>
<wall x="17" y="29"/>
<wall x="18" y="29"/>
<wall x="19" y="29"/>
<wall x="20" y="29"/>
<wall x="21" y="29"/>
<wall x="22" y="29"/>
<wall x="23" y="29"/>
<wall x="24" y="29"/>
<wall x="25" y="29"/>
<wall x="26" y="29"/>
<wall x="27" y="29"/>
<wall x="28" y="29"/>
<wall x="29" y="29"/>
<wall y="1" x="0"/>
<wall y="2" x="0"/>
<wall y="3" x="0"/>
<wall y="4" x="0"/>
<wall y="5" x="0"/>
<wall y="6" x="0"/>
<wall y="7" x="0"/>
<wall y="8" x="0"/>
<wall y="9" x="0"/>
<wall y="10" x="0"/>
<wall y="11" x="0"/>
<wall y="12" x="0"/>
<wall y="13" x="0"/>
<wall y="14" x="0"/>
<wall y="15" x="0"/>
<wall y="16" x="0"/>
<wall y="17" x="0"/>
<wall y="18" x="0"/>
<wall y="19" x="0"/>
<wall y="20" x="0"/>
<wall y="21" x="0"/>
<wall y="22" x="0"/>
<wall y="23" x="0"/>
<wall y="24" x="0"/>
<wall y="25" x="0"/>
<wall y="26" x="0"/>
<wall y="27" x="0"/>
<wall y="28" x="0"/>
<wall y="1" x="29"/>
<wall y="2" x="29"/>
<wall y="3" x="29"/>
<wall y="4" x="29"/>
<wall y="5" x="29"/>
<wall y="6" x="29"/>
<wall y="7" x="29"/>
<wall y="8" x="29"/>
<wall y="9" x="29"/>
<wall y="10" x="29"/>
<wall y="11" x="29"/>
<wall y="12" x="29"/>
<wall y="13" x="29"/>
<wall y="14" x="29"/>
<wall y="15" x="29"/>
<wall y="16" x="29"/>
<wall y="17" x="29"/>
<wall y="18" x="29"/>
<wall y="19" x="29"/>
<wall y="20" x="29"/>
<wall y="21" x="29"/>
<wall y="22" x="29"/>
<wall y="23" x="29"/>
<wall y="24" x="29"/>
<wall y="25" x="29"/>
<wall y="26" x="29"/>
<wall y="27" x="29"/>
<wall y="28" x="29"/>
</map>
</snake>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment