def ModifiedMinMax(self, board, limit, player, checker = 0): global depth depth = depth + 1 ''' a recursive min-max function the function changes min/max utility based on the player passed the algorithm is modified such that the selection is made based on maximizing one score and minimizing a different score at each step ''' #if the board is half empty the game is over if board.isHalfEmpty(): return 0 max_score = float("-inf") max_move = 0 moves = board.generateOptions(player) isCloneMove = True #check if the game state is in the level just above the limit in the min max tree #only check for maximum profit of the current player and return the best move if ((depth == (limit - 1)) or ((depth == limit) and (limit == 1))): for move_option in moves: new_board = board.clone() new_board_score = new_board.move(move_option, isCloneMove) if new_board_score > max_score: max_score = new_board_score max_move = move_option #on any depth other than limit - 1 recursively call the function and get the best score of the opponent in the resulting stage of the current move #calculate the ratio of the addition to current player score by the move, to best potential addition to the opponent score of the resulting game state by the move #choose the move with maximum ratio else: max_ratio = float("-inf") for move_option in moves: new_board = board.clone() new_player = (player + 1) % 2 score = new_board.move(move_option, isCloneMove) opponent_score = self.ModifiedMinMax(new_board, limit, new_player, depth) if opponent_score != 0: if float(score/opponent_score) > max_ratio: max_score = score max_ratio = float(score/opponent_score) max_move = move_option else: max_score = score max_move = move_option if checker == 1: return max_move else: return max_score
def pickMaxMin(self, board, marker): moves = board.getMoves() pairs = [] for (x, y) in moves: temp = board.clone() temp.move(x, y, marker) if temp.finished(): score = self.evalBoard(temp) else: pick = self.pickMaxMin(temp, self.flip(marker)) score = pick[1] pairs.append(((x, y), score)) pairs.sort(key=lambda x: x[1]) if marker == self.marker: index = -1 else: index = 0 return pairs[index]
def pickMaxMin(self, board, marker): moves = board.getMoves() pairs = [] for (x,y) in moves: temp = board.clone() temp.move(x,y,marker) if temp.finished(): score = self.evalBoard(temp) else: pick = self.pickMaxMin(temp, self.flip(marker)) score = pick[1] pairs.append(((x,y),score)) pairs.sort(key=lambda x:x[1]) if marker == self.marker: index = -1 else: index = 0 return pairs[index]
def greedyPlay(self): ''' plays the game heuristics: look ahead for one step and select the move with maximum score ''' #choosing a pit max_score = float("-inf") max_move = 0 moves = board.generateOptions(player) for move_option in moves: new_board = board.clone() new_board_score = new_board.move(move_option) if new_board_score > max_score: max_score = new_board_score max_move = move_option pit = max_move #make moves and update score score = self.game_board.move(pit) self.updateScore(score) self.switchPlayer()
def mainaction(board): score = [] movescore = [1] allmoves = moves() for lists in allmoves: sandbox = board.clone() oldscore = sandbox.score hole = 0 height = 24 allheights, diff_in_height, var, num_of_blocks, allholes = [], [], [], [], [] for move in lists: if sandbox.falling is None: continue elif isinstance(move, Rotation): sandbox.rotate(move) elif isinstance(move, Direction): sandbox.move(move) else: continue holes = 0 for x in range(sandbox.width): min_y_seen = 24 temp = 0 for y in range(sandbox.height): if (x, y) in sandbox: if y <= min_y_seen: # OVERALL HEIGHT min_y_seen = y allheights.append((height - min_y_seen)) # HEIGHT DIFFERENCES ma = max(y for (x, y) in sandbox.cells) mi = min(y for (x, y) in sandbox.cells) differences = (ma - mi) diff_in_height.append(differences) mean = (height - min_y_seen) / 10 difsq = (y - mean) * (y - mean) var.append(difsq) num_of_blocks.append(len(sandbox.cells)) if (x, y) in sandbox.cells: temp = temp + 1 if (x, y) not in sandbox.cells and temp > 0: holes = holes + 1 allholes.append(holes) # # HOLES IN THE BOARD # if y >= min_y_seen: # if (x, y) in sandbox.cells and (x, y + 1) not in sandbox.cells and (x, y + 2) in sandbox.cells: # hole = hole + 10 # allholes.append(hole) # else: # allholes.append(0) # VARIANCE act_var = sum(var) / 10 # STANDARD DIVIATION sd = [] sd.append(statistics.stdev(allheights)) # BUMPINESS bump = [] for i in range(len(allheights) - 1): l = abs(allheights[i + 1] - allheights[i]) bump.append(l) # GAME if sandbox.score - oldscore > 99: movescore.append(10) elif sandbox.score - oldscore > 199: movescore.append(5) else: movescore.append(0) # movescore = [float(i)/max(movescore) for i in movescore] sumofscores = sum(movescore) sumofheights = sum(allheights) sumofdifferences = sum(diff_in_height) sumofbump = sum(bump) sumofsd = sum(sd) sumofvar = sum(var) / 1000 sumofblocks = sum(num_of_blocks) / 10 sumofholes = sum(allholes) ok = [-0.5, 0.25, 0.29, 0.05, 0.35, 0, 0, 0.35] # Normalise the values of individual weights: sums up to 1 s = [ float(i) / sum(ok) for i in ok ] # from https://stackoverflow.com/questions/26785354/normalizing-a-list-of-numbers-in-python heuristics = (s[0] * sumofheights, s[1] * sumofdifferences, s[2] * sumofscores, s[3] * sumofbump, s[4] * sumofsd, s[5] * sumofvar, s[6] * sumofblocks, s[7] * sumofholes) sum_of_heuro = sum(heuristics) # 2, 16, 1, 8 tuple move - score: 4440 # 2, 0, 0, 8 tuples move - score: 5376 # 10, 1, 1, 8 tuple move - score: 6874 # 10, 1, 1, 10 tuple move - score: 8805 # 10, 1, 10, 10 tuple move - score: 9699 # 10, 1, 10, 1, 10 tuple move score: 10404 (with four heuristics) # 10, 1, 10, 1, 1, 0, 0, 0, 10 tuple move score: 17632 (left, right, left, right, clock, anti-clock) # 1.5, 0.5, 0.5, 0.1, 0, 0, 0, score.append(sum_of_heuro) lowestscoore = score.index(min(score)) return allmoves[lowestscoore]
def set_board(self, board): self.board = board.clone() self.preview.board_drawer.set_board(self.board)
def GetAlphaBetaScore(self, board, limit, player, alpha, beta, checker=0): # The function changes min/max utility with alpha beta prunning based on the player passed # Getting class depth value global depth depth = depth + 1 # If the board is half empty the game is over if board.isEmptyBoard(): return 0 # Gets current choices for this agent max_score = float("-inf") max_move = 0 moves = board.getCurrentChoices(player) isCloneMove = True # The algorithm take moves such that the selection is made based on maximizing one score and minimizing a different score at each step # Also it prunes the some of the tree to avoid bigger space requirement and to decrease time complexity # First of all check if the game state is in the level just above the limit in the min max tree # Then Only check for maximum profit of the current player and return the best move if ((depth == (limit - 1)) or ((depth == limit) and (limit == 1))): for move_option in moves: new_board = board.clone() new_board_score = new_board.move(move_option, isCloneMove, self.current_score) if new_board_score > max_score: max_score = new_board_score max_move = move_option # On any depth other than limit - 1 recursively call the function and get the best score of the opponent in the resulting stage of the current move # Calculate the ratio of the addition to current player score by the move, to best potential addition to the opponent score of the resulting game state by the move # Choose the move with maximum ratio else: max_ratio = float("-inf") # Iterate over all of the choices for move_option in moves: new_board = board.clone() new_player = (player + 1) % 2 score = new_board.move(move_option, isCloneMove, self.current_score) # if new score is smaller than alpha value then prune rest of tree branch if (score <= alpha): return score # if new score is greater than beta value then prune rest of tree branch if (score >= beta): return score # This is the recursion call for another player # This time will be minimizing the score for oppenents opponent_score = self.GetAlphaBetaScore( new_board, limit, new_player, alpha + score, beta + score) if opponent_score != 0: if float(score / opponent_score) > max_ratio: max_score = score max_ratio = float(score / opponent_score) max_move = move_option else: max_score = score max_move = move_option if checker == 1: return max_move else: return max_score