-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathpuzzleplayer.py
110 lines (96 loc) · 4.2 KB
/
puzzleplayer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
"""
This class provides a TUI for interaction with Solvers and Puzzles
"""
from puzzlesolver.util import PuzzleValue
class PuzzlePlayer:
def __init__(self, puzzle, solver=None, info=False, auto=False):
self.base = puzzle
self.puzzle = puzzle
self.solver = solver
self.info = info
if not solver and (auto or info):
raise Exception("Cannot have auto or info arguments without a solver")
self.auto = auto
if solver:
self.solver.solve(verbose=True)
# Starts the PuzzlePlayer
def play(self):
self.puzzle = self.base
self.turn = 0
while True:
self.printInfo()
print("Puzzle: ")
print(self.puzzle.toString(mode="complex"))
if self.puzzle.primitive() != PuzzleValue.UNDECIDED: break
self.printTurn()
print("Game Over")
def printInfo(self):
print("Turn: ", self.turn),
print("Primitive: ", self.puzzle.primitive())
if self.info and self.solver:
print("Solver: ", self.solver.getValue(self.puzzle))
print("Remoteness: ", self.solver.getRemoteness(self.puzzle))
print("Best Move: ", self.generateBestMove())
self.turn += 1
# Prompts for input and moves
def printTurn(self):
if self.solver: move = self.generateBestMove()
# Auto generate a possible solution
if self.auto:
self.puzzle = self.puzzle.doMove(move)
else:
moves = list(self.puzzle.generateMoves(movetype="legal"))
# Have the best move be the first index
if self.solver and self.info:
moves.remove(move)
moves.insert(0, move)
print("Possible Moves:")
for count, m in enumerate(moves):
print(str(count) + " -> " + str(m))
print("Enter Piece: ")
index = int(input())
if index >= len(moves):
print("Not a valid move, try again")
else:
self.puzzle = self.puzzle.doMove(moves[index])
print("----------------------------")
# Generates best move from the solver
def generateBestMove(self):
if self.solver.getValue(self.puzzle) == PuzzleValue.UNSOLVABLE: return None
if self.puzzle.primitive() == PuzzleValue.SOLVABLE: return None
remotes = {
self.solver.getRemoteness(self.puzzle.doMove(move)) : move
for move in self.puzzle.generateMoves(movetype="legal")
}
if PuzzleValue.UNSOLVABLE in remotes:
del remotes[PuzzleValue.UNSOLVABLE]
return remotes[min(remotes.keys())]
if __name__ == "__main__":
import argparse
from puzzlesolver.puzzles import PuzzleManager
parser = argparse.ArgumentParser()
parser.add_argument("puzzleid", help="PuzzleID of the puzzle you wish to view")
parser.add_argument("-v", "--variant", help="Variant of puzzle")
parser.add_argument("-p", "--position", help="Specific position of puzzle (overrides variant)")
parser.add_argument("-i", "--info", action="store_true", help="Solver reveals some helpful info")
parser.add_argument("-a", "--auto", action="store_true", help="Puzzle plays itself")
parser.add_argument("-l", "--list", action="store_true", help="Lists puzzles and their ids")
args = parser.parse_args()
if not PuzzleManager.hasPuzzleId(args.puzzleid):
print("Possible puzzles:")
print("\n".join(PuzzleManager.getPuzzleIds()))
raise Exception("Puzzleid is not recorded in PuzzleList")
p_cls = PuzzleManager.getPuzzleClass(args.puzzleid)
puzzle = None
if args.variant:
puzzle = p_cls.generateStartPosition(args.variant)
if args.position:
puzzle = p_cls.deserialize(args.position)
if not puzzle:
puzzle = p_cls()
if args.info or args.auto:
s_cls = PuzzleManager.getSolverClass(args.puzzleid, args.variant)
solver = s_cls(puzzle)
else:
solver = None
PuzzlePlayer(puzzle, solver=solver, info=args.info, auto=args.auto).play()