-
Notifications
You must be signed in to change notification settings - Fork 0
/
solve100.py
150 lines (137 loc) · 4.46 KB
/
solve100.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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
'''
Solves a "Cento" board of arbitrary dimensions (from 5x5 to 10x10) using a brute-force algorithm
(c) 2019 Luca Donaggio <donaggio at gmail dot com>
'''
import argparse
class Coord:
def __init__(self, col, row):
self.x = col
self.y = row
class Move:
def __init__(self, col, row, num):
self.coord = Coord(col, row)
self.num = num
self.nextMoves = {'N': '', 'NE': '', 'E': '', 'SE': '', 'S': '', 'SW': '', 'W': '', 'NW': ''}
class Game:
def __init__(self, dim):
self.dim = dim
self.board = [[None for x in range(self.dim)] for y in range(self.dim)]
self.currMove = None
self.moves = []
self.maxNum = 0
self.step = 0
def _dirToCoord(self, dir):
if dir == 'N':
return Coord(self.currMove.coord.x, self.currMove.coord.y - 3)
elif dir == 'NE':
return Coord(self.currMove.coord.x + 2, self.currMove.coord.y - 2)
elif dir == 'E':
return Coord(self.currMove.coord.x + 3, self.currMove.coord.y)
elif dir == 'SE':
return Coord(self.currMove.coord.x + 2, self.currMove.coord.y + 2)
elif dir == 'S':
return Coord(self.currMove.coord.x, self.currMove.coord.y + 3)
elif dir == 'SW':
return Coord(self.currMove.coord.x - 2, self.currMove.coord.y + 2)
elif dir == 'W':
return Coord(self.currMove.coord.x - 3, self.currMove.coord.y)
elif dir == 'NW':
return Coord(self.currMove.coord.x - 2, self.currMove.coord.y - 2)
def _evalNextMove(self, dir):
nextCoord = self._dirToCoord(dir)
if nextCoord.x in range(self.dim) and nextCoord.y in range(self.dim):
if self.board[nextCoord.y][nextCoord.x] is None:
return 'V'
else:
return 'X'
else:
return 'I'
def _nextMove(self):
# Evaluate all possible moves
for d in self.currMove.nextMoves:
if not self.currMove.nextMoves[d]:
self.currMove.nextMoves[d] = self._evalNextMove(d)
# Choose next move
nextDir = ''
for di in self.currMove.nextMoves.items():
if di[1] == 'V':
nextDir = di[0]
break
if nextDir:
self.currMove.nextMoves[nextDir] = 'P'
nextCoord = self._dirToCoord(nextDir)
return Move(nextCoord.x, nextCoord.y, self.currMove.num + 1)
else:
return None
def _printProgress(self):
print ('\rStep ' + str(self.step) + ': [{0:<' + str(self.dim ** 2) + '}] Max. ' + str(self.maxNum)).format('#' * self.currMove.num),
def solve(self):
# Start at top-left corner
move = Move(0, 0, 1)
while move is not None:
# Do move
self.moves.append(move)
self.currMove = move
self.board[move.coord.y][move.coord.x] = move.num
if self.maxNum < move.num:
self.maxNum = move.num
# Print progress indicator
self._printProgress()
# Have we finished?
if move.num == self.dim ** 2:
return
# No, choose next move
nextMove = self._nextMove()
# Are there no more moves?
while nextMove is None:
# Undo current move
self.board[self.currMove.coord.y][self.currMove.coord.x] = None
self.moves.pop()
# Is there a previous move?
if len(self.moves) > 0:
# Yes, make that the current move
self.currMove = self.moves[-1]
# Print progress indicator
self._printProgress()
# Mark the former move as 'failed' so to not go there again
for d in self.currMove.nextMoves:
if self.currMove.nextMoves[d] == 'P':
self.currMove.nextMoves[d] = 'F'
break
# Choose next m ve
nextMove = self._nextMove()
else:
# No, restart from next position on the board
self.currMove = None
self.step += 1
if self.step < self.dim ** 2:
nextMove = Move(self.step // self.dim, self.step % self.dim, 1)
else:
print
return
move = nextMove
return
def printBoard(self):
maxNumWidth = len(str(self.dim ** 2))
cellWidth = maxNumWidth + 2
rowDiv = '+' + ('-' * ((cellWidth + 1 ) * self.dim - 1)) + '+'
for row in range(self.dim):
print rowDiv
print '|',
for col in range(self.dim):
if self.board[row][col] is not None:
print ('{n:<' + str(maxNumWidth) + '} |').format(n = self.board[row][col]),
else:
print '-' * maxNumWidth + ' |',
print
print rowDiv
if __name__ == "__main__":
parser = argparse.ArgumentParser(description = 'Solves a "Cento" board of given dimensions.')
parser.add_argument("-d", "--dim", type = int, choices = [5, 6, 7, 8, 9, 10], default = 5, help = 'board dimension')
args = parser.parse_args()
print '\n"Cento" solver (c) 2019 Luca Donaggio <donaggio at gmail dot com>\n\nSolving a {dim}x{dim} board:\n'.format(dim = args.dim)
game = Game(args.dim)
game.solve()
print '\n'
game.printBoard()
print