summaryrefslogtreecommitdiff
path: root/Game.py
diff options
context:
space:
mode:
authorJoel Kronqvist <joel.kronqvist@iki.fi>2025-11-03 23:10:04 +0200
committerJoel Kronqvist <joel.kronqvist@iki.fi>2025-11-03 23:10:04 +0200
commitd1c404fe8eac3c743004a9a48a683e9361c8f7b3 (patch)
treef5df16492fd5cfc3a2915c678306b53c212edb5e /Game.py
parentef6abc27cec35e32acef66c5077ffcc6bedde983 (diff)
downloadSnakePuzzle-d1c404fe8eac3c743004a9a48a683e9361c8f7b3.tar.gz
SnakePuzzle-d1c404fe8eac3c743004a9a48a683e9361c8f7b3.zip
fix: added typing
Diffstat (limited to 'Game.py')
-rw-r--r--Game.py124
1 files changed, 76 insertions, 48 deletions
diff --git a/Game.py b/Game.py
index af9b233..7e5cb1d 100644
--- a/Game.py
+++ b/Game.py
@@ -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