diff options
| -rw-r--r-- | Box.py | 1 | ||||
| -rw-r--r-- | Door.py | 9 | ||||
| -rw-r--r-- | Game.py | 164 | ||||
| -rw-r--r-- | GameView.py | 96 | ||||
| -rw-r--r-- | PressurePlate.py | 14 | ||||
| -rw-r--r-- | Snake.py | 8 | ||||
| -rw-r--r-- | Trail.py | 7 | ||||
| -rw-r--r-- | Vec.py | 6 | ||||
| -rw-r--r-- | Walls.py | 2 | ||||
| -rwxr-xr-x | main.py | 19 |
10 files changed, 211 insertions, 115 deletions
@@ -2,7 +2,6 @@ from Vec import Vec2 class Box: - pos = None def __init__(self, pos: Vec2): self.pos = pos @@ -1,17 +1,10 @@ class Door: - pos = None - - _open = False - - _active = False - - def __init__(self, pos, isOpen = False, isActive = False): self.pos = pos self._open = isOpen - self._active = False + self._active = isActive def isOpen(self): return self._open @@ -1,96 +1,133 @@ 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 Walls: - """Contains the walls of a game. - Supports iterating over the walls and checking if there is a wall at a specific coordinate. - Useful as a wrapper to later increase performance of these operations.""" +class Game: + """Class responsible for the main logic of a game. + For a game, this will probably be a singleton.""" + - _walls = [] + 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 __init__(self, walls): - self._walls = walls - def fromString(wallString): - walls = [] + 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 wallString.split('\n'): + for line in self.levels[self.level].split('\n'): x = 0 for char in line: - if char == "#": - walls.append(Vec2(x, y)) + 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 - return Walls(walls) - - def walls(self): - return self._walls - - def wallAt(self, pos): - return (pos in self._walls) - def width(self): - return max(self._walls, key=lambda p: p.x).x + 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 height(self): - return max(self._walls, key=lambda p: p.y).y + 1 + 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) -class Game: - """Class responsible for the main logic of a game. - For a game, this will probably be a singleton.""" - - snake = None - boxes = [] - walls = None - statics = [] - - def __init__(self): - self.snake = Snake([Vec2(6, 6), Vec2(6, 7), Vec2(6,8), Vec2(7,8), Vec2(8, 8), Vec2(9, 8)], self) - _box1 = Box(Vec2(11, 4)) - _box2 = Box(Vec2(3, 10)) - self.boxes = [_box1, _box2] - - self.statics = [Door(Vec2(0, 6)), Door(Vec2(6, 11)), PressurePlate(Vec2(6, 9))] - self.statics.append(Trail(Vec2(6,10), self.statics[1])) - self.statics[2].addTrail(self.statics[3]) - - self.walls = Walls.fromString("""############### -# # -# f # -# # -# b # -# # -D h # -# t # -# tttt # -# _ # -# b # -######D###### # -# f # -###############""") 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 (static.pos in self.snake.cells)) and (not (static.pos in map(lambda box: box.pos, self.boxes))): + if static.isActive() and not self._movableSolidAt(static.pos): static.deactivate() - elif (not static.isActive()) and ((static.pos in self.snake.cells) or (static.pos in map(lambda box: box.pos, self.boxes))): + 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 (static.pos in self.snake.cells or static.pos in map(lambda box: box.pos, self.boxes)): + if not self._movableSolidAt(static.pos): static.close() @@ -98,6 +135,15 @@ D h # 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: diff --git a/GameView.py b/GameView.py index 3b458d8..d2badfe 100644 --- a/GameView.py +++ b/GameView.py @@ -10,15 +10,77 @@ import pygame class GameView: - game = Game() - - cellWidth = 64 - - _tickTime = 700 - - _previousTick = None - - nextControlDirection = None + def __init__(self): + self.game = Game([ + +"""#####O##### +# # # # +# # # # +# # # # +# # +# # +# # +# # +# # +# # +#####I#####""", + +"""#####D##### +# + # +# _ # +# # +# # +# # +# # +# # +# # +# # +#####I#####""", + +"""#####D##### +# + # +# + # +# + # +# + # +# b _ # +# # +# # +# # +# # +#####I#####""", + +"""#####D##### +#+++++ # +#+ # +#+ # +#_ # +#####D##### +# + # +# b + # +# + # +# _ # +#####I#####""", + +"""########### +# # +# # +# # +# # +# # +# # +# # +# # +# # +#####I#####""", + ]) + + self.cellWidth = 64 + + self._tickTime = 700 + + self._previousTick = None + + self.nextControlDirection = None def isRunning(self): return not self.game.isLost() @@ -68,10 +130,16 @@ class GameView: def update(self, time): - if (self._previousTick == None) or (self._previousTick + self._tickTime <= time): - self._previousTick = time - if self.nextControlDirection != None: - self.game.snake.heading = self.nextControlDirection - self.nextControlDirection = None + if self.nextControlDirection != None: + self.game.snake.heading = self.nextControlDirection + self.nextControlDirection = None self.game.tick() + # Time-dependent: + # if (self._previousTick == None) or (self._previousTick + self._tickTime <= time): + # self._previousTick = time + # if self.nextControlDirection != None: + # self.game.snake.heading = self.nextControlDirection + # self.nextControlDirection = None + # self.game.tick() + diff --git a/PressurePlate.py b/PressurePlate.py index 0bcb7c3..fcebe4b 100644 --- a/PressurePlate.py +++ b/PressurePlate.py @@ -2,19 +2,15 @@ class PressurePlate: - pos = None - - _trails = [] - - _isActive = False - - - def __init__(self, pos, trails = []): + def __init__(self, pos): self.pos = pos - self._trails = trails + self._trails = [] + print(f"at pos {self.pos.toString()} trails: {self._trails}") + self._isActive = False def addTrail(self, trail): + print(f"plate at {self.pos.toString()} got trail: {trail}") self._trails.append(trail) @@ -5,12 +5,12 @@ import Game class Snake: - cells = [] - heading = Vec.up - game = None - hasCollided = False def __init__(self, cells: list[Vec2], game: Game): + self.cells = [] + self.heading = Vec.up + self.game = None + self.hasCollided = False self.cells = cells self.game = game @@ -4,13 +4,8 @@ import Trail class Trail: - - _nextTrail = None - pos = None - _isOn = False - - def __init__(self, pos: Vec2, nextTrail: Trail): + self._isOn = False self.pos = pos self._nextTrail = nextTrail @@ -1,9 +1,6 @@ class Vec2: - x = 0 - y = 0 - def __init__(self, x, y): self.x = x self.y = y @@ -17,6 +14,9 @@ class Vec2: def neg(self): return Vec2(-self.x, -self.y) + def toString(self): + return f"({self.x}, {self.y})" + right = Vec2(1, 0) up = Vec2(0, -1) left = Vec2(-1, 0) @@ -7,9 +7,9 @@ class Walls: Supports iterating over the walls and checking if there is a wall at a specific coordinate. Useful as a wrapper to later increase performance of these operations.""" - _walls = [] def __init__(self, walls): + _walls = [] self._walls = walls def fromString(wallString): @@ -18,16 +18,15 @@ while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False - - keys = pygame.key.get_pressed() - if keys[pygame.K_w] and view.game.snake.heading != Vec.up.neg(): - view.nextControlDirection = Vec.up - elif keys[pygame.K_a] and view.game.snake.heading != Vec.left.neg(): - view.nextControlDirection = Vec.left - elif keys[pygame.K_r] and view.game.snake.heading != Vec.down.neg(): - view.nextControlDirection = Vec.down - elif keys[pygame.K_s] and view.game.snake.heading != Vec.right.neg(): - view.nextControlDirection = Vec.right + elif event.type == pygame.KEYDOWN: + if event.key == pygame.K_w and view.game.snake.heading != Vec.up.neg(): + view.nextControlDirection = Vec.up + elif event.key == pygame.K_a and view.game.snake.heading != Vec.left.neg(): + view.nextControlDirection = Vec.left + elif event.key == pygame.K_r and view.game.snake.heading != Vec.down.neg(): + view.nextControlDirection = Vec.down + elif event.key == pygame.K_s and view.game.snake.heading != Vec.right.neg(): + view.nextControlDirection = Vec.right if view.isRunning(): view.update(pygame.time.get_ticks()) |
