def get_move(self, game_state, possible_moves): self.clock = time.time() self.time_for_current_move = self.time_remaining_in_round / self.turns_remaining_in_round - 0.05 if len(possible_moves) == 1: return possible_moves[0] # best_move = possible_moves[0] # next_state = copy.deepcopy(game_state) # next_state.perform_move(best_move[0], best_move[1]) # # Choosing an arbitrary move # # Get the best move according the utility function # for move in possible_moves: # new_state = copy.deepcopy(game_state) # new_state.perform_move(move[0], move[1]) # if self.utility(new_state) > self.utility(next_state): # next_state = new_state # best_move = move best_move = possible_moves[0] min_max = MiniMaxWithAlphaBetaPruning(self.utility, self.color, self.no_more_time, None) depth = 1 while self.no_more_time() is False: min_max_val = min_max.search(game_state, depth, -INFINITY, INFINITY, True)[1] best_move = min_max_val if min_max_val is not None else best_move depth += 1 if self.turns_remaining_in_round == 1: self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns else: self.turns_remaining_in_round -= 1 self.time_remaining_in_round -= (time.time() - self.clock) return best_move
def get_move(self, game_state, possible_moves): self.clock = time.clock() if len(possible_moves) == 1: return 0 current_depth = 1 prev_alpha = -INFINITY # Choosing an arbitrary move: best_move = possible_moves[0] # Iterative deepening until the time runs out. while True: print('going to depth: {}, remaining time: {}, prev_alpha: {}, best_move: {}'.format( current_depth, self.time_per_move - (time.clock() - self.clock), prev_alpha, best_move)) minimax = MiniMaxWithAlphaBetaPruning(self.utility, self.color, self.no_more_time) alpha, move = minimax.search(game_state, current_depth, -INFINITY, INFINITY, True) if self.no_more_time(): print('no more time') break prev_alpha = alpha best_move = move if alpha == INFINITY: print('the move: {} will guarantee victory.'.format(best_move)) break current_depth += 1 return possible_moves.index(best_move)
def get_move(self, game_state, possible_moves): self.clock = time.clock() if len(possible_moves) == 1: return 0 current_depth = 1 prev_alpha = -INFINITY # Choosing an arbitrary move: best_move = possible_moves[0] # Iterative deepening until the time runs out. while True: print('going to depth: {}, remaining time: {}, prev_alpha: {}, best_move: {}'.format( current_depth, self.time_per_move - (time.clock() - self.clock), prev_alpha, best_move)) minimax = MiniMaxWithAlphaBetaPruning(self.utility, self.color, self.no_more_time) alpha, move = minimax.search(game_state, current_depth, -INFINITY, INFINITY, True) if self.no_more_time(): print('no more time') break prev_alpha = alpha best_move = move if alpha == INFINITY: print('the move: {} will guarantee victory.'.format(best_move)) break current_depth += 1 return possible_moves.index(best_move)
def __init__(self, setup_time, player_color, time_per_k_turns, k): abstract.AbstractPlayer.__init__(self, setup_time, player_color, time_per_k_turns, k) self.clock = time.time() self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns self.time_for_current_move = self.time_remaining_in_round / self.turns_remaining_in_round - 0.05 self.algorithm = MiniMaxWithAlphaBetaPruning( utility=self.utility, my_color=self.color, no_more_time=self.no_more_time, selective_deepening=self.selective_deeping) self.opening_book = create_better_opening_book(b_create_file=False) self.last_board = [] for i in range(BOARD_COLS): self.last_board.append([EM] * BOARD_ROWS) for x in range(BOARD_COLS): for y in range(BOARD_ROWS): self.last_board[x][y] = EM # Starting pieces: self.last_board[3][3] = X_PLAYER self.last_board[3][4] = O_PLAYER self.last_board[4][3] = O_PLAYER self.last_board[4][4] = X_PLAYER self.moves = ""
def __init__(self, setup_time, player_color, time_per_k_turns, k): abstract.AbstractPlayer.__init__(self, setup_time, player_color, \ time_per_k_turns, k) self.clock = time.time() # We are simply providing (remaining time / remaining turns) for each \ # turn in round. # Taking a spare time of 0.05 seconds. self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns self.time_for_current_move = self.time_remaining_in_round / \ self.turns_remaining_in_round - 0.05 # keep the game-moves as a string self.moves = '' self.last_state = GameState() # chose board configuration if self.color == O_PLAYER: # will be set after opponent first move self.book2reality = None self.reality2book = None else: self.book2reality = self.book2reality1 self.reality2book = self.reality2book1 self.alpha_beta_algorithm = MiniMaxWithAlphaBetaPruning(self.utility, \ self.color, self.no_more_time, None) # for performence self.max_steps_left = 62 # create opening book opening_book = {} f = open("best_70_opens.gam", 'r') # FIXME: remove error check before submmision if not f: print("cannot open file") sys.exit(1) # assume we play first and update otherwise first_move_index = 0 if self.color == O_PLAYER: first_move_index = 1 for line in f: tmp = line.split(' ')[1] tmp = re.split(r'[+-]', tmp) moves = ''.join(tmp) for i in range(first_move_index, 10, 2): tmp = re.split('r[+-]', moves) key = moves[0:2 * i] if key not in opening_book: opening_book[key] = moves[2 * i:2 * i + 2] f.close() self.opening_book = opening_book
def iterative_deepening(self, state): alpha_beta = MiniMaxWithAlphaBetaPruning(self.utility, self.color, self.no_more_time, False) depth = 1 optimal_move = None while True: _, move = alpha_beta.search(state, depth, -INFINITY, INFINITY, True) if move is None: break optimal_move = move depth += 1 return optimal_move
def get_move(self, game_state, possible_moves): self.clock = time.process_time() self.time_for_current_move = self.time_remaining_in_round / self.turns_remaining_in_round - 0.05 if len(possible_moves) == 1: return possible_moves[0] current_depth = 1 prev_alpha = -INFINITY # Choosing an arbitrary move in case Minimax does not return an answer: best_move = possible_moves[0] # Initialize Minimax algorithm, still not running anything minimax = MiniMaxWithAlphaBetaPruning( self.utility, self.color, self.no_more_time, self.selective_deepening_criterion) # Iterative deepening until the time runs out. while True: print( 'going to depth: {}, remaining time: {}, prev_alpha: {}, best_move: {}' .format( current_depth, self.time_for_current_move - (time.process_time() - self.clock), prev_alpha, best_move)) try: (alpha, move), run_time = run_with_limited_time( minimax.search, (game_state, current_depth, -INFINITY, INFINITY, True), {}, self.time_for_current_move - (time.process_time() - self.clock)) except (ExceededTimeError, MemoryError): print('no more time, achieved depth {}'.format(current_depth)) break if self.no_more_time(): print('no more time') break prev_alpha = alpha best_move = move if alpha == INFINITY: print('the move: {} will guarantee victory.'.format(best_move)) break if alpha == -INFINITY: print('all is lost') break current_depth += 1 if self.turns_remaining_in_round == 1: self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns else: self.turns_remaining_in_round -= 1 self.time_remaining_in_round -= (time.process_time() - self.clock) return best_move
def get_move(self, game_state, possible_moves): if len(possible_moves) == 1: return possible_moves[0] self.clock = time.time() self.time_for_current_move = self.time_remaining_in_round / self.turns_remaining_in_round - self.time_per_k_turns * 0.05 minimaxObject = MiniMaxWithAlphaBetaPruning( self.utility, self.color, self.no_more_time, self.selective_deepening_criterion) D = 1 (value, move) = (0, possible_moves[0]) while not self.no_more_time(): (value, move1) = minimaxObject.search(game_state, D, -INFINITY, INFINITY, True) if not self.no_more_time(): move = move1 D = D + 1 return move
def __init__(self, setup_time, player_color, time_per_k_turns, k): abstract.AbstractPlayer.__init__(self, setup_time, player_color, \ time_per_k_turns, k) self.clock = time.time() # We are simply providing (remaining time / remaining turns) for each \ # turn in round. # Taking a spare time of 0.05 seconds. self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns self.time_for_current_move = self.time_remaining_in_round / \ self.turns_remaining_in_round - 0.05 self.alpha_beta_algorithm = MiniMaxWithAlphaBetaPruning(self.utility, \ self.color, self.no_more_time, None) # for performence self.max_steps_left = 62
def get_move(self, game_state, possible_moves): self.clock = time.time() self.time_for_current_move = self.time_remaining_in_round / self.turns_remaining_in_round - 0.05 if len(possible_moves) == 1: return possible_moves[0] best_move = possible_moves[0] next_state = copy.deepcopy(game_state) next_state.perform_move(best_move[0], best_move[1]) best_util = self.utility(next_state) min_max = MiniMaxWithAlphaBetaPruning( self.utility, self.color, self.no_more_time, self.selective_deepening_criterion) i = 1 alpha = -INFINITY max = -INFINITY # print("search time is ", self.time_for_current_move) # start_time = time.time() depth = 0 while not self.no_more_time(): # print("clock ", time.time() - start_time) curr_best_util, curr_best_move = min_max.search( game_state, i, alpha, INFINITY, True) depth += 1 if curr_best_util > best_util: best_move = curr_best_move best_util = curr_best_util if curr_best_util > max: alpha = curr_best_util max = curr_best_util i += 1 # print("clock ", time.time() - start_time) if self.turns_remaining_in_round == 1: self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns else: self.turns_remaining_in_round -= 1 self.time_remaining_in_round -= (time.time() - self.clock) #print("alpha_beta depth : ",depth) return best_move
def __init__(self, setup_time, player_color, time_per_k_turns, k): AbstractPlayer.__init__(self, setup_time, player_color, time_per_k_turns, k) self.clock = time.time() # We are simply providing (remaining time / remaining turns) for each turn in round. # Taking a spare time of 0.05 seconds. self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns self.time_for_current_move = self.time_remaining_in_round / self.turns_remaining_in_round - 0.05 self.simple = simplePlayer(setup_time, player_color, time_per_k_turns, k) self.algorithm = MiniMaxWithAlphaBetaPruning( utility=self.simple.utility, my_color=self.color, no_more_time=self.no_more_time, selective_deepening=self.selective_deeping)
def get_move(self, board_state, possible_moves): self.clock = time.process_time() self.time_for_current_move = self.time_remaining_in_round / self.turns_remaining_in_round -0.5 if len(possible_moves) == 1: return possible_moves[0] current_depth = 1 prev_alpha = -INFINITY # Choosing an arbitrary move: best_move = possible_moves[0] # Iterative deepening until the time runs out. while True: print('going to depth: {}, remaining time: {}, prev_alpha: {}, best_move: {}'.format( current_depth, self.time_for_current_move - (time.process_time() - self.clock), prev_alpha, best_move)) minimax = MiniMaxWithAlphaBetaPruning(self.utility, self.color, self.no_more_time) alpha, move = minimax.search(board_state, current_depth, -INFINITY, INFINITY, True) if self.no_more_time(): print('no more time') break prev_alpha = alpha best_move = move if alpha == INFINITY: print('the move: {} will guarantee victory.'.format(best_move)) break current_depth += 1 if self.turns_remaining_in_round == 1: self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns else: self.turns_remaining_in_round -= 1 self.time_remaining_in_round -= (time.process_time() - self.clock) return best_move
def __init__(self, setup_time, player_color, time_per_k_turns, k): abstract.AbstractPlayer.__init__(self, setup_time, player_color, time_per_k_turns, k) self.clock = time.time() # We are simply providing (remaining time / remaining turns) for each turn in round. # Taking a spare time of 0.05 seconds. self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns self.time_for_current_move = self.time_remaining_in_round / self.turns_remaining_in_round - 0.05 alphaBeta = MiniMaxWithAlphaBetaPruning(self.utilityBetter, player_color, self.no_more_time, False) self.search = alphaBeta.search # divide the board into 5 categories and score them from best to worst cells1Type = [2, 3, 4, 5] cells2TypeA = [1, 6] cells2TypeB = [2, 3, 4, 5] cells3TypeA = [0, 7] cells3TypeB = [2, 3, 4, 5] cells4Type = [0, 1, 6] cells5Type = [0, 7] # initiate the scoring matrix self.scoreMat = np.zeros((8, 8)) for i in cells2TypeA: for j in cells2TypeB: self.scoreMat[i][j] = Player.SCORE_PERIMETER_BORDER self.scoreMat[j][i] = Player.SCORE_PERIMETER_BORDER for i in cells3TypeA: for j in cells3TypeB: self.scoreMat[i][j] = Player.SCORE_BORDER self.scoreMat[j][i] = Player.SCORE_BORDER for i in cells4Type: for j in cells4Type: self.scoreMat[i][j] = Player.SCORE_PERIMETER_CORNER for i in cells5Type: for j in cells5Type: self.scoreMat[i][j] = Player.SCORE_CORNER
class Player(abstract.AbstractPlayer): def __init__(self, setup_time, player_color, time_per_k_turns, k): abstract.AbstractPlayer.__init__(self, setup_time, player_color, \ time_per_k_turns, k) self.clock = time.time() # We are simply providing (remaining time / remaining turns) for each \ # turn in round. # Taking a spare time of 0.05 seconds. self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns self.time_for_current_move = self.time_remaining_in_round / \ self.turns_remaining_in_round - 0.05 # keep the game-moves as a string self.moves = '' self.last_state = GameState() # chose board configuration if self.color == O_PLAYER: # will be set after opponent first move self.book2reality = None self.reality2book = None else: self.book2reality = self.book2reality1 self.reality2book = self.reality2book1 self.alpha_beta_algorithm = MiniMaxWithAlphaBetaPruning(self.utility, \ self.color, self.no_more_time, None) # for performence self.max_steps_left = 62 # create opening book opening_book = {} f = open("best_70_opens.gam", 'r') # FIXME: remove error check before submmision if not f: print("cannot open file") sys.exit(1) # assume we play first and update otherwise first_move_index = 0 if self.color == O_PLAYER: first_move_index = 1 for line in f: tmp = line.split(' ')[1] tmp = re.split(r'[+-]', tmp) moves = ''.join(tmp) for i in range(first_move_index, 10, 2): tmp = re.split('r[+-]', moves) key = moves[0:2 * i] if key not in opening_book: opening_book[key] = moves[2 * i:2 * i + 2] f.close() self.opening_book = opening_book def get_move(self, game_state, possible_moves): self.clock = time.time() self.time_for_current_move = self.time_remaining_in_round / \ self.turns_remaining_in_round - 0.05 self.max_steps_left -= 2 # discover last move done by opponent opponent_move_str_format = '' for x in range(BOARD_COLS): for y in range(BOARD_ROWS): if self.last_state.board[x][ y] == EM and game_state.board[x][y] != EM: opponent_move_str_format = TO_LETTER[str(x + 1)] + str(y + 1) # chose the board_configuration - firs opponent move became 'd3' in # oppening book if self.book2reality == None: if opponent_move_str_format == 'd6': self.book2reality = self.book2reality1 self.reality2book = self.reality2book1 elif opponent_move_str_format == 'e3': self.book2reality = self.book2reality2 self.reality2book = self.reality2book2 elif opponent_move_str_format == 'c5': self.book2reality = self.book2reality3 self.reality2book = self.reality2book3 elif opponent_move_str_format == 'f4': self.book2reality = self.book2reality4 self.reality2book = self.reality2book4 else: raise ImpossibleBoardTransform # append opponent move to self.moves - this will represent the key. # if we play first we will append '' to '' (nothing will hapen) self.moves += self.reality2book(opponent_move_str_format) # this case is handled in run_game.py assert (len(possible_moves) != 0) # find the best move if len(possible_moves) == 1: best_move = possible_moves[0] else: # check if next move can be determined from oppening book oppening_book_res = self.opening_move() if oppening_book_res != None: best_move = oppening_book_res else: curr_depth = 1 # there is maximum max_steps_left steps in the game last_move = None while curr_depth < self.max_steps_left: try: _, best_move = self.alpha_beta_algorithm.search( \ game_state, curr_depth, -INFINITY*2, INFINITY*2, True) if last_move != best_move: game_state_copy = copy.deepcopy(game_state) game_state_copy.perform_move( best_move[0], best_move[1]) self.last_state = game_state_copy last_move = best_move curr_depth += 1 except ExceededTimeError: break to_be_append = TO_LETTER[str(best_move[0] + 1)] + str(best_move[1] + 1) self.moves += self.reality2book(to_be_append) if self.turns_remaining_in_round == 1: self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns else: self.turns_remaining_in_round -= 1 self.time_remaining_in_round -= (time.time() - self.clock) return best_move def opening_move(self): # check if we can find the next move in the oppening book if self.moves not in self.opening_book: return None res = self.book2reality(self.opening_book[self.moves]) return [int(TO_DIGIT[res[0]]) - 1, int(res[1]) - 1] def book2reality1(self, str_move): old_digit = str_move[1] old_letter = str_move[0] new_digit = str(BOARD_ROWS - int(old_digit) + 1) new_letter = old_letter return new_letter + new_digit def reality2book1(self, str_move): if str_move == '': return '' return self.book2reality1(str_move) def book2reality2(self, str_move): old_digit = str_move[1] old_letter = str_move[0] new_digit = old_digit new_letter = REVERSE_LETTER[old_letter] return new_letter + new_digit def reality2book2(self, str_move): if str_move == '': return '' return self.book2reality2(str_move) def book2reality3(self, str_move): old_digit = str_move[1] old_letter = str_move[0] new_digit = TO_LETTER[old_digit] new_letter = TO_DIGIT[REVERSE_LETTER[old_letter]] return new_digit + new_letter def reality2book3(self, str_move): if str_move == '': return '' return self.book2reality4(str_move) def book2reality4(self, str_move): old_digit = str_move[1] old_letter = str_move[0] new_digit = REVERSE_LETTER[TO_LETTER[old_digit]] new_letter = TO_DIGIT[old_letter] return new_digit + new_letter def reality2book4(self, str_move): if str_move == '': return '' return self.book2reality3(str_move) #------------------------------------------------------------------------------ def __is_stable(self, state, J, I): # a corner is always stable if (I == 0 or I == BOARD_ROWS) and (J == 0 or J == BOARD_COLS): return True elif I == 0 or I == BOARD_ROWS - 1: res1 = True res2 = True for j in range(J): if state.board[j][I] == OPPONENT_COLOR[self.color]: res1 = False break for j in range(J, BOARD_COLS): if state.board[j][I] == OPPONENT_COLOR[self.color]: res2 = False break return res1 or res2 elif J == 0 or J == BOARD_COLS - 1: res1 = True res2 = True for i in range(I): if state.board[J][i] == OPPONENT_COLOR[self.color]: res1 = False break for i in range(I, BOARD_ROWS): if state.board[J][i] == OPPONENT_COLOR[self.color]: res2 = False break return res1 or res2 # don't let this method be applied on a non-edge index else: raise NonEdgeIndex def __score_utility(self, state): CORNER_FAC = 100 STABLE_EDGE_FAC = 20 EDGE_FAC = 5 INTER_FAC = 1 my_u = 0 op_u = 0 for x in range(BOARD_COLS): for y in range(BOARD_ROWS): # is a corner if (x == 0 or x == BOARD_COLS - 1) and (y == 0 or y == BOARD_ROWS - 1): if state.board[x][y] == self.color: my_u += CORNER_FAC elif state.board[x][y] == OPPONENT_COLOR[self.color]: op_u += CORNER_FAC # is an edge elif x == 0 or x == BOARD_COLS - 1 or y == 0 or y == BOARD_ROWS - 1: # is stable if self.__is_stable(state, x, y): if state.board[x][y] == self.color: my_u += STABLE_EDGE_FAC elif state.board[x][y] == OPPONENT_COLOR[self.color]: op_u += STABLE_EDGE_FAC # is not stable else: if state.board[x][y] == self.color: my_u += EDGE_FAC elif state.board[x][y] == OPPONENT_COLOR[self.color]: op_u -= EDGE_FAC # is internal else: if state.board[x][y] == self.color: my_u += INTER_FAC elif state.board[x][y] == OPPONENT_COLOR[self.color]: op_u -= INTER_FAC if my_u == 0: # I have no tools left return -INFINITY elif op_u == 0: # The opponent has no tools left return INFINITY else: return my_u - op_u def __mobility_utility(self, state): MOBILITY_FAC = 1 # when this function is called the current player is already the # opponent so we will change it to our player, check the value and # return it as it was op_moves = len(state.get_possible_moves()) state.curr_player = OPPONENT_COLOR[state.curr_player] my_moves = len(state.get_possible_moves()) state.curr_player = OPPONENT_COLOR[state.curr_player] return (my_moves - op_moves) * MOBILITY_FAC def utility(self, state): assert (len(state.get_possible_moves()) != 0) mobility_fac = 1 score_fac = 1 mobility_res = self.__mobility_utility(state) score_res = self.__score_utility(state) return mobility_res * mobility_fac + score_res * score_fac #------------------------------------------------------------------------------ def selective_deepening_criterion(self, state): # Simple player does not selectively deepen into certain nodes. return False def no_more_time(self): return (time.time() - self.clock) >= self.time_for_current_move def __repr__(self): return '{} {}'.format(abstract.AbstractPlayer.__repr__(self), 'old_competition')
class Player(abstract.AbstractPlayer): def __init__(self, setup_time, player_color, time_per_k_turns, k): abstract.AbstractPlayer.__init__(self, setup_time, player_color, time_per_k_turns, k) self.clock = time.time() self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns self.time_for_current_move = self.time_remaining_in_round / self.turns_remaining_in_round - 0.05 self.algorithm = MiniMaxWithAlphaBetaPruning( utility=self.utility, my_color=self.color, no_more_time=self.no_more_time, selective_deepening=self.selective_deeping) self.opening_book = create_better_opening_book(b_create_file=False) self.last_board = [] for i in range(BOARD_COLS): self.last_board.append([EM] * BOARD_ROWS) for x in range(BOARD_COLS): for y in range(BOARD_ROWS): self.last_board[x][y] = EM # Starting pieces: self.last_board[3][3] = X_PLAYER self.last_board[3][4] = O_PLAYER self.last_board[4][3] = O_PLAYER self.last_board[4][4] = X_PLAYER self.moves = "" def __repr__(self): return '{} {}'.format(abstract.AbstractPlayer.__repr__(self), '- competition_player') def no_more_time(self): time_passed = (time.time() - self.clock) self.time_for_current_move -= time_passed self.time_remaining_in_round -= time_passed self.clock = time.time() if self.time_for_current_move <= 0.05 or self.time_remaining_in_round <= 0.05: return True return False def time_for_step(self): return (self.split_time_equally(PERCENTAGE_OF_TIME_TO_SPLIT_EQUALLY * self.time_remaining_in_round) + \ self.split_time_not_equally( PERCENTAGE_OF_TIME_TO_SPLIT_NOT_EQUALLY * self.time_remaining_in_round))*0.97 def split_time_equally(self, time_remaining): return time_remaining / self.turns_remaining_in_round def split_time_not_equally(self, time_remaining): sum_of_remaining_turns = sum(range(self.turns_remaining_in_round + 1)) return time_remaining * (self.turns_remaining_in_round / sum_of_remaining_turns) def get_move(self, game_state, possible_moves): depth = 0 self.time_for_current_move = self.time_for_step() self.clock = time.time() best_move = None max_value = 0 reached_leaves = False while not self.no_more_time() and not reached_leaves: depth += 1 [value, move, reached_leaves] = self.algorithm.search(game_state, depth, -INFINITY, INFINITY, True) if best_move is None or value > max_value: max_value = value best_move = move if self.turns_remaining_in_round == 1: self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns else: self.turns_remaining_in_round -= 1 return best_move if best_move is not None else possible_moves[0] def find_opposite_move(self, game_state): for i in range(BOARD_COLS): for j in range(BOARD_ROWS): if self.last_board[i][j] == EM and game_state.board[i][j] != EM: return i, j return None def opening_move(self, game_state): if len(self.moves) / 2 > NUM_OF_MOVES_IN_OPENING_BOOK: return None other_player_move = self.find_opposite_move(game_state) if other_player_move is None: other_player_move_as_str = "" else: other_player_move_as_str = xy_to_a1(other_player_move) self.moves += other_player_move_as_str return a1_to_xy(self.opening_book[ self.moves]) if self.moves in self.opening_book else None def selective_deeping(self, state): sensitive_spots = [(0, 1), (1, 0), (1, 1), (0, 6), (1, 6), (1, 7), (6, 0), (6, 1), (7, 1), (7, 6), (6, 7), (6, 6)] corners = [(0, 0), (0, 7), (7, 0), (7, 7)] for move in state.get_possible_moves(): if move in sensitive_spots: return True if move in corners: return True return False def utility(self, state): if len(state.get_possible_moves()) == 0: winner = state.get_winner() if winner == self.color: return INFINITY elif winner == TIE: return 0 else: return -INFINITY my_in_corner = 0 opp_in_corner = 0 my_front = 0 opp_front = 0 my = 0 opp = 0 d = 0 my_close_to_corner = 0 opp_close_to_corner = 0 opp_state = copy.deepcopy(state) X = [-1, -1, 0, 1, 1, 1, 0, -1] Y = [0, 1, 1, 1, 0, -1, -1, -1] piece_diff = [[0] * BOARD_ROWS] * BOARD_COLS piece_diff[0] = [20, -3, 11, 8, 8, 11, -3, 20] piece_diff[1] = [-3, -7, -4, 1, 1, -4, -7, -3] piece_diff[2] = [11, -4, 2, 2, 2, 2, -4, 11] piece_diff[3] = [8, 1, 2, -3, -3, 2, 1, 8] piece_diff[4] = [8, 1, 2, -3, -3, 2, 1, 8] piece_diff[5] = [11, -4, 2, 2, 2, 2, -4, 11] piece_diff[6] = [-3, -7, -4, 1, 1, -4, -7, -3] piece_diff[7] = [20, -3, 11, 8, 8, 11, -3, 20] if state.curr_player == X_PLAYER: opp_state.curr_player = O_PLAYER else: opp_state.curr_player = X_PLAYER grid = state.board for x in range(BOARD_COLS): for y in range(BOARD_ROWS): if grid[x][y] == self.color: my += 1 d += piece_diff[x][y] elif grid[x][y] == OPPONENT_COLOR[self.color]: opp += 1 d -= piece_diff[x][y] if grid[x][y] != ' ': for k in range(BOARD_ROWS): i = x + X[k] j = y + Y[k] if i >= 0 and i < BOARD_ROWS and j >= 0 and j < BOARD_ROWS and grid[ i][j] == EM: if grid[x][y] == self.color: my_front += 1 elif grid[x][y] == OPPONENT_COLOR[self.color]: opp_front += 1 break if my_front > opp_front: front = -(100.0 * my_front) / (my_front + opp_front) elif my_front < opp_front: front = (100.0 * opp_front) / (my_front + opp_front) else: front = 0 if my > opp: tiles = (100.0 * my) / (my + opp) elif my < opp: tiles = -(100.0 * opp) / (my + opp) else: tiles = 0 if grid[0][0] == self.color: my_in_corner += 1 elif grid[0][0] == OPPONENT_COLOR[self.color]: opp_in_corner += 1 if grid[0][BOARD_ROWS - 1] == self.color: my_in_corner += 1 elif grid[0][BOARD_ROWS - 1] == OPPONENT_COLOR[self.color]: opp_in_corner += 1 if grid[BOARD_COLS - 1][0] == self.color: my_in_corner += 1 elif grid[BOARD_COLS - 1][0] == OPPONENT_COLOR[self.color]: opp_in_corner += 1 if grid[BOARD_COLS - 1][BOARD_ROWS - 1] == self.color: my_in_corner += 1 elif grid[BOARD_COLS - 1][BOARD_ROWS - 1] == OPPONENT_COLOR[self.color]: opp_in_corner += 1 corners = 25 * (my_in_corner - opp_in_corner) my_options = len(state.get_possible_moves()) opp_options = len(opp_state.get_possible_moves()) if my_options > opp_options: options = (100.0 * my_options) / (my_options + opp_options) elif opp_options > my_options: options = -(100.0 * opp_options) / (my_options + opp_options) else: options = 0 if grid[0][0] == EM: if grid[0][1] == self.color: my_close_to_corner += 1 elif grid[0][1] == OPPONENT_COLOR[self.color]: opp_close_to_corner += 1 if grid[1][1] == self.color: my_close_to_corner += 1 elif grid[1][1] == OPPONENT_COLOR[self.color]: opp_close_to_corner += 1 if grid[1][0] == self.color: my_close_to_corner += 1 elif grid[1][0] == OPPONENT_COLOR[self.color]: opp_close_to_corner += 1 if grid[0][BOARD_COLS - 1] == EM: if grid[0][BOARD_COLS - 2] == self.color: my_close_to_corner += 1 elif grid[0][BOARD_COLS - 2] == OPPONENT_COLOR[self.color]: opp_close_to_corner += 1 if grid[1][BOARD_COLS - 2] == self.color: my_close_to_corner += 1 elif grid[1][BOARD_COLS - 2] == OPPONENT_COLOR[self.color]: opp_close_to_corner += 1 if grid[1][BOARD_COLS - 1] == self.color: my_close_to_corner += 1 elif grid[1][BOARD_COLS - 1] == OPPONENT_COLOR[self.color]: opp_close_to_corner += 1 if grid[BOARD_ROWS - 1][0] == EM: if grid[BOARD_ROWS - 1][1] == self.color: my_close_to_corner += 1 elif grid[BOARD_ROWS - 1][1] == OPPONENT_COLOR[self.color]: opp_close_to_corner += 1 if grid[BOARD_ROWS - 2][1] == self.color: my_close_to_corner += 1 elif grid[BOARD_ROWS - 2][1] == OPPONENT_COLOR[self.color]: opp_close_to_corner += 1 if grid[BOARD_ROWS - 2][0] == self.color: my_close_to_corner += 1 elif grid[BOARD_ROWS - 2][0] == OPPONENT_COLOR[self.color]: opp_close_to_corner += 1 if grid[BOARD_ROWS - 1][BOARD_COLS - 1] == EM: if grid[BOARD_ROWS - 2][BOARD_COLS - 1] == self.color: my_close_to_corner += 1 elif grid[BOARD_ROWS - 2][BOARD_COLS - 1] == OPPONENT_COLOR[self.color]: opp_close_to_corner += 1 if grid[BOARD_ROWS - 2][BOARD_COLS - 2] == self.color: my_close_to_corner += 1 elif grid[BOARD_ROWS - 2][BOARD_COLS - 2] == OPPONENT_COLOR[self.color]: opp_close_to_corner += 1 if grid[BOARD_ROWS - 1][BOARD_COLS - 2] == self.color: my_close_to_corner += 1 elif grid[BOARD_ROWS - 1][BOARD_COLS - 2] == OPPONENT_COLOR[self.color]: opp_close_to_corner += 1 close_to_corner = -12.5 * (my_close_to_corner - opp_close_to_corner) return (10 * tiles) + (801.724 * corners) + (382.026 * close_to_corner) + (78.922 * options) + (74.396 * front) \ + (10 * d)
class Player(abstract.AbstractPlayer): def __init__(self, setup_time, player_color, time_per_k_turns, k): abstract.AbstractPlayer.__init__(self, setup_time, player_color, \ time_per_k_turns, k) self.clock = time.time() # We are simply providing (remaining time / remaining turns) for each \ # turn in round. # Taking a spare time of 0.05 seconds. self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns self.time_for_current_move = self.time_remaining_in_round / \ self.turns_remaining_in_round - 0.05 self.alpha_beta_algorithm = MiniMaxWithAlphaBetaPruning(self.utility, \ self.color, self.no_more_time, None) # for performence self.max_steps_left = 62 def get_move(self, game_state, possible_moves): self.clock = time.time() self.time_for_current_move = self.time_remaining_in_round / \ self.turns_remaining_in_round - 0.05 self.max_steps_left -= 2 if len(possible_moves) == 1: best_move = possible_moves[0] else: curr_depth = 1 # there is maximum max_steps_left steps in the game while curr_depth < self.max_steps_left: try: _, best_move = self.alpha_beta_algorithm.search( \ game_state, curr_depth, -INFINITY, INFINITY, True) curr_depth += 1 except ExceededTimeError: break if self.turns_remaining_in_round == 1: self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns else: self.turns_remaining_in_round -= 1 self.time_remaining_in_round -= (time.time() - self.clock) return best_move def utility(self, state): if len(state.get_possible_moves()) == 0: return INFINITY if state.curr_player != self.color else -INFINITY my_u = 0 op_u = 0 for x in range(BOARD_COLS): for y in range(BOARD_ROWS): if state.board[x][y] == self.color: my_u += 1 if state.board[x][y] == OPPONENT_COLOR[self.color]: op_u += 1 if my_u == 0: # I have no tools left return -INFINITY elif op_u == 0: # The opponent has no tools left return INFINITY else: return my_u - op_u def selective_deepening_criterion(self, state): # Simple player does not selectively deepen into certain nodes. return False def no_more_time(self): return (time.time() - self.clock) >= self.time_for_current_move def __repr__(self): return '{} {}'.format(abstract.AbstractPlayer.__repr__(self), 'alpha_beta')
def get_move(self, game_state, possible_moves): self.clock = time.process_time() self.time_for_current_move = self.time_remaining_in_round / self.turns_remaining_in_round - 0.05 if len(possible_moves) == 1: # update time and turns if self.turns_remaining_in_round == 1: self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns else: self.turns_remaining_in_round -= 1 self.time_remaining_in_round -= (time.process_time() - self.clock) self.curr_board = game_state.board # save game board return possible_moves[0] current_depth = 1 prev_alpha = -INFINITY # Choosing an arbitrary move in case Minimax does not return an answer: best_move = possible_moves[0] # Initialize Minimax algorithm, still not running anything minimax = MiniMaxWithAlphaBetaPruning( self.utility, self.color, self.no_more_time, self.selective_deepening_criterion) # Iterative deepening until the time runs out. while True: time_for_current_depth: float print( 'going to depth: {}, remaining time: {}, prev_alpha: {}, best_move: {}' .format( current_depth, current_depth, self.time_for_current_move - (time.process_time() - self.clock), prev_alpha, best_move)) # The array is init for 6 depth (the average depth) if he succeeded more than that give the remain time if current_depth - 1 > 5: time_for_current_depth = self.time_for_current_move - ( time.process_time() - self.clock) else: # Deeper in the tree get more time (see array values) time_for_current_depth = self.time_for_current_move * self.split_time_array[ current_depth - 1] try: (alpha, move), run_time = run_with_limited_time( minimax.search, (game_state, current_depth, -INFINITY, INFINITY, True), {}, time_for_current_depth) except (ExceededTimeError, MemoryError): print('no more time, achieved depth {}'.format(current_depth)) break if self.no_more_time(): print('no more time') break prev_alpha = alpha best_move = move if alpha == INFINITY: print('the move: {} will guarantee victory.'.format(best_move)) break if alpha == -INFINITY: print('all is lost') break current_depth += 1 if self.turns_remaining_in_round == 1: self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns else: self.turns_remaining_in_round -= 1 self.time_remaining_in_round -= (time.process_time() - self.clock) self.curr_board = game_state.board # save game board return best_move
def get_move(self, game_state, possible_moves): """ This method returns the next move of the player by using a minimax search with Alpha-Beta pruning. In this method we apply a smart time management mechanism. The method performs a minimax search layer by layer, but in case the alpha value and best next move have not been changed in 3 layer cycles (Starting from 5 layer) we stop the search and the best move so far is returned, thus saving time for future moves. In the last turn of the cycle, we exhaust all the remaining time. """ self.clock = time.process_time() self.time_for_current_move = self.time_remaining_in_round / self.turns_remaining_in_round - 0.05 # If there is only one possible move. if len(possible_moves) == 1: # If this was the last turn in current round. if self.turns_remaining_in_round == 1: self.turns_remaining_in_round = self.k # Reset turns count. self.time_remaining_in_round = self.time_per_k_turns # Reset time count. else: self.turns_remaining_in_round -= 1 # Decrease turns amount by 1. self.time_remaining_in_round -= ( time.process_time() - self.clock) # Update remaining time. return possible_moves[0] current_depth = 1 prev_alpha = -INFINITY # Choosing an arbitrary move in case Minimax does not return an answer. best_move = possible_moves[0] # Initialize Minimax algorithm, still not running anything. minimax = MiniMaxWithAlphaBetaPruning( self.utility, self.color, self.no_more_time, self.selective_deepening_criterion) roundsNotChanged = 0 # Iterative deepening until the time runs out. while True: print( 'going to depth: {}, remaining time: {}, prev_alpha: {}, best_move: {}' .format( current_depth, self.time_for_current_move - (time.process_time() - self.clock), prev_alpha, best_move)) try: (alpha, move), run_time = run_with_limited_time( minimax.search, (game_state, current_depth, -INFINITY, INFINITY, True), {}, self.time_for_current_move - (time.process_time() - self.clock)) except (ExceededTimeError, MemoryError): print('no more time, achieved depth {}'.format(current_depth)) break if self.no_more_time(): print('no more time') break # Check if both alpha and next best move according to the last search has not been changed. if prev_alpha == alpha and move.origin_loc == best_move.origin_loc \ and move.target_loc == best_move.target_loc and current_depth > MIN_DEEPENING_DEPTH: # If so, then increment the counter. roundsNotChanged += 1 else: # alpha or next best move were changed - Reset counter. roundsNotChanged = 0 # If alpha and best move have not been changed in 3 cycles, stop searching and save time for future moves. if roundsNotChanged == 3 and self.turns_remaining_in_round > 1: print( 'Best move and alpha has not changed for {} rounds, depth is {}.' .format(roundsNotChanged, current_depth)) break prev_alpha = alpha best_move = move if alpha == INFINITY: print('the move: {} will guarantee victory.'.format(best_move)) break if alpha == -INFINITY: print('all is lost') break current_depth += 1 # If this was the last turn in current round. if self.turns_remaining_in_round == 1: self.turns_remaining_in_round = self.k # Reset turns count. self.time_remaining_in_round = self.time_per_k_turns # Reset time count. else: self.turns_remaining_in_round -= 1 # Decrease turns amount by 1. self.time_remaining_in_round -= (time.process_time() - self.clock ) # Update remaining time. return best_move
res5 = mma.search(root, 10, True) no_more_time2.time = 38 res6 = mma.search(root, 10, False) asssert(res5[0] == 13) asssert(res6[0] == 10) no_more_time2.time = 38 res5 = mma.search(root2, 10, True) no_more_time2.time = 38 res6 = mma.search(root2, 10, False) asssert(res5[0] == 13) asssert(res6[0] == 10) # check alpha betta abp = MiniMaxWithAlphaBetaPruning(utility, 'X', no_more_time2, None) no_more_time2.time = 38 res7 = abp.search(root, 10, -INFINITY, INFINITY, True) no_more_time2.time = 38 res8 = abp.search(root, 10, -INFINITY, INFINITY, False) no_more_time2.time = 38 res9 = abp.search(root, 2, -INFINITY, INFINITY, True) no_more_time2.time = 38 res10 = abp.search(root, 2, -INFINITY, INFINITY, False) asssert(res7[0] == 13) asssert(res8[0] == 10)
def get_move(self, game_state, possible_moves): """ This method is the same as the get_move method of the simple_player, but with a fix of the time management bug found in simple_player that is not counting a move in case this move is the only possible move, thus makes a mis-synchronization between the internal time calculation of the class and the external time calculation which ultimately results in a timeout. """ self.clock = time.process_time() self.time_for_current_move = self.time_remaining_in_round / self.turns_remaining_in_round - 0.05 if len(possible_moves) == 1: # If this was the last turn in current round. if self.turns_remaining_in_round == 1: self.turns_remaining_in_round = self.k # Reset turns count. self.time_remaining_in_round = self.time_per_k_turns # Reset time count. else: self.turns_remaining_in_round -= 1 # Decrease turns amount by 1. self.time_remaining_in_round -= (time.process_time() - self.clock) # Update remaining time. return possible_moves[0] current_depth = 1 prev_alpha = -INFINITY # Choosing an arbitrary move in case Minimax does not return an answer. best_move = possible_moves[0] # Initialize Minimax algorithm, still not running anything. minimax = MiniMaxWithAlphaBetaPruning(self.utility, self.color, self.no_more_time, self.selective_deepening_criterion) # Iterative deepening until the time runs out. while True: print('going to depth: {}, remaining time: {}, prev_alpha: {}, best_move: {}'.format( current_depth, self.time_for_current_move - (time.process_time() - self.clock), prev_alpha, best_move)) try: (alpha, move), run_time = run_with_limited_time( minimax.search, (game_state, current_depth, -INFINITY, INFINITY, True), {}, self.time_for_current_move - (time.process_time() - self.clock)) except (ExceededTimeError, MemoryError): print('no more time, achieved depth {}'.format(current_depth)) break if self.no_more_time(): print('no more time') break prev_alpha = alpha best_move = move if alpha == INFINITY: print('the move: {} will guarantee victory.'.format(best_move)) break if alpha == -INFINITY: print('all is lost') break current_depth += 1 # If this was the last turn in current round. if self.turns_remaining_in_round == 1: self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns else: self.turns_remaining_in_round -= 1 self.time_remaining_in_round -= (time.process_time() - self.clock) return best_move
class Player(AbstractPlayer): def __init__(self, setup_time, player_color, time_per_k_turns, k): AbstractPlayer.__init__(self, setup_time, player_color, time_per_k_turns, k) self.clock = time.time() # We are simply providing (remaining time / remaining turns) for each turn in round. # Taking a spare time of 0.05 seconds. self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns self.time_for_current_move = self.time_remaining_in_round / self.turns_remaining_in_round - 0.05 self.simple = simplePlayer(setup_time, player_color, time_per_k_turns, k) self.algorithm = MiniMaxWithAlphaBetaPruning( utility=self.simple.utility, my_color=self.color, no_more_time=self.no_more_time, selective_deepening=self.selective_deeping) def __repr__(self): return '{} {}'.format(abstract.AbstractPlayer.__repr__(self), '- alpha_beta_player') def no_more_time(self): time_passed = (time.time() - self.clock) self.clock = time.time() self.time_for_current_move -= time_passed self.time_remaining_in_round -= time_passed if self.time_for_current_move <= 0.05 or self.time_remaining_in_round <= 0.05: return True return False def time_for_step(self): return (self.split_time_equally(PERCENTAGE_OF_TIME_TO_SPLIT_EQUALLY * self.time_remaining_in_round) + self.split_time_not_equally( PERCENTAGE_OF_TIME_TO_SPLIT_NOT_EQUALLY * self.time_remaining_in_round)) * 0.97 def split_time_equally(self, time_remaining): return time_remaining / self.turns_remaining_in_round def split_time_not_equally(self, time_remaining): sum_of_remaining_turns = sum(range(self.turns_remaining_in_round + 1)) return time_remaining * (self.turns_remaining_in_round / sum_of_remaining_turns) def selective_deeping(self, state): sensitive_spots = [(0, 1), (1, 0), (1, 1), (0, 6), (1, 6), (1, 7), (6, 0), (6, 1), (7, 1), (7, 6), (6, 7), (6, 6)] corners = [(0, 0), (0, 7), (7, 0), (7, 7)] for move in state.get_possible_moves(): if move in sensitive_spots: return True if move in corners: return True return False def get_move(self, game_state, possible_moves): depth = 0 self.time_for_current_move = self.time_for_step() self.clock = time.time() best_move = None max_value = 0 reached_leaves = False while not self.no_more_time() and not reached_leaves: depth += 1 [value, move, reached_leaves] = self.algorithm.search(game_state, depth, -INFINITY, INFINITY, True) if best_move is None or value > max_value: max_value = value best_move = move if self.turns_remaining_in_round == 1: self.turns_remaining_in_round = self.k self.time_remaining_in_round = self.time_per_k_turns else: self.turns_remaining_in_round -= 1 return best_move if best_move is not None else possible_moves[0]