def MiniMaxValue(board, node, isMax): """ Gets the made tree top node and going through it recursively to min or max over children board -> Getting the current board node -> top node of created miniMax tree isMax -> Is this iteration of recursive for maxing over children or mining """ """ MinimaxValue: Going from top to bottom of the tree and min and maxing over nodes considering isMax variable and returning the min or max node in each parent. In the end the nominated node returns with its evaluation function point. """ # If this node has no children if (len(node.GetChildren()) == 0): # Making a copy of the board currentBoard = Entities.Board() currentBoard.Copy(board) currentActions = node.actions.copy() # Looping over all actions in this node and updating the board for i in range(currentActions.__len__()): action = currentActions.popleft() currentBoard.Action(action[0], action[1]) # Calculating the evaluation Function point node.point = currentBoard.EvaluationFunction() # returning this node return node # if this node has children else: # Recurse to children subNodes = [] for child in node.GetChildren(): subNodes.append(MiniMaxValue(board, child, not isMax)) # If its the max turn, max over children nodes and return the max if (isMax): maxNode = max(subNodes, key=lambda x: x.point) node.point = maxNode.point return maxNode # If its the min turn, min over children nodes and return the min else: minNode = min(subNodes, key=lambda x: x.point) node.point = minNode.point return minNode
def get_current_board(self): minions = [] weapons = [] heroes = [] for board_card in self.game.in_zone(1): if type(board_card) == Card: id = board_card.card_id if id: card_info = self.get_card_info(id) card_type = card_info['type'] if card_info and card_type != "HERO_POWER": tags = board_card.tags if card_type == 'WEAPON': weapon = entities.BoardWeapon( card_info['name'], id, tags.get(GameTag.ZONE_POSITION, 0), board_card.controller, tags.get(GameTag.ATK, 0), tags.get(GameTag.DURABILITY, 0)) weapons.append(weapon) elif card_type == 'MINION': exhaust = tags.get(GameTag.EXHAUSTED, not tags.get(GameTag.CHARGE, 0)) if tags.get(GameTag.FROZEN): exhaust = 1 minion = entities.BoardMinion( card_info['name'], id, tags[GameTag.ZONE_POSITION], board_card.controller, tags.get(GameTag.ATK, 0), tags.get(GameTag.HEALTH, 0), tags.get(GameTag.TAUNT, 0), exhaust) minions.append(minion) elif card_type == 'HERO': hero = entities.BoardHero( card_info['name'], id, None, board_card.controller, tags.get(GameTag.HEALTH, 30) - tags.get(GameTag.DAMAGE, 0), tags.get(GameTag.EXHAUSTED, 0) ) heroes.append(hero) return entities.Board(minions, heroes, weapons)
def Reset(): """ Refresh the whole window for other experiments, it also terminate the current thread of the gameplay, in the other hand, terminate the current running game. """ global Display global Gameplay global Board global GameplayThread if (Gameplay != None): if (Gameplay.isGameTerminated == False): Gameplay.End() Gameplay = None Board = Entities.Board() Initialization()
A project at the university of Ashrafi Esfahani. """ import entities as Entities import graphics as Graphics import bot as Bot import profiler as Profiler import statistics as Statistics import game as Game import player as Player import threading as Threading import statistics # Global instances Display = Graphics.Display("Fox and Sheeps!", '#ccb684') Board = Entities.Board() Gameplay = None GameplayThread = None def Initialization(): """ Each time we are refreshing the page, we're doing this """ Display.Initialization(Board) def Start(): """ Equivalent to the action of the 'Start' button. Roughly making a new game play """ global GameplayThread global DefaultFox global oncePressed global Gameplay
def train(): # font for training font = pygame.font.SysFont('Comic Sans MS', TRAINING_TEXT_SIZE) # define a base and max moves-per-second (MPS) MPS = 20 MIN_MPS = MPS MAX_MPS = MPS * 2**3 # entities for training board = entities.Board(BOARD_WIDTH, BOARD_HEIGHT) for row in range(BOARD_HEIGHT): for column in range(BOARD_WIDTH): board.add_tile(entities.Tile(column * TILE_SIZE + TILE_MARGIN * column + TILE_MARGIN,\ row * TILE_SIZE + TILE_MARGIN * row + TILE_MARGIN, TILE_SIZE, column, row, 'empty')) empty_button = entities.Button(WINDOW_WIDTH - 1.1 * TILE_BUTTON_SIZE,\ TILE_BUTTON_SIZE, TILE_BUTTON_SIZE, TILE_BUTTON_SIZE) obstacle_button = entities.Button(WINDOW_WIDTH - 1.1 * TILE_BUTTON_SIZE,\ TILE_BUTTON_SIZE + 1.5 * TILE_BUTTON_SIZE, TILE_BUTTON_SIZE, TILE_BUTTON_SIZE) reward_button = entities.Button(WINDOW_WIDTH - 1.1 * TILE_BUTTON_SIZE,\ TILE_BUTTON_SIZE + 3 * TILE_BUTTON_SIZE, TILE_BUTTON_SIZE, TILE_BUTTON_SIZE) # loop variables in_training = True started = False decrease_randomness = True tile_choice = 'empty' current_key_pressed = None while in_training: # refresh background background.fill(TRAINING_BACKGROUND_COLOUR) # check for user mouse input for event in pygame.event.get(): if event.type == pygame.QUIT: # red 'x' at top of window clicked in_training = False if event.type == pygame.MOUSEBUTTONDOWN: # mouse click mouse_position = event.pos if empty_button.collidepoint(mouse_position): tile_choice = 'empty' elif obstacle_button.collidepoint(mouse_position): tile_choice = 'obstacle' elif reward_button.collidepoint(mouse_position): tile_choice = 'reward' elif board.tile_clicked(mouse_position): board.change_tile_type(tile_choice) # handle key inputs from either both, or just one player keys = pygame.key.get_pressed() if keys[pygame.K_ESCAPE]: in_training = False elif keys[pygame.K_RETURN]: started = True elif keys[pygame.K_SPACE] and current_key_pressed != pygame.K_SPACE: decrease_randomness = False if decrease_randomness else True current_key_pressed = pygame.K_SPACE elif keys[pygame.K_RIGHT] and current_key_pressed != pygame.K_RIGHT: if MPS < MAX_MPS: MPS *= 2 current_key_pressed = pygame.K_RIGHT elif keys[pygame.K_LEFT] and current_key_pressed != pygame.K_LEFT: if MPS > MIN_MPS: MPS /= 2 current_key_pressed = pygame.K_LEFT else: current_key_pressed = None # draw the board board.draw_board(background) # update the board board.update(started, decrease_randomness) # display the buttons pygame.draw.rect(background, EMPTY_TILE_COLOUR, empty_button) pygame.draw.rect(background, OBSTACLE_TILE_COLOUR, obstacle_button) pygame.draw.rect(background, REWARD_TILE_COLOUR, reward_button) # display the % random moves and fast-forward speed display_text(font, str(abs(round(board.player.epsilon * 100, 1))) + "% Random",\ (WINDOW_WIDTH - 100, WINDOW_HEIGHT - 50 - TRAINING_TEXT_SIZE), TRAINING_TEXT_COLOUR, centered = True) display_text(font, u"\u00d7" + str(MPS / MIN_MPS), (WINDOW_WIDTH - 50, WINDOW_HEIGHT - 50),\ TRAINING_TEXT_COLOUR, centered = True) # update the window pygame.display.update() # wait to draw the next frame to ensure this frame is visible pygame.time.wait(int(1000 / MPS))
def Minimax(board, turn, depth): """ Returns and action (start, end) which start and end are 2D tuples representing positions in the board. board -> Gets the current board and calculate on it. isFox -> Determining which side of the board (Sheep or fox) now taking a turn. window -> Getting the main window in the graphics for updating purposes. """ """ Minimax: Creates a table of decisions based on all available actions for each side of the game. Tree is full and all parts should be there. We go as deep as we want (here is depth limited) and the reason is we cant go all the way through the tree because game trees are mostly considered unlimited. Then in any depth we want we stop and calculate Evaluation functions for tree leaves and then trace back the tree and run Minimax algorithm which is in each state, we either max or min over children based on whom turn it is. """ global algorithmBreak algorithmBreak = False isFox = True if (turn == Entities.Turn.Sheeps): isFox = False currentDepth = 0 top = Entities.MinimaxTreeNode(currentDepth) top.actions = queue.Queue().queue # Going as deeply as we want, a depth limited. It can be break by minimaxBreak while (algorithmBreak == False and currentDepth != depth): # Getting all the noes in the current depth states = top.GetDepthNodes(currentDepth) if (states == None): break # Looping over all same depth nodes for updating board and make their children for s in states: # Creating a copy of the current board currentBoard = Entities.Board() currentBoard.Copy(board) priviousActions = s.actions.copy() # Loop over all actions respectably in this state and update the board for i in range(s.actions.__len__()): action = s.actions.popleft() currentBoard.Action(action[0], action[1]) availableActions = [] # If this is fox turn if (isFox == True): availableActions = currentBoard.AvailableActionsFox() # If this is sheeps turn elif (isFox == False): availableActions = currentBoard.AvailableActionsSheep() # Loop over all available actions and for each make a child and assign to this state for a in availableActions: child = Entities.MinimaxTreeNode(currentDepth + 1) newActions = priviousActions.copy() newActions.append(a) child.actions = newActions child.parent = s s.AddChildren(child) currentDepth += 1 isFox = not isFox # Filling the tree is done, now we select the appropriate action isFox = True if (turn == Entities.Turn.Sheeps): isFox = False TheNode = MiniMaxValue(board, top, isFox) # Returning the calculated action if (algorithmBreak == False): if (len(TheNode.actions) != 0): return TheNode.actions.popleft(), top else: algorithmBreak = False
def Deepimax(board, node, depth, maxDepth, isMax, doa, roa): """ This is the algorithm i made to go as deep as we can while maintaining good decisions. """ if (depth == 0 and node == None): node = Entities.DeepimaxTreeNode(0) node.actions = queue.Queue().queue # Creating a new instance of the board currentBoard = Entities.Board() currentBoard.Copy(board) previousActions = node.actions.copy() # Updating the board for i in range(previousActions.__len__()): action = previousActions.popleft() currentBoard.Action(action[0], action[1]) # Get list of available actions availableActions = [] if (isMax): availableActions = currentBoard.AvailableActionsFox() else: availableActions = currentBoard.AvailableActionsSheep() # Check the leave (We are in the last depth or we have no children) if (depth == maxDepth or availableActions.__len__() == 0 or (roa == 0 and doa == 0)): node.point = currentBoard.EvaluationFunction() return node theNode = None tempChildren = [] # Make the children for action in availableActions: child = Entities.DeepimaxTreeNode(node.depth + 1) newActions = node.actions.copy() newActions.append(action) child.actions = newActions if ((depth % doa) == 0 and depth != 0): tempChildren.append(child) else: child.parent = node node.AddChildren(child) # Actual Deepimax Algorithms if ((depth % doa) == 0 and depth != 0): # Expecting v = 0 for child in tempChildren: temp = Deepimax(board, child, maxDepth, maxDepth, isMax, 0, 0) if (temp == None): continue v = v + temp.point node.point = v / tempChildren.__len__() return node else: for child in node.children: temp = Deepimax(board, child, depth + 1, maxDepth, not isMax, doa, roa) if (temp == None): continue if ((depth % doa) == doa - 1): # Collective node.nominies.append(temp) continue else: node.nominies += temp.nominies theNode = node if ((depth % doa) == 0): if (theNode.nominies.__len__() != 0): # We have nominees, so we re engage again theNode.nominies = sorted(theNode.nominies, key=lambda x: x.point, reverse=isMax) # Re-engage for 'range of accuracy' times results = [] theMaxDepth = maxDepth - doa if (doa > 1): doa -= 1 currentRoa = roa if (currentRoa > 1): currentRoa -= 1 for i in range(roa): if (i > theNode.nominies.__len__() - 1): break # Call if (theMaxDepth == 0): results.append(theNode.nominies[i]) else: temp = Deepimax(board, theNode.nominies[i], 0, theMaxDepth, not isMax, doa, currentRoa) if (temp != None): results.append(temp) result = None if (isMax): result = max(results, key=lambda x: x.point) else: result = min(results, key=lambda x: x.point) theNode.point = result.point return result else: return theNode else: return theNode
def Expectimax(board, node, depth, maxDebth, isMax, isExpect): """ The expectimax algorithm """ # Creating the node if (depth == 0): node = Entities.MinimaxTreeNode(0) node.actions = queue.Queue().queue # Creating a new instance of the board currentBoard = Entities.Board() currentBoard.Copy(board) previousActions = node.actions.copy() # Updating the board for i in range(previousActions.__len__()): action = previousActions.popleft() currentBoard.Action(action[0], action[1]) # Get list of available actions availableActions = [] if (isMax): availableActions = currentBoard.AvailableActionsFox() else: availableActions = currentBoard.AvailableActionsSheep() # Check the leave (We are in the last depth or we have no children) if (depth == maxDebth or availableActions.__len__() == 0): node.point = currentBoard.EvaluationFunction() return node # Make the children for action in availableActions: child = Entities.MinimaxTreeNode(depth + 1) newActions = node.actions.copy() newActions.append(action) child.actions = newActions child.parent = node node.AddChildren(child) # Actual minimax with alpha beta pruning code if (not isExpect): # Maximum v = -math.inf childNode = None for child in node.children: temp = Expectimax(board, child, depth + 1, maxDebth, not isMax, True) if (temp == None): continue if (temp.point > v): childNode = temp v = temp.point node.point = v return childNode else: # Expecting v = 0 childNode = None for child in node.children: temp = Expectimax(board, child, depth + 1, maxDebth, not isMax, False) if (temp == None): continue v = v + temp.point node.point = v / node.children.__len__() return node
def MinimaxAlphaBeta(board, node, depth, maxDebth, isMax, a, b): """ The minimax Method with alpha beta pruning. Returns a node """ # Creating the node if (depth == 0): node = Entities.MinimaxTreeNode(0) node.actions = queue.Queue().queue # Creating a new instance of the board currentBoard = Entities.Board() currentBoard.Copy(board) previousActions = node.actions.copy() # Updating the board for i in range(previousActions.__len__()): action = previousActions.popleft() currentBoard.Action(action[0], action[1]) # Get list of available actions availableActions = [] if (isMax): availableActions = currentBoard.AvailableActionsFox() else: availableActions = currentBoard.AvailableActionsSheep() # Check the leave (We are in the last depth or we have no children) if (depth == maxDebth or availableActions.__len__() == 0): node.point = currentBoard.EvaluationFunction() return node # Make the children for action in availableActions: child = Entities.MinimaxTreeNode(depth + 1) newActions = node.actions.copy() newActions.append(action) child.actions = newActions child.parent = node node.AddChildren(child) # Actual minimax with alpha beta pruning code if (isMax): v = -math.inf childNode = None for child in node.children: temp = MinimaxAlphaBeta(board, child, depth + 1, maxDebth, False, a, b) if (temp == None): continue if (temp.point > v): childNode = temp v = temp.point if (v >= b): return None a = max(a, v) node.point = v return childNode else: v = math.inf childNode = None for child in node.children: temp = MinimaxAlphaBeta(board, child, depth + 1, maxDebth, True, a, b) if (temp == None): continue if (temp.point < v): childNode = temp v = temp.point if (v <= a): return None b = min(a, v) node.point = v return childNode