350 lines
9.4 KiB
Python
350 lines
9.4 KiB
Python
from collections import defaultdict
|
|
from copy import deepcopy
|
|
|
|
E = "_" # no pawn
|
|
O = "O" # pawn
|
|
_ = " " # empty space
|
|
COLS = "ABCDEFG"
|
|
ROWS = "1234567"
|
|
|
|
STARTING_BOARD = [
|
|
[_, _, O, O, O, _, _],
|
|
[_, _, O, O, O, _, _],
|
|
[O, O, O, O, O, O, O],
|
|
[O, O, O, E, O, O, O],
|
|
[O, O, O, O, O, O, O],
|
|
[_, _, O, O, O, _, _],
|
|
[_, _, O, O, O, _, _],
|
|
]
|
|
|
|
|
|
def translate_rc_pos(r, c):
|
|
return COLS[c] + ROWS[r]
|
|
|
|
|
|
def translate_pos_rc(pos):
|
|
return ROWS.find(pos[1]), COLS.find(pos[0])
|
|
|
|
|
|
def gen_around(r, c, board):
|
|
moves = []
|
|
pawn = board[r][c]
|
|
if pawn in [_, E]:
|
|
return []
|
|
# pawn exists
|
|
cur_pawn = translate_rc_pos(r, c)
|
|
|
|
# bottom pawn
|
|
if c + 2 < len(board[r]):
|
|
r_pawn = board[r][c + 1]
|
|
rr_pawn = board[r][c + 2]
|
|
if r_pawn == O and rr_pawn == E:
|
|
next_pawn = translate_rc_pos(r, c + 2)
|
|
moves.append(cur_pawn + next_pawn)
|
|
|
|
# top pawn
|
|
if c - 2 >= 0:
|
|
r_pawn = board[r][c - 1]
|
|
rr_pawn = board[r][c - 2]
|
|
if r_pawn == O and rr_pawn == E:
|
|
next_pawn = translate_rc_pos(r, c - 2)
|
|
moves.append(cur_pawn + next_pawn)
|
|
|
|
# right pawn
|
|
if r + 2 < len(board):
|
|
r_pawn = board[r + 1][c]
|
|
rr_pawn = board[r + 2][c]
|
|
if r_pawn == O and rr_pawn == E:
|
|
next_pawn = translate_rc_pos(r + 2, c)
|
|
moves.append(cur_pawn + next_pawn)
|
|
|
|
# left pawn
|
|
if r - 2 >= 0:
|
|
r_pawn = board[r - 1][c]
|
|
rr_pawn = board[r - 2][c]
|
|
if r_pawn == O and rr_pawn == E:
|
|
next_pawn = translate_rc_pos(r - 2, c)
|
|
moves.append(cur_pawn + next_pawn)
|
|
|
|
return moves
|
|
|
|
|
|
def gen_moves(board):
|
|
moves = []
|
|
for r, row in enumerate(board):
|
|
for c, pawn in enumerate(row):
|
|
if pawn == O:
|
|
pos_moves = gen_around(r, c, board)
|
|
moves += pos_moves
|
|
|
|
return moves
|
|
|
|
|
|
def play_move(move, board):
|
|
move = move.upper()
|
|
start_pos, end_pos = move[0:2], move[2:4]
|
|
# start row, start column; end row, end column
|
|
sr, sc = translate_pos_rc(start_pos)
|
|
er, ec = translate_pos_rc(end_pos)
|
|
assert board[sr][sc] == O, "starting position is not pawn"
|
|
assert board[er][ec] == E, "ending position is not empty"
|
|
|
|
if ec == sc:
|
|
if er - sr == 2:
|
|
assert board[er - 1][ec] == O, "No pawn in between (ec==sc, er-sr)"
|
|
board[er - 1][ec] = E
|
|
elif sr - er == 2:
|
|
assert board[er + 1][ec] == O, "No pawn in between (ec==sc, sr-er)"
|
|
board[er + 1][ec] = E
|
|
else:
|
|
raise Exception("There must be a pawn in between.")
|
|
elif er == sr:
|
|
if ec - sc == 2:
|
|
assert board[er][ec - 1] == O, "No pawn in between (er==sr, ec-sc)"
|
|
board[er][ec - 1] = E
|
|
elif sc - ec == 2:
|
|
assert board[er][ec + 1] == O, "No pawn in between (er==sr, sc-ec)"
|
|
board[er][ec + 1] = E
|
|
else:
|
|
raise Exception("There must be a pawn in between.")
|
|
else:
|
|
raise Exception("You can only jump 1.")
|
|
|
|
board[sr][sc] = E
|
|
board[er][ec] = O
|
|
return board
|
|
|
|
|
|
def undo_move(move, board):
|
|
move = move.upper()
|
|
start_pos, end_pos = move[2:4], move[0:2]
|
|
sr, sc = translate_pos_rc(start_pos)
|
|
er, ec = translate_pos_rc(end_pos)
|
|
assert board[sr][sc] == O, "starting position is not pawn"
|
|
assert board[er][ec] == E, "ending position is not empty"
|
|
|
|
if ec == sc:
|
|
if er - sr == 2:
|
|
assert board[er - 1][ec] == E, "pawn in between (ec==sc, er-sr)"
|
|
board[er - 1][ec] = O
|
|
elif sr - er == 2:
|
|
assert board[er + 1][ec] == E, "pawn in between (ec==sc, sr-er)"
|
|
board[er + 1][ec] = O
|
|
else:
|
|
raise Exception("There must NOT be a pawn in between.")
|
|
elif er == sr:
|
|
if ec - sc == 2:
|
|
assert board[er][ec - 1] == E, "pawn in between (er==sr, ec-sc)"
|
|
board[er][ec - 1] = O
|
|
elif sc - ec == 2:
|
|
assert board[er][ec + 1] == E, "pawn in between (er==sr, sc-ec)"
|
|
board[er][ec + 1] = O
|
|
else:
|
|
raise Exception("There must NOT be a pawn in between.")
|
|
else:
|
|
raise Exception("You can only jump 1.")
|
|
|
|
board[sr][sc] = E
|
|
board[er][ec] = O
|
|
return board
|
|
|
|
|
|
def print_board(board):
|
|
print(" ", end="")
|
|
for r in COLS:
|
|
print(" ", r, " ", end="")
|
|
print()
|
|
for c, row in zip(ROWS, board):
|
|
print(c, row, end="")
|
|
print()
|
|
|
|
|
|
def count_pawns(board):
|
|
count = 0
|
|
for row in board:
|
|
for pawn in row:
|
|
if pawn == O:
|
|
count += 1
|
|
return count
|
|
|
|
|
|
def play():
|
|
moves_this_game = []
|
|
stat = dict(
|
|
moves=defaultdict(int),
|
|
num_moves=0,
|
|
)
|
|
board = deepcopy(STARTING_BOARD)
|
|
i = 0
|
|
while True:
|
|
print("MOVE: {}".format(i))
|
|
print_board(board)
|
|
moves = gen_moves(board)
|
|
stat["moves"][len(moves)] += 1
|
|
print("MOVES: {}".format(moves))
|
|
if len(moves) == 0:
|
|
print("No valid moves left")
|
|
break
|
|
# this_move = random.choice(moves)\
|
|
# print("Chose Move: {}".format(this_move))
|
|
this_move = input("> ")
|
|
moves_this_game.append(this_move)
|
|
try:
|
|
board = play_move(this_move, board)
|
|
except Exception as e:
|
|
print("invalid move")
|
|
print(e)
|
|
continue
|
|
i += 1
|
|
stat["num_moves"] = i
|
|
return board, moves_this_game, stat
|
|
|
|
|
|
def main_play():
|
|
game_no = 1
|
|
gauss = defaultdict(int)
|
|
while True:
|
|
board, moves_this_game, stat = play()
|
|
count = count_pawns(board)
|
|
# print("{} Left pawns: {} | {} | {}".format(game_no, count, stat["num_moves"], sorted([(a,b) for a, b in stat['moves'].items()], key=lambda x: x[1], reverse=True)))
|
|
print("{} Left pawns: {}".format(game_no, count))
|
|
gauss[count] += 1
|
|
game_no += 1
|
|
if count == 1:
|
|
print("Moves this game: {}".format(moves_this_game))
|
|
print("FINISHED!")
|
|
break
|
|
|
|
|
|
HASHES = {}
|
|
|
|
|
|
def rot_board(board):
|
|
A = deepcopy(board)
|
|
N = len(A[0])
|
|
for i in range(N // 2):
|
|
for j in range(i, N - i - 1):
|
|
temp = A[i][j]
|
|
A[i][j] = A[N - 1 - j][i]
|
|
A[N - 1 - j][i] = A[N - 1 - i][N - 1 - j]
|
|
A[N - 1 - i][N - 1 - j] = A[j][N - 1 - i]
|
|
A[j][N - 1 - i] = temp
|
|
return A
|
|
|
|
|
|
def hash_board(board):
|
|
the_hash, cnt = "", 0
|
|
prev_cell = None
|
|
for row in board:
|
|
for cell in row:
|
|
if cell == _:
|
|
continue
|
|
if prev_cell is not None and prev_cell != cell:
|
|
the_hash += "{}{}|".format(cnt, prev_cell)
|
|
cnt = 0
|
|
prev_cell = cell
|
|
cnt += 1
|
|
if cnt != 0:
|
|
the_hash += "{}{}".format(cnt, prev_cell)
|
|
return the_hash
|
|
|
|
|
|
def rot_hash_board(board):
|
|
value = len(HASHES)
|
|
for i in range(4):
|
|
hash0 = hash_board(board)
|
|
HASHES[hash0] = value
|
|
board = rot_board(board)
|
|
|
|
|
|
class Node(object):
|
|
def __init__(self, board, move, parent_node):
|
|
self.parent = parent_node
|
|
self.board = board
|
|
self.move = move
|
|
|
|
if self.parent:
|
|
self.prev_moves = self.parent.prev_moves + [move]
|
|
else:
|
|
if move:
|
|
self.prev_moves = [move]
|
|
else:
|
|
self.prev_moves = []
|
|
self.visited = False
|
|
self.complete = False
|
|
|
|
self.moves = gen_moves(self.board)
|
|
self.children = {move: None for move in self.moves}
|
|
|
|
@property
|
|
def non_explored_moves(self):
|
|
rv = []
|
|
for move in self.children.keys():
|
|
node = self.get_node_from_move(move)
|
|
if not node.visited:
|
|
rv.append(move)
|
|
return rv
|
|
|
|
def get_node_from_move(self, move):
|
|
if self.children.get(move) is None:
|
|
self.children[move] = Node(play_move(move, deepcopy(self.board)), move, self)
|
|
return self.children[move]
|
|
|
|
|
|
|
|
|
|
def iterative_main():
|
|
board = deepcopy(STARTING_BOARD)
|
|
moves_this_game = []
|
|
while True:
|
|
moves = gen_moves(board)
|
|
if len(moves) <= 0:
|
|
print("finished")
|
|
pawns = count_pawns(board)
|
|
if pawns == 1:
|
|
print(moves_this_game)
|
|
break
|
|
print_board(board)
|
|
print(moves_this_game)
|
|
rot_hash_board(board)
|
|
|
|
this_move = moves_this_game[-1]
|
|
board = undo_move(this_move, board)
|
|
else:
|
|
this_move = moves[0]
|
|
moves_this_game.append(this_move)
|
|
board = play_move(this_move, board)
|
|
|
|
|
|
def main():
|
|
root = Node(board=STARTING_BOARD, move=None, parent_node=None)
|
|
node = root
|
|
while True:
|
|
if len(node.non_explored_moves) == 0:
|
|
pawns_left = count_pawns(node.board)
|
|
if pawns_left == 1:
|
|
print("WIN")
|
|
break
|
|
else:
|
|
# backtrack
|
|
node.visited = True
|
|
node = node.parent
|
|
else:
|
|
# there is at least one move
|
|
move = node.non_explored_moves[0]
|
|
node = node.get_node_from_move(move)
|
|
|
|
|
|
def play_moves():
|
|
board = STARTING_BOARD
|
|
moves = input("> ")
|
|
for move in moves.split(','):
|
|
move = move.strip()[1:-1]
|
|
print(move)
|
|
board = play_move(move, board)
|
|
print_board(board)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# main()
|
|
play_moves() |