Created
April 5, 2022 22:23
-
-
Save aarroyoc/b4cdb3a660852e25570d8fb52348a000 to your computer and use it in GitHub Desktop.
Snake wxPython
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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