From 7764a70799116829dbddb7f4ad5e25e49f4af9eb Mon Sep 17 00:00:00 2001 From: Alexander Munch-Hansen Date: Sat, 14 Apr 2018 14:51:50 +0200 Subject: [PATCH] Changed calculate_legal_states to allow for possible face_value of 0 --- board.py | 225 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 129 insertions(+), 96 deletions(-) diff --git a/board.py b/board.py index 2136e47..5103458 100644 --- a/board.py +++ b/board.py @@ -55,116 +55,151 @@ class Board: # tesauro + # @staticmethod + # def board_features_tesauro(board, cur_player): + # def ordinary_trans(val, player): + # abs_val = val * player + # if abs_val <= 0: return (0,0,0,0) + # elif abs_val == 1: return (1,0,0,0) + # elif abs_val == 2: return (1,1,0,0) + # elif abs_val == 3: return (1,1,1,0) + # else: return (1,1,1, (abs_val - 3) / 2) + + # def bar_trans(board, player): + # if player == 1: return (abs(board[0]/2),) + # elif player == -1: return (abs(board[25]/2),) + + # # def ordinary_trans_board(board, player): + # # return np.array( + # # [ordinary_trans(x, player) for x in board[1:25]] + # # ).flatten() + + # board_rep = [] + # for player in [1,-1]: + # for x in board[1:25]: + # board_rep += ordinary_trans(x, player) + # board_rep += bar_trans(board, player) + # board_rep += (15 - Board.num_of_checkers_for_player(board, player),) + + # board_rep += ([1,0] if cur_player == 1 else [1,0]) + + # return np.array(board_rep).reshape(1,198) + + @staticmethod def board_features_tesauro(board, cur_player): - def ordinary_trans(val, player): - abs_val = val * player - if abs_val <= 0: return (0,0,0,0) - elif abs_val == 1: return (1,0,0,0) - elif abs_val == 2: return (1,1,0,0) - elif abs_val == 3: return (1,1,1,0) - else: return (1,1,1, (abs_val - 3) / 2) + features = [] + for player in [-1,1]: + sum = 0.0 + for board_range in range(1,25): + pin = board[board_range] + #print("PIIIN:",pin) + feature = [0.0]*4 + if np.sign(pin) == np.sign(player): + sum += abs(pin) + for i in range(min(abs(pin), 3)): + feature[i] = 1 + if (abs(pin) > 3): + feature[3] = (abs(pin)-3)/2 + features += feature + #print("SUUUM:",sum) + # Append the amount of men on the bar of the current player divided by 2 + features.append((board[0] if np.sign(player) < 0 else board[25]) / 2.0) + # Calculate how many pieces there must be in the home state and divide it by 15 + features.append((15 - sum) / 15) + features += ([1,0] if np.sign(cur_player) > 0 else [0,1]) + test = np.array(features).reshape(1,-1) + #print("TEST:",test) + return test - def bar_trans(board, player): - if player == 1: return (abs(board[0]/2),) - elif player == -1: return (abs(board[25]/2),) - - # def ordinary_trans_board(board, player): - # return np.array( - # [ordinary_trans(x, player) for x in board[1:25]] - # ).flatten() - - board_rep = [] - for player in [1,-1]: - for x in board[1:25]: - board_rep += ordinary_trans(x, player) - board_rep += bar_trans(board, player) - board_rep += (15 - Board.num_of_checkers_for_player(board, player),) - - board_rep += ([1,0] if cur_player == 1 else [0,1]) - - return np.array(board_rep).reshape(1,198) @staticmethod def is_move_valid(board, player, face_value, move): - def sign(a): - return (a > 0) - (a < 0) + if face_value == 0: + return True + else: + def sign(a): + return (a > 0) - (a < 0) - from_idx = move[0] - to_idx = move[1] - to_state = None - from_state = board[from_idx] - delta = to_idx - from_idx - direction = sign(delta) - bearing_off = None + from_idx = move[0] + to_idx = move[1] + to_state = None + from_state = board[from_idx] + delta = to_idx - from_idx + direction = sign(delta) + bearing_off = None - # FIXME: Use get instead of array-like indexing - if to_idx >= 1 and to_idx <= 24: - to_state = board[to_idx] - bearing_off = False - else: # Bearing off - to_state = 0 - bearing_off = True + # FIXME: Use get instead of array-like indexing + if to_idx >= 1 and to_idx <= 24: + to_state = board[to_idx] + bearing_off = False + else: # Bearing off + to_state = 0 + bearing_off = True - # print("_"*20) - # print("board:", board) - # print("to_idx:", to_idx, "board[to_idx]:", board[to_idx], "to_state:", to_state) - # print("+"*20) + # print("_"*20) + # print("board:", board) + # print("to_idx:", to_idx, "board[to_idx]:", board[to_idx], "to_state:", to_state) + # print("+"*20) - def is_forward_move(): - return direction == player + def is_forward_move(): + return direction == player - def face_value_match_move_length(): - return abs(delta) == face_value + def face_value_match_move_length(): + return abs(delta) == face_value - def bear_in_if_checker_on_bar(): - if player == 1: - bar = 0 - else: - bar = 25 - - bar_state = board[bar] - - if bar_state != 0: - return from_idx == bar - else: - return True - - def checkers_at_from_idx(): - return sign(from_state) == player - - def no_block_at_to_idx(): - if -sign(to_state) == player: - return abs(to_state) == 1 - else: - return True - - def can_bear_off(): - checker_idxs = Board.idxs_with_checkers_of_player(board, player) - def is_moving_backmost_checker(): + def bear_in_if_checker_on_bar(): if player == 1: - return all([(idx >= from_idx) for idx in checker_idxs]) + bar = 0 else: - return all([(idx <= from_idx) for idx in checker_idxs]) + bar = 25 - def all_checkers_in_last_quadrant(): - if player == 1: - return all([(idx >= 19) for idx in checker_idxs]) + bar_state = board[bar] + + if bar_state != 0: + return from_idx == bar else: - return all([(idx <= 6) for idx in checker_idxs]) + return True - return all([ is_moving_backmost_checker(), - all_checkers_in_last_quadrant() ]) + def checkers_at_from_idx(): + return sign(from_state) == player + + def no_block_at_to_idx(): + if -sign(to_state) == player: + return abs(to_state) == 1 + else: + return True + + def can_bear_off(): + checker_idxs = Board.idxs_with_checkers_of_player(board, player) + def is_moving_backmost_checker(): + if player == 1: + return all([(idx >= from_idx) for idx in checker_idxs]) + else: + return all([(idx <= from_idx) for idx in checker_idxs]) + + def all_checkers_in_last_quadrant(): + if player == 1: + return all([(idx >= 19) for idx in checker_idxs]) + else: + return all([(idx <= 6) for idx in checker_idxs]) + + return all([ is_moving_backmost_checker(), + all_checkers_in_last_quadrant() ]) # TODO: add switch here instead of wonky ternary in all - - return all([ is_forward_move(), - face_value_match_move_length(), - bear_in_if_checker_on_bar(), - checkers_at_from_idx(), - no_block_at_to_idx(), - can_bear_off() if bearing_off else True ]) + # print("is_forward:",is_forward_move()) + # print("face_value:",face_value_match_move_length()) + # print("Checkes_at_from:",checkers_at_from_idx()) + # print("no_block:",no_block_at_to_idx()) + + return all([ is_forward_move(), + face_value_match_move_length(), + bear_in_if_checker_on_bar(), + checkers_at_from_idx(), + no_block_at_to_idx(), + can_bear_off() if bearing_off else True ]) @staticmethod def any_move_valid(board, player, roll): @@ -232,11 +267,11 @@ class Board: (idx, idx + (face_value * player))) else None) for idx in idxs_with_checkers] - + # print("pls:",boards) board_list = list(filter(None, boards)) # Remove None-values # if len(board_list) == 0: # return [board] - + # print("board list:", board_list) return board_list # Problem with cal_moves: Method can return empty list (should always contain at least same board). @@ -247,12 +282,11 @@ class Board: # 2. Iterate through remaining dice legal_moves = set() - + if not Board.any_move_valid(board, player, roll): return { board } - dice_permutations = list(itertools.permutations(roll)) if roll[0] != roll[1] else [[roll[0]]*4] - + print("Dice permuts:",dice_permutations) for roll in dice_permutations: # Calculate boards resulting from first move #print("initial board: ", board) @@ -265,7 +299,6 @@ class Board: nested_boards = [calc_moves(board, die) for board in boards] #print("nested boards: ", nested_boards) boards = [board for boards in nested_boards for board in boards] - # What the fuck #for board in boards: # print(board)