diff options
Diffstat (limited to 'Game.py')
| -rw-r--r-- | Game.py | 124 |
1 files changed, 76 insertions, 48 deletions
@@ -1,4 +1,9 @@ +from typing import Optional, Union + +import Vec + +from OptionalUtils import exists from Snake import Snake from Vec import Vec2 import Vec @@ -7,27 +12,28 @@ from Door import Door from PressurePlate import PressurePlate from Trail import Trail from Walls import Walls +Static = Union[PressurePlate, Trail, Door] + 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 + def __init__(self, levels: list[str]): + self.snake: Optional[Snake] = None + self.boxes: list[Box] = [] + self.statics: list[Static] = [] + self.walls: Walls = Walls.empty() + self.levelIn: Optional['Vec2'] = None self.level = -1 self.levels = [] self.levels = levels self.nextLevel() - def nextLevel(self): + def nextLevel(self) -> None: self.level += 1 - self.walls = Walls.fromString(self.levels[self.level]) self.snake = None self.statics = [] @@ -38,24 +44,27 @@ class Game: x = 0 for char in line: if char == "b": - self.boxes.append(Box(Vec2(x, y))) + self.boxes.append(Box(Vec.Vec2(x, y))) elif char == "D": - if self.doorAt(Vec2(x, y)) == None: - self.statics.append(Door(Vec2(x, y))) + if self.doorAt(Vec.Vec2(x, y)) == None: + self.statics.append(Door(Vec.Vec2(x, y))) elif char == "_": - self.parseSwitchTrail(self.levels[self.level], Vec2(x, y)) + self.parseSwitchTrail(self.levels[self.level], Vec.Vec2(x, y)) elif char == "I": - self.levelIn = Vec2(x, y) + self.levelIn = Vec.Vec2(x, y) x += 1 y += 1 - self.snake = Snake([self.levelIn, self.levelIn, self.levelIn, self.levelIn], self) + match self.levelIn: + case None: raise ValueError("level must have entrance") + case Vec2: 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') + def parseSwitchTrail(self, levelStr: str, platePos: Vec2) -> None: + level = levelStr.split('\n') startSwitch = None if level[platePos.y][platePos.x] == "_": startSwitch = PressurePlate(platePos) @@ -64,7 +73,7 @@ class Game: raise ValueError visited = [] - def inner(pos: Vec2): + def inner(pos: Vec2) -> Optional[Static]: visited.append(pos) if level[pos.y][pos.x] == "+": directions = filter(lambda p: not p in visited, @@ -72,20 +81,27 @@ class Game: [Vec.up, Vec.down, Vec.left, Vec.right] ) ) - nextPart = filter(lambda x: x != None, map(lambda x: inner(x), directions)) + nextIter = filter(lambda x: x != None, map(lambda x: inner(x), directions)) + nextPart = next(nextIter) #assert len(nextPart) == 1 - trail = Trail(pos, next(nextPart)) - self.statics.append(trail) - return trail + match nextPart: + case Trail() | Door(): + trail = Trail(pos, nextPart) + self.statics.append(trail) + return trail + case _: raise ValueError(f"trails must be followed by doors or trails, not {nextPart}") 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) + nextIter = filter(lambda x: x != None, map(lambda x: inner(x), directions)) + for tr in nextIter: + match tr: + case Door() | Trail(): startSwitch.addTrail(tr) + case _: raise ValueError(f"plates must be followed by doors or trails, not {tr}") + elif level[pos.y][pos.x] == "D": door = self.doorAt(pos) if door != None: @@ -94,6 +110,8 @@ class Game: door = Door(pos) self.statics.append(door) return door + return None + inner(platePos) for static in self.statics: match static: @@ -101,20 +119,28 @@ class Game: trailString = map( lambda x: x.pos.toString(), static._trails) - def width(self): return self.walls.width() - def height(self): return self.walls.height() + def width(self) -> int: + return self.walls.width() + def height(self) -> int: + 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): - self.snake.move() - - lastSnakeCell = self.snake.cells[0]#[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 + match self.snake: + case Snake(): + if pos in self.snake.cells: + return True + return pos in map(lambda box: box.pos, self.boxes) + + def tick(self) -> None: + match self.snake: + case Snake(): + self.snake.move() + + lastSnakeCell = self.snake.cells[0]#[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 for static in self.statics: match static: @@ -131,11 +157,13 @@ class Game: static.close() - def isLost(self): - return self.snake.hasCollided + def isLost(self) -> bool: + match self.snake: + case Snake(): return self.snake.hasCollided + case _: return False - def doorAt(self, pos: Vec2): + def doorAt(self, pos: Vec2) -> Optional[Door]: for static in self.statics: match static: case Door(): @@ -152,7 +180,7 @@ class Game: res = res or (static.pos == pos and not static.isOpen()) return res - def switchAt(self, pos: Vec2): + def switchAt(self, pos: Vec2) -> Optional[PressurePlate]: for static in self.statics: match static: case PressurePlate(): @@ -165,14 +193,14 @@ class Game: 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: + elif exists(self.snake, lambda s: pos in s.cells): return False - elif boxAt != None: - if self.enter(pos + inDir, inDir): - boxAt.pos = pos + inDir - return True - else: - return False else: - return True - + match boxAt: + case None: return True + case _: + if self.enter(pos + inDir, inDir): + boxAt.pos = pos + inDir + return True + else: + return False |
