Changed calculate_legal_states to allow for possible face_value of 0

This commit is contained in:
Alexander Munch-Hansen 2018-04-14 14:51:50 +02:00
parent c08e7fe540
commit 7764a70799

225
board.py
View File

@ -55,116 +55,151 @@ class Board:
# tesauro # 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 @staticmethod
def board_features_tesauro(board, cur_player): def board_features_tesauro(board, cur_player):
def ordinary_trans(val, player): features = []
abs_val = val * player for player in [-1,1]:
if abs_val <= 0: return (0,0,0,0) sum = 0.0
elif abs_val == 1: return (1,0,0,0) for board_range in range(1,25):
elif abs_val == 2: return (1,1,0,0) pin = board[board_range]
elif abs_val == 3: return (1,1,1,0) #print("PIIIN:",pin)
else: return (1,1,1, (abs_val - 3) / 2) 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 @staticmethod
def is_move_valid(board, player, face_value, move): def is_move_valid(board, player, face_value, move):
def sign(a): if face_value == 0:
return (a > 0) - (a < 0) return True
else:
def sign(a):
return (a > 0) - (a < 0)
from_idx = move[0] from_idx = move[0]
to_idx = move[1] to_idx = move[1]
to_state = None to_state = None
from_state = board[from_idx] from_state = board[from_idx]
delta = to_idx - from_idx delta = to_idx - from_idx
direction = sign(delta) direction = sign(delta)
bearing_off = None bearing_off = None
# FIXME: Use get instead of array-like indexing # FIXME: Use get instead of array-like indexing
if to_idx >= 1 and to_idx <= 24: if to_idx >= 1 and to_idx <= 24:
to_state = board[to_idx] to_state = board[to_idx]
bearing_off = False bearing_off = False
else: # Bearing off else: # Bearing off
to_state = 0 to_state = 0
bearing_off = True bearing_off = True
# print("_"*20) # print("_"*20)
# print("board:", board) # print("board:", board)
# print("to_idx:", to_idx, "board[to_idx]:", board[to_idx], "to_state:", to_state) # print("to_idx:", to_idx, "board[to_idx]:", board[to_idx], "to_state:", to_state)
# print("+"*20) # print("+"*20)
def is_forward_move(): def is_forward_move():
return direction == player return direction == player
def face_value_match_move_length(): def face_value_match_move_length():
return abs(delta) == face_value return abs(delta) == face_value
def bear_in_if_checker_on_bar(): 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():
if player == 1: if player == 1:
return all([(idx >= from_idx) for idx in checker_idxs]) bar = 0
else: else:
return all([(idx <= from_idx) for idx in checker_idxs]) bar = 25
def all_checkers_in_last_quadrant(): bar_state = board[bar]
if player == 1:
return all([(idx >= 19) for idx in checker_idxs]) if bar_state != 0:
return from_idx == bar
else: else:
return all([(idx <= 6) for idx in checker_idxs]) return True
return all([ is_moving_backmost_checker(), def checkers_at_from_idx():
all_checkers_in_last_quadrant() ]) 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 # TODO: add switch here instead of wonky ternary in all
# print("is_forward:",is_forward_move())
return all([ is_forward_move(), # print("face_value:",face_value_match_move_length())
face_value_match_move_length(), # print("Checkes_at_from:",checkers_at_from_idx())
bear_in_if_checker_on_bar(), # print("no_block:",no_block_at_to_idx())
checkers_at_from_idx(),
no_block_at_to_idx(), return all([ is_forward_move(),
can_bear_off() if bearing_off else True ]) 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 @staticmethod
def any_move_valid(board, player, roll): def any_move_valid(board, player, roll):
@ -232,11 +267,11 @@ class Board:
(idx, idx + (face_value * player))) (idx, idx + (face_value * player)))
else None) else None)
for idx in idxs_with_checkers] for idx in idxs_with_checkers]
# print("pls:",boards)
board_list = list(filter(None, boards)) # Remove None-values board_list = list(filter(None, boards)) # Remove None-values
# if len(board_list) == 0: # if len(board_list) == 0:
# return [board] # return [board]
# print("board list:", board_list)
return board_list return board_list
# Problem with cal_moves: Method can return empty list (should always contain at least same board). # 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 # 2. Iterate through remaining dice
legal_moves = set() legal_moves = set()
if not Board.any_move_valid(board, player, roll): if not Board.any_move_valid(board, player, roll):
return { board } return { board }
dice_permutations = list(itertools.permutations(roll)) if roll[0] != roll[1] else [[roll[0]]*4] 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: for roll in dice_permutations:
# Calculate boards resulting from first move # Calculate boards resulting from first move
#print("initial board: ", board) #print("initial board: ", board)
@ -265,7 +299,6 @@ class Board:
nested_boards = [calc_moves(board, die) for board in boards] nested_boards = [calc_moves(board, die) for board in boards]
#print("nested boards: ", nested_boards) #print("nested boards: ", nested_boards)
boards = [board for boards in nested_boards for board in boards] boards = [board for boards in nested_boards for board in boards]
# What the fuck # What the fuck
#for board in boards: #for board in boards:
# print(board) # print(board)