spaghetti-code-3/maze.py

137 lines
3.5 KiB
Python
Raw Normal View History

2024-11-26 13:06:34 +01:00
import fileinput
import random
from enum import StrEnum
'''
This file gives a starting point for the 'spaghetti codenight'.
A example that uses the Maze class can be found at the bottom.
'''
class Block(StrEnum):
"""
a Block represents a field in the maze
"""
Wall = "" # Can't be entered as a player
Field = "" # Can be entered as a player
FieldKnown = "🟥" # Same as field except that you have already been there
Player = "🍝" # You
End = "😮" # The end of the maze
class Direction(StrEnum):
"""
Possible directions to move to.
"""
2024-11-26 13:07:31 +01:00
Up = "⬆️"
Right = "➡️"
Down = "⬇️"
Left = "⬅️"
2024-11-26 13:06:34 +01:00
@property
def coordinates(self) -> tuple[int, int]:
"""
The coordinates to add to the current player's position for each possible direction
"""
coordinates_map = {
2024-11-26 17:44:31 +01:00
"⬆️": (-1, 0),
"➡️": (0, 1),
"⬇️": (1, 0),
"⬅️": (0, -1),
2024-11-26 13:06:34 +01:00
}
return coordinates_map[self.value]
class Maze:
def __init__(self):
self.field: list[list[Block]] # 2D array representing the maze
self.player: tuple[int, int] # Position of the player.
self.end: tuple[int, int] # Position of the end.
2024-11-26 17:44:31 +01:00
self._input = fileinput.input(encoding='utf-8') # Reads the input. You shouldn't access this manually
2024-11-26 13:06:34 +01:00
self._get_state()
def move(self, dir: Direction) -> bool:
"""
Move in a given direction
Args:
mov (Direction): The direction to move in
Returns:
2024-11-26 20:18:55 +01:00
bool: Returns if the player didn't move
2024-11-26 13:06:34 +01:00
"""
old_player = self.player
2024-11-26 17:44:31 +01:00
print(dir.value, flush=True) # Do move (Flush is necessary!)
2024-11-26 13:06:34 +01:00
self._get_state() # Get new maze state
return old_player == self.player
def get_block(self, dir: Direction) -> Block:
"""
Get the block in the given direction from the player
Args:
mov (Direction): The direction
Returns:
Block: The block
"""
dy, dx = dir.coordinates
2024-11-26 17:44:31 +01:00
py, px = self.player[0] + dy, self.player[1] + dx
2024-11-26 13:06:34 +01:00
return self.field[py][px]
def _get_state(self):
"""
Private method. Should not be called manually.
Gets the next state from stdin and parses it.
"""
self.field = []
2024-11-26 17:44:31 +01:00
# Split line by tab and loop over them
for i, line in enumerate(self._input.readline().split('\t')):
2024-11-26 13:06:34 +01:00
row: list[Block] = []
2024-11-26 17:44:31 +01:00
# Loop over emojis
2024-11-26 13:06:34 +01:00
for j, char in enumerate(line):
2024-11-26 17:44:31 +01:00
if char in ['\t', '\n']: # Next line
continue
2024-11-26 13:06:34 +01:00
row.append(Block(char))
2024-11-26 17:44:31 +01:00
# Check for player
2024-11-26 13:06:34 +01:00
if char == Block.Player.value:
self.player = (i, j)
2024-11-26 17:44:31 +01:00
# Check for the end
2024-11-26 13:06:34 +01:00
if char == Block.End.value:
2024-11-26 17:44:31 +01:00
self.end = (i, j)
2024-11-26 13:06:34 +01:00
self.field.append(row)
def random_solver():
"""
An example of a maze solver using the Maze class.
It doesn't guarantee a solution as it will move in a random direction.
"""
# Get maze
maze = Maze()
# As long as the maze isn't solved, move in a random direction
# The program gets killed when a maze is solved so we don't have to worry about that
while True:
# Get a random direction
dir = random.choice([dir for dir in Direction])
# Move
maze.move(dir)
if __name__ == '__main__':
random_solver()