summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Kronqvist <joel.kronqvist@iki.fi>2025-11-02 22:36:35 +0200
committerJoel Kronqvist <joel.kronqvist@iki.fi>2025-11-02 22:36:35 +0200
commit1475dd4020ec24df8b29f5d90d89843b64f93f95 (patch)
tree7965e1d9cdaceb0a02a945b8f39b1437be3ed7e7
parent314be3895ece7dbeb47bcdd85a05acbc4bc0ff9c (diff)
downloadSnakePuzzle-1475dd4020ec24df8b29f5d90d89843b64f93f95.tar.gz
SnakePuzzle-1475dd4020ec24df8b29f5d90d89843b64f93f95.zip
feat: hacky level changing and parsing system, also fixed unintentional class-wide variables
-rw-r--r--Box.py1
-rw-r--r--Door.py9
-rw-r--r--Game.py164
-rw-r--r--GameView.py96
-rw-r--r--PressurePlate.py14
-rw-r--r--Snake.py8
-rw-r--r--Trail.py7
-rw-r--r--Vec.py6
-rw-r--r--Walls.py2
-rwxr-xr-xmain.py19
10 files changed, 211 insertions, 115 deletions
diff --git a/Box.py b/Box.py
index 0b8f432..9641a44 100644
--- a/Box.py
+++ b/Box.py
@@ -2,7 +2,6 @@
from Vec import Vec2
class Box:
- pos = None
def __init__(self, pos: Vec2):
self.pos = pos
diff --git a/Door.py b/Door.py
index 31030d8..111bb5a 100644
--- a/Door.py
+++ b/Door.py
@@ -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
diff --git a/Game.py b/Game.py
index f61fbdd..59d410a 100644
--- a/Game.py
+++ b/Game.py
@@ -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)
diff --git a/Snake.py b/Snake.py
index a1e3dc4..7127326 100644
--- a/Snake.py
+++ b/Snake.py
@@ -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
diff --git a/Trail.py b/Trail.py
index f393645..08450d3 100644
--- a/Trail.py
+++ b/Trail.py
@@ -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
diff --git a/Vec.py b/Vec.py
index 9f22dd0..31a67da 100644
--- a/Vec.py
+++ b/Vec.py
@@ -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)
diff --git a/Walls.py b/Walls.py
index 860a7ef..d591291 100644
--- a/Walls.py
+++ b/Walls.py
@@ -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):
diff --git a/main.py b/main.py
index 52063f7..a0659f3 100755
--- a/main.py
+++ b/main.py
@@ -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())