from Snake import Snake from Vec import Vec2 import Vec from Box import Box from Door import Door from PressurePlate import PressurePlate from Trail import Trail from Walls import Walls class Game: """Class responsible for the main logic of a game. For a game, this will probably be a singleton.""" def __init__(self, levels): self.snake = None self.boxes = [] self.walls = None self.statics = [] self.levelIn = None self.level = -1 self.levels = [] self.levels = levels self.nextLevel() def nextLevel(self): self.level += 1 self.walls = Walls.fromString(self.levels[self.level]) self.snake = None self.statics = [] self.boxes = [] y = 0 for line in self.levels[self.level].split('\n'): x = 0 for char in line: if char == "b": self.boxes.append(Box(Vec2(x, y))) elif char == "D": if self.doorAt(Vec2(x, y)) == None: self.statics.append(Door(Vec2(x, y))) elif char == "_": self.parseSwitchTrail(self.levels[self.level], Vec2(x, y)) elif char == "I": self.levelIn = Vec2(x, y) x += 1 y += 1 self.snake = Snake([self.levelIn, self.levelIn, self.levelIn, self.levelIn], self) self.statics.append(Door(self.levelIn)) self.snake.heading = Vec.up def parseSwitchTrail(self, level: str, platePos: Vec2): level = level.split('\n') startSwitch = None if level[platePos.y][platePos.x] == "_": startSwitch = PressurePlate(platePos) self.statics.append(startSwitch) else: raise ValueError visited = [] def inner(pos: Vec2): visited.append(pos) if level[pos.y][pos.x] == "+": directions = filter(lambda p: not p in visited, map(lambda p: pos + p, [Vec.up, Vec.down, Vec.left, Vec.right] ) ) nextPart = filter(lambda x: x != None, map(lambda x: inner(x), directions)) #assert len(nextPart) == 1 trail = Trail(pos, next(nextPart)) self.statics.append(trail) return trail elif level[pos.y][pos.x] == "_" and pos == platePos: directions = filter(lambda p: not p in visited, map(lambda p: pos + p, [Vec.up, Vec.down, Vec.left, Vec.right] ) ) nextPart = filter(lambda x: x != None, map(lambda x: inner(x), directions)) for trail in nextPart: startSwitch.addTrail(trail) elif level[pos.y][pos.x] == "D": door = self.doorAt(pos) if door != None: return door door = Door(pos) self.statics.append(door) return door inner(platePos) for static in self.statics: match static: case PressurePlate(): trailString = map( lambda x: x.pos.toString(), static._trails) def width(self): return self.walls.width() def height(self): return self.walls.height() def _movableSolidAt(self, pos: Vec2) -> bool: return (pos in self.snake.cells) or (pos in map(lambda box: box.pos, self.boxes)) def tick(self): lastSnakeCell = self.snake.cells[len(self.snake.cells) - 1] if (lastSnakeCell.x < 0 or lastSnakeCell.x >= self.width()) or (lastSnakeCell.y < 0 or lastSnakeCell.y >= self.height()): self.nextLevel() return self.snake.move() for static in self.statics: match static: case PressurePlate(): if static.isActive() and not self._movableSolidAt(static.pos): static.deactivate() elif (not static.isActive()) and self._movableSolidAt(static.pos): static.activate() for static in self.statics: match static: case Door(): if static.isOpen() and not static.isActive(): if not self._movableSolidAt(static.pos): static.close() def isLost(self): return self.snake.hasCollided def doorAt(self, pos: Vec2): for static in self.statics: match static: case Door(): if static.pos == pos: return static return None def closedDoorAt(self, pos: Vec2) -> bool: res = False for static in self.statics: match static: case Door(): res = res or (static.pos == pos and not static.isOpen()) return res def switchAt(self, pos: Vec2): for static in self.statics: match static: case PressurePlate(): if static.pos == pos: return static return None def enter(self, pos: Vec2, inDir: Vec2) -> bool: boxAt = next(filter(lambda box: box.pos == pos, self.boxes), None) if self.walls.wallAt(pos) or self.closedDoorAt(pos): return False elif pos in self.snake.cells: return False elif boxAt != None: if self.enter(pos + inDir, inDir): boxAt.pos = pos + inDir return True else: return False else: return True