pasians/main.py

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()