def _check_dfs(self, node): action_dim, hole_dim = 0, 1 if node.node_type == NodeTypes.TERMINAL_CALL or node.node_type == NodeTypes.TERMINAL_FOLD: return assert node.strategy is not None action_count = len(node.children) strategy_ = node.strategy board_mask = Mask.get_board_mask(board=node.board) if node.current_player == Players.CHANCE: check_sum = strategy_.sum(axis=action_dim) assert not (strategy_ <= 0).any() assert not (check_sum > 1.001).any() assert not (check_sum < 0.999).any() assert (node.ranges_absolute[node.ranges_absolute < 0]).sum() == 0 assert (node.ranges_absolute[node.ranges_absolute > 1]).sum() == 0 # check if range consist only of cards that don't overlap with the board board_mask_inverse = Mask.get_board_mask_inverse( board=node.board).reshape((1, Argument.hole_count)) impossible_range_sum = node.ranges_absolute * board_mask_inverse.repeat( repeats=2, axis=0).astype(dtype=float) assert impossible_range_sum.sum() == 0 for child_node in node.children: self._check_dfs(child_node) return
def compute_mask(self, board): """compute hole mask and it's inverse, should be called before _set_xxx_matirx() :param board: a list of board cards :return: None """ self.hole_mask = Mask.get_hole_mask() self.inverse_hole_mask = 1 - self.hole_mask
def compute_fold_matrix(self, board): fold_matrix = np.ones(shape=(self.hc, self.hc), dtype=float) fold_matrix = self.hole_mask.copy() # make sure player hole don't conflict with opponent hole board_mask = Mask.get_board_mask(board) # make sure hole don't conflict with board fold_matrix[board_mask == False, :] = 0 fold_matrix[:, board_mask == False] = 0 return fold_matrix
def __init__(self): self.hc, self.cc = Argument.hole_count, Argument.card_count self.hole_mask = Mask.get_hole_mask() self.board_mask = None self.call_matrix = None self.fold_matrix = None self.call_matrix_tensor = None self.fold_matrix_tensor = None
def get_uniform_range(cls, board): """construct a range vector where all possible hands have same probability all impossible hands have zero probability :return: a uniform range which doesn't conflict with board """ out = Argument.Tensor(Argument.hole_count).zero_() mask = Mask.get_board_mask(board) out[mask] = 1 out.div_(out.sum()) assert out.sum() == 1 return out
def test_terminal_equity_numpy(): dll = cdll.LoadLibrary("../so/hand_eval.dll") if platform.system() == "Windows" \ else cdll.LoadLibrary("../so/hand_eval.so") deck = list(range(52)) te = TerminalEquity() hole_mask = Mask.get_hole_mask() for t in range(10): shuffle(deck) board = deck[0:5] te.set_board(board=board) call_matrix = te.get_call_matrix() fold_matrix = te.get_fold_matrix() # eval using dll _strength = (c_int * 1326)() # param 1 _board = (c_int * 5)(*board) # param3 dll.eval5Board(_board, 5, _strength) strenght_list = np.array(_strength, dtype=int) for i in range(1325): for j in range(i + 1, 1326): assert call_matrix[i, j] == -call_matrix[j, i] # test call matrix for i in range(1325): for j in range(i + 1, 1326): if strenght_list[i] == -1 or strenght_list[j] == -1: # call_matrix[i, j] should be 0, because is conflict with board assert call_matrix[i, j] == 0 elif not hole_mask[i][j]: assert call_matrix[i, j] == 0 elif strenght_list[i] > strenght_list[j]: try: assert call_matrix[i, j] == -1 except AssertionError: print(call_matrix[i, j], strenght_list[i], strenght_list[j]) raise Exception elif strenght_list[i] == strenght_list[j]: assert call_matrix[i, j] == 0 else: assert call_matrix[i, j] == 1 # test fold matrix for i in range(51): for j in range(i + 1, 52): index1 = j * (j - 1) // 2 + i for m in range(51): for n in range(m + 1, 52): index2 = n * (n - 1) // 2 + m if i == m or i == n or j == m or j == n or i in board or j in board or m in board or n in board: assert fold_matrix[index1, index2] == fold_matrix[index2, index1] == 0 else: assert fold_matrix[index1, index2] == fold_matrix[index2, index1] == 1
def test_terminal_equity(): dll = cdll.LoadLibrary("../so/hand_eval.dll") if platform.system() == "Windows" \ else cdll.LoadLibrary("../so/hand_eval.so") deck = list(range(52)) te = TerminalEquity() hole_mask = Mask.get_hole_mask() inv_mask = 1 - hole_mask for t in range(10): shuffle(deck) board = deck[0:5] te.set_board(board=board) call_matrix = te.call_matrix fold_matrix = te.fold_matrix _strength = (c_int * 1326)() dll.eval5Board((c_int * 5)(*board), 5, _strength) strength = torch.FloatTensor(_strength) assert call_matrix[inv_mask].sum() == 0 assert (call_matrix == -call_matrix.t()).all() # test call matrix for i in range(51): for j in range(i + 1, 52): for m in range(51): for n in range(m + 1, 52): idx1 = (j * (j - 1)) // 2 + i idx2 = (n * (n - 1)) // 2 + m if m in (i, j) or n in (i, j) or i in board or j in board or m in board or n in board: assert call_matrix[idx1, idx2] == 0 else: if strength[idx1] > strength[idx2]: assert call_matrix[idx1, idx2] == -1 elif strength[idx1] < strength[idx2]: assert call_matrix[idx1, idx2] == 1 elif strength[idx1] == strength[idx2]: assert call_matrix[idx1, idx2] == 0 # test fold matrix for i in range(51): for j in range(i + 1, 52): index1 = j * (j - 1) // 2 + i for m in range(51): for n in range(m + 1, 52): index2 = n * (n - 1) // 2 + m if i == m or i == n or j == m or j == n or i in board or j in board or m in board or n in board: assert fold_matrix[index1, index2] == fold_matrix[index2, index1] == 0 else: assert fold_matrix[index1, index2] == fold_matrix[index2, index1] == 1
def _set_fold_matrix(self, board): """compute fold_matrix according to hole_mask :param board: a list of board cards :return: None """ self.fold_matrix = Argument.Tensor(Argument.hole_count, Argument.hole_count) board_mask = Mask.get_board_mask(board) inverse_board_mask = 1 - board_mask inverse_board_mask_view1 = inverse_board_mask.view(1, -1).expand_as( self.fold_matrix) inverse_board_mask_view2 = inverse_board_mask.view(-1, 1).expand_as( self.fold_matrix) self.fold_matrix.copy_(self.hole_mask.float()) self.fold_matrix[inverse_board_mask_view1] = 0 self.fold_matrix[inverse_board_mask_view2] = 0
def compute_buckets(self, board): """compute buckets using expected hand strength :parameter board: a list of board card :return a buckets FloatTensor (bucket_count, ) """ board_count = len(board) rd = [0, None, None, 1, 2, 3][board_count] assert board_count in (0, 3, 4, 5) ehs_tensor = self.ehs.get_hand_ehs(board) board_mask = Mask.get_board_mask(board) # use fixed interval bucketing, convert ehs to [0,1,2...499] buckets = ehs_tensor.clone() buckets[board_mask] *= (self.bucket_count - 1) buckets.floor_() return buckets
def generate_data(self, data_count): self.batch_count = data_count // self.batch_size # initialize inputs, targets, masks' place holder if self.inputs_ph is None: self.inputs_ph = Arguments.Tensor(self.batch_size, 2 * 1326 + 1) self.targets_ph = Arguments.Tensor(self.batch_size, 2 * 1326) self.mask_ph = Arguments.Tensor(self.batch_size, 1326) for i in range(self.batch_count): # [1.0] generate random poker situations # board list, ranges FloatTensor, pots FloatTensor board, ranges, pots = self.generate_random_poker_situation() # ranges_tensor = Arguments.Tensor(ranges).view(Arguments.hole_count) # pots_tensor = Arguments.Tensor(pots).div_(Arguments.stack) mask = Mask.get_board_mask(board).float() # convert ByteTensor to FloatTensor # mask self.mask_ph.copy_(mask.view(1, -1).expand_as(self.mask_ph)) # pots of inputs pots.mul_(1 / Arguments.stack) self.inputs_ph[:, -1:].copy_(pots.view(self.batch_size, 1)) # [2.0] solve random poker situations for j in range(self.batch_size): bet = int(pots[j]) bets = [bet, bet] root = self.tree_builder.build_tree(street=self.round, initial_bets=bets, current_player=Players.P0, board=board) start_range = ranges[:, j, :] # (2, hole_count) self.solver.run_cfr(root, start_range=start_range) cf_values = root.cf_values # inputs self.inputs_ph[j, :Arguments.hole_count].copy_(ranges[0, j, :]) self.inputs_ph[j, Arguments.hole_count:-1].copy_(ranges[1, j, :]) # targets self.targets_ph[j, :Arguments.hole_count].copy_(cf_values[0]) self.targets_ph[j, Arguments.hole_count:].copy_(cf_values[1]) # mask already handled before loop # end for # save a batch of data self.save_batch_data()
# -*- coding: utf-8 -*- from Equity.mask import Mask hole_mask = Mask.get_hole_mask() row_sum = hole_mask.float().sum(1) col_sum = hole_mask.float().sum(0) assert (row_sum == 1225).all() assert (col_sum == 1225).all() for i in range(51): for j in range(i + 1, 52): index1 = j * (j - 1) // 2 + i for m in range(51): for n in range(m + 1, 52): index2 = n * (n - 1) // 2 + m if i == m or i == n or j == m or j == n: assert hole_mask[index1, index2] == hole_mask[index2, index1] == False else: assert hole_mask[index1, index2] == hole_mask[index2, index1] == True board = [0, 1, 2] board_mask = Mask.get_board_mask(board=board) assert len(board_mask.float()) == 1326 assert sum(board_mask.float()) == 1176 for i in range(51): for j in range(i + 1, 52): index = j * (j - 1) // 2 + i