forked from spaghetti/spaghetti-code-3
136 lines
3.5 KiB
Python
136 lines
3.5 KiB
Python
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.
|
|
"""
|
|
Up = "⬆️"
|
|
Right = "➡️"
|
|
Down = "⬇️"
|
|
Left = "⬅️"
|
|
|
|
@property
|
|
def coordinates(self) -> tuple[int, int]:
|
|
"""
|
|
The coordinates to add to the current player's position for each possible direction
|
|
"""
|
|
coordinates_map = {
|
|
"⬆️": (-1, 0),
|
|
"➡️": (0, 1),
|
|
"⬇️": (1, 0),
|
|
"⬅️": (0, -1),
|
|
}
|
|
|
|
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.
|
|
|
|
self._input = fileinput.input(encoding='utf-8') # Reads the input. You shouldn't access this manually
|
|
|
|
self._get_state()
|
|
|
|
def move(self, dir: Direction) -> bool:
|
|
"""
|
|
Move in a given direction
|
|
|
|
Args:
|
|
mov (Direction): The direction to move in
|
|
|
|
Returns:
|
|
bool: Returns if the player didn't move
|
|
"""
|
|
old_player = self.player
|
|
|
|
print(dir.value, flush=True) # Do move (Flush is necessary!)
|
|
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
|
|
py, px = self.player[0] + dy, self.player[1] + dx
|
|
|
|
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 = []
|
|
|
|
# Split line by tab and loop over them
|
|
for i, line in enumerate(self._input.readline().split('\t')):
|
|
row: list[Block] = []
|
|
# Loop over emojis
|
|
for j, char in enumerate(line):
|
|
if char in ['\t', '\n']: # Next line
|
|
continue
|
|
|
|
row.append(Block(char))
|
|
|
|
# Check for player
|
|
if char == Block.Player.value:
|
|
self.player = (i, j)
|
|
|
|
# Check for the end
|
|
if char == Block.End.value:
|
|
self.end = (i, j)
|
|
|
|
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()
|