[2] add interactive player controls and undo feature
This commit is contained in:
parent
3dc7e19f49
commit
fab92c6734
|
|
@ -192,6 +192,86 @@ class LabyrinthSolver:
|
|||
ms = (t1 - t0) * 1000
|
||||
return ms, self.algorithm.get_visited(), len(path)
|
||||
|
||||
class Player:
|
||||
def __init__(self, start, lab):
|
||||
self.current = start
|
||||
self.last = None
|
||||
self.lab = lab
|
||||
|
||||
def move(self, cell):
|
||||
if cell and cell.can_step():
|
||||
self.last = self.current
|
||||
self.current = cell
|
||||
return True
|
||||
return False
|
||||
|
||||
def undo(self):
|
||||
if self.last:
|
||||
self.current, self.last = self.last, None
|
||||
return True
|
||||
return False
|
||||
|
||||
class Command:
|
||||
def do(self):
|
||||
raise NotImplementedError
|
||||
def revert(self):
|
||||
raise NotImplementedError
|
||||
|
||||
class MoveCommand(Command):
|
||||
def __init__(self, player, dx, dy, lab):
|
||||
self.player = player
|
||||
self.dx = dx
|
||||
self.dy = dy
|
||||
self.lab = lab
|
||||
self.done = False
|
||||
|
||||
def do(self):
|
||||
nx = self.player.current.x + self.dx
|
||||
ny = self.player.current.y + self.dy
|
||||
target = self.lab.get_point(nx, ny)
|
||||
if target and target.can_step():
|
||||
self.player.move(target)
|
||||
self.done = True
|
||||
return True
|
||||
return False
|
||||
|
||||
def revert(self):
|
||||
if self.done:
|
||||
self.player.undo()
|
||||
self.done = False
|
||||
return True
|
||||
return False
|
||||
|
||||
class InteractiveView:
|
||||
def __init__(self, lab, player):
|
||||
self.lab = lab
|
||||
self.player = player
|
||||
|
||||
def render(self):
|
||||
os.system('cls' if os.name == 'nt' else 'clear')
|
||||
print("=" * (self.lab.w * 2 + 4))
|
||||
print(" LABYRINTH (P = player)")
|
||||
print("=" * (self.lab.w * 2 + 4))
|
||||
for y in range(self.lab.h):
|
||||
print(" ", end='')
|
||||
for x in range(self.lab.w):
|
||||
p = self.lab.get_point(x, y)
|
||||
if self.player.current == p:
|
||||
print('P', end=' ')
|
||||
elif p == self.lab.start_point:
|
||||
print('S', end=' ')
|
||||
elif p == self.lab.exit_point:
|
||||
print('E', end=' ')
|
||||
elif p.blocked:
|
||||
print('#', end=' ')
|
||||
else:
|
||||
print('.', end=' ')
|
||||
print()
|
||||
print("=" * (self.lab.w * 2 + 4))
|
||||
print(f" Position: ({self.player.current.x},{self.player.current.y})")
|
||||
print(" Controls: h(left) j(down) k(up) l(right) u=undo q=quit")
|
||||
print(" Auto-search: b=BFS d=DFS a=A*")
|
||||
|
||||
def run_experiment(maze_file, algo, runs=5):
|
||||
loader = TextMazeLoader()
|
||||
lab = loader.load(maze_file)
|
||||
|
|
@ -209,35 +289,53 @@ def run_experiment(maze_file, algo, runs=5):
|
|||
total_len += plen
|
||||
return total_ms / runs, total_visited / runs, total_len / runs
|
||||
|
||||
class TextView:
|
||||
def display(self, lab):
|
||||
os.system('cls' if os.name == 'nt' else 'clear')
|
||||
print("=" * (lab.w * 2 + 4))
|
||||
print(" LABYRINTH")
|
||||
print("=" * (lab.w * 2 + 4))
|
||||
for y in range(lab.h):
|
||||
print(" ", end='')
|
||||
for x in range(lab.w):
|
||||
p = lab.get_point(x, y)
|
||||
if p == lab.start_point:
|
||||
print('S', end=' ')
|
||||
elif p == lab.exit_point:
|
||||
print('E', end=' ')
|
||||
elif p.blocked:
|
||||
print('#', end=' ')
|
||||
else:
|
||||
print('.', end=' ')
|
||||
print()
|
||||
print("=" * (lab.w * 2 + 4))
|
||||
print(" S - start E - exit # - wall . - path")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# quick demo
|
||||
loader = TextMazeLoader()
|
||||
lab = loader.load("maze/maze1.txt")
|
||||
view = TextView()
|
||||
view.display(lab)
|
||||
print("\nRunning experiment on maze1.txt with BFS...")
|
||||
bfs = BreadthFirst()
|
||||
avg_t, avg_v, avg_l = run_experiment("maze/maze1.txt", bfs, runs=3)
|
||||
print(f"BFS: time={avg_t:.3f}ms visited={avg_v:.0f} length={avg_l:.0f}")
|
||||
player = Player(lab.start_point, lab)
|
||||
view = InteractiveView(lab, player)
|
||||
view.render()
|
||||
|
||||
solver = LabyrinthSolver(lab)
|
||||
history = []
|
||||
|
||||
while True:
|
||||
key = input("\n > ").lower()
|
||||
if key == 'q':
|
||||
print("Goodbye!")
|
||||
break
|
||||
elif key == 'b':
|
||||
solver.set_algorithm(BreadthFirst())
|
||||
ms, vis, plen = solver.solve()
|
||||
print(f"BFS: {ms:.3f}ms, visited={vis}, length={plen}")
|
||||
elif key == 'd':
|
||||
solver.set_algorithm(DepthFirst())
|
||||
ms, vis, plen = solver.solve()
|
||||
print(f"DFS: {ms:.3f}ms, visited={vis}, length={plen}")
|
||||
elif key == 'a':
|
||||
solver.set_algorithm(AStar())
|
||||
ms, vis, plen = solver.solve()
|
||||
print(f"A*: {ms:.3f}ms, visited={vis}, length={plen}")
|
||||
elif key in ('h','j','k','l'):
|
||||
moves = {'h': (-1,0), 'l': (1,0), 'k': (0,-1), 'j': (0,1)}
|
||||
dx, dy = moves[key]
|
||||
cmd = MoveCommand(player, dx, dy, lab)
|
||||
if cmd.do():
|
||||
history.append(cmd)
|
||||
view.render()
|
||||
if player.current == lab.exit_point:
|
||||
print("\n*** YOU REACHED THE EXIT! ***")
|
||||
print(f"Total moves: {len(history)}")
|
||||
break
|
||||
else:
|
||||
print("Can't go there - wall!")
|
||||
elif key == 'u':
|
||||
if history:
|
||||
cmd = history.pop()
|
||||
cmd.revert()
|
||||
view.render()
|
||||
print("Undo last move")
|
||||
else:
|
||||
print("Nothing to undo")
|
||||
else:
|
||||
print("Unknown command")
|
||||
Loading…
Reference in New Issue
Block a user