Skip to content

Commit

Permalink
- Fixed take back (needed to update playerJustMoved and to keep track…
Browse files Browse the repository at this point in the history
… of nextBoard instead of inferring it from move played)
  • Loading branch information
AngelVI13 committed May 4, 2019
1 parent 13eb51d commit 0f0fde3
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 19 deletions.
2 changes: 1 addition & 1 deletion board/base_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def __str__(self):
def __copy__(self):
raise NotImplementedError

def make_move(self, move):
def make_move(self, *args, **kwargs):
raise NotImplementedError

def take_move(self):
Expand Down
14 changes: 8 additions & 6 deletions board/ultimate_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from board.base_board import *


Move = namedtuple('Move', ['board_idx', 'move_idx'])
Move = namedtuple('Move', ['board_idx', 'move_idx', 'forced_board'])


class UltimateBoard(BaseBoard):
Expand Down Expand Up @@ -80,13 +80,13 @@ def _is_board_valid(self, board: int):
# if board is not None but nextBoard is None (i.e. start of game)
return True, board

def make_move(self, move: int, board: int = None):
def make_move(self, board: int, move: int):
"""Make a move to the specified board, if no board is specified
the move is done on the board indicated by nextBoard i.e forced
by the last played move.
@param move: move to play (i.e. index on the board)
@param board: index indicating which board to play the move on
@param move: move to play (i.e. index on the board)
"""
self.playerJustMoved = -self.playerJustMoved

Expand All @@ -98,14 +98,16 @@ def _make_move(self, move: int, board: int):
"""Actually perform move when the validity of the move has already been determined."""

self.pos[board].make_move(move, self.playerJustMoved)
self.history.append(Move(board_idx=board, move_idx=move))
self.history.append(Move(board_idx=board, move_idx=move, forced_board=self.nextBoard))
# the move on the board represents the next board
self.nextBoard = move if self.pos[move].get_result() is None else ANY_BOARD

def take_move(self):
self.playerJustMoved = -self.playerJustMoved

move = self.history.pop()
self.pos[move.board_idx].take_move(move.move_idx)
self.nextBoard = move.board_idx # update nextBoard to be the one forced
self.nextBoard = move.forced_board # update nextBoard to be the one forced

def get_all_moves(self) -> List[Tuple[int, int]]:
"""Get all possible moves in the form of a list of tuples
Expand Down Expand Up @@ -177,7 +179,7 @@ def get_result(self, player_jm):
# user_input.strip()
# user_input = user_input.split(' ')
# target_board, move_idx = int(user_input[0]), int(user_input[1])
ub.make_move(move_idx, target_board)
ub.make_move(target_board, move_idx)
print(ub)
print('\n\n')

Expand Down
13 changes: 7 additions & 6 deletions engine/uct.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def uct_multi(rootstate_: Board, itermax, verbose):
processes = []
for move in moves:
current_state = rootstate_.__copy__()
current_state.make_move(move)
current_state.make_move(*move)
p = Process(target=uct, args=(queue, move, current_state, avg_iters, verbose))
p.start()
processes.append(p)
Expand All @@ -28,8 +28,9 @@ def uct_multi(rootstate_: Board, itermax, verbose):
process.join()
# for move in moves:
# state = rootstate_.__copy__()
# state.make_move(move)
# state.make_move(*move)
# uct(queue, move, state, avg_iters, verbose)
# time.sleep(0.1)

results = []
while not queue.empty():
Expand Down Expand Up @@ -61,19 +62,19 @@ def uct(queue: Queue, move_origin, rootstate, itermax, verbose=False):
# Select
while not node.untriedMoves and node.childNodes: # node is fully expanded and non-terminal
node = node.uct_select_child()
state.make_move(node.move)
state.make_move(*node.move)
moves_to_root += 1

# Expand
if node.untriedMoves: # if we can expand (i.e. state/node is non-terminal)
m = rand_choice(node.untriedMoves)
state.make_move(m)
state.make_move(*m)
moves_to_root += 1
node = node.add_child(m, state) # add child and descend tree

# Rollout - this can often be made orders of magnitude quicker using a state.GetRandomMove() function
while state.get_result(state.side) is None: # while state is non-terminal
state.make_move(rand_choice(state.get_moves()))
while state.get_result(state.playerJustMoved) is None: # while state is non-terminal
state.make_move(*rand_choice(state.get_moves()))
moves_to_root += 1

# Backpropagate
Expand Down
19 changes: 16 additions & 3 deletions gui/gui_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,13 @@ def subcell_clicked(self, cell):
self.clicked_cells.add(cell)

move, board = cell.cell_idx, cell.board_idx
self.board.make_move(move, board)
self.board.make_move(board, move)
# print(board, move)
# print(self.board)
# print(self.board.playerJustMoved, self.board.nextBoard)
self.allowed_cells = self.find_allowed_cells()
# print(self.allowed_cells)
# print()

result = self.board.pos[board].get_result()
if result is not None:
Expand Down Expand Up @@ -92,18 +97,26 @@ def click_random_cell(self):
time.sleep(0.2) # sleep 1s so output can be checked

def get_best_engine_move(self):
print(uct_multi(self.board, itermax=10000, verbose=False))
return uct_multi(self.board, itermax=1000, verbose=False)

def do_nothing(self):
pass # todo remove this later

def get_game_input(self, game_type, mouse_pos):
if game_type == GameType.SINGLE_PLAYER:
if self.board.playerJustMoved == PLAYER_O: # X's turn todo allow user to select side to play
# self.click_random_cell() todo used to testing
if mouse_pos is not None:
self.click_cell_under_mouse(mouse_pos)
else:
self.click_random_cell() # todo replace with AI
# self.click_random_cell() # todo replace with AI
board, move = self.get_best_engine_move()
for cell in self.allowed_cells:
if cell.board_idx == board and cell.cell_idx == move:
self.subcell_clicked(cell)
break
else:
raise Exception('Wrong engine move (move not in allowed moves) ({}{})'.format(board, move))

elif game_type == GameType.MULTI_PLAYER:
if mouse_pos is not None:
Expand Down
5 changes: 2 additions & 3 deletions gui/gui_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ def __init__(self, pos_x, pos_y, width, height, player, board_idx=None, cell_idx
self.board_idx = board_idx
self.cell_idx = cell_idx

def __repr__(self):
return '{name}(pos_x={x}, pos_y={y}, width={w}, height={h}, player={p})'.format(
name=self.__class__.__name__, x=self.pos_x, y=self.pos_y, w=self.width, h=self.height, p=self.player)
def __repr__(self): # todo only used for debugging
return '{}(board={}, cell={})'.format(self.__class__.__name__, self.board_idx, self.cell_idx)

def __hash__(self):
return hash((self.pos_x, self.pos_y))
Expand Down

0 comments on commit 0f0fde3

Please sign in to comment.