def make_hypothetical_move(start, finish, chessboard): """ Make a hypothetical move, this will be used to generate the possibilities to be stored in the chess tree This method has a ton of redundant code with the make_move() method so I should probably input: starting coordinate: example "e4" ending coordinate: example "e5" chessboard: chessboard that you want to move output: "Move success" or "Move invalid" Uses the RulesEnforcer() to make sure that the move is valid """ #deepcopy the chessboard so that it does not affect the original mychessboard = copy.deepcopy(chessboard[:]) #map start and finish to gameboard coordinates start = RulesEnforcer.coordinate_mapper(start) finish = RulesEnforcer.coordinate_mapper(finish) #need to move alot of this logic to the rules enforcer start_cor0 = start[0] start_cor1 = start[1] finish_cor0 = finish[0] finish_cor1 = finish[1] #check if destination is white, black or empty start_color = mychessboard[start_cor0][start_cor1].split('-')[0] start_piece = mychessboard[start_cor0][start_cor1].split('-')[1] #check if destination is white, black or empty destination_color = mychessboard[finish_cor0][finish_cor1].split( '-')[0] destination_piece = mychessboard[finish_cor0][finish_cor1].split( '-')[1] #cannot move if starting square is empty if start_color == '0': return "Starting square is empty!" mypiece = mychessboard[start_cor0][start_cor1] mychessboard[start_cor0][start_cor1] = '0-0' mychessboard[finish_cor0][finish_cor1] = mypiece return mychessboard
def __init__(self, ai_depth): """ Creates a chessboard with pieces params: ai_depth: max number of moves to search into the future Notation: ------------ 000 == empty space "b-p" == black pawn "b-r" == black rook "b-r" == black rook "b-n" == black knight "b-b" == black bishop "b-q" == black queen "b-k" == black king "w-k" == white king ... etc etc you get the idea As soon as the chess game is initialized, the chess computer will start calculating """ ChessAi.__init__(self, ai_depth) RulesEnforcer.__init__(self) #super(ChessGame, self).__init__() self.ai_depth = ai_depth #initialize the chessboard self.chessboard = [["0-0"] * 8 for i in range(8)] """Track aspects of the game""" #track which pieces have been taken self.white_taken = [] self.black_taken = [] #track which moves have been made in the game, key: move number, value: len 2 list of white and black move self.moves_made = {} #track the number of moves made self.move_count = 0 #track whose turn it is (white always starts) self.current_turn = "w" #create pawns for i in range(8): self.chessboard[1][i] = 'b-p' self.chessboard[6][i] = 'w-p' #create rooks self.chessboard[0][0] = 'b-r' self.chessboard[0][7] = 'b-r' self.chessboard[7][0] = 'w-r' self.chessboard[7][7] = 'w-r' #create knights self.chessboard[0][1] = 'b-n' self.chessboard[0][6] = 'b-n' self.chessboard[7][1] = 'w-n' self.chessboard[7][6] = 'w-n' #create bishops self.chessboard[0][2] = 'b-b' self.chessboard[0][5] = 'b-b' self.chessboard[7][2] = 'w-b' self.chessboard[7][5] = 'w-b' #create queen and king self.chessboard[0][3] = 'b-q' self.chessboard[0][4] = 'b-k' self.chessboard[7][3] = 'w-q' self.chessboard[7][4] = 'w-k' self.game_over = False
def make_move(self, start, finish): """ Make a move input: starting coordinate: example "e4" ending coordinate: example "e5" output: "Move success" or "Move invalid", self.chessboard is updated with the move made Uses the RulesEnforcer() to make sure that the move is valid """ #map start and finish to gameboard coordinates start = RulesEnforcer.coordinate_mapper(start) finish = RulesEnforcer.coordinate_mapper(finish) #need to move alot of this logic to the rules enforcer start_cor0 = start[0] start_cor1 = start[1] finish_cor0 = finish[0] finish_cor1 = finish[1] #check if destination is white, black or empty start_color = self.chessboard[start_cor0][start_cor1].split('-')[0] start_piece = self.chessboard[start_cor0][start_cor1].split('-')[1] #check if destination is white, black or empty destination_color = self.chessboard[finish_cor0][finish_cor1].split( '-')[0] destination_piece = self.chessboard[finish_cor0][finish_cor1].split( '-')[1] #cannot move if starting square is empty if start_color == '0': return "Starting square is empty!" #cannot move the other person's piece if self.current_turn != start_color: return "Cannot move the other person's piece!" #cannot take your own piece if self.current_turn == destination_color: return "invalid move, cannot take your own piece!" elif self.current_turn != destination_color and destination_color != '0': if destination_piece == 'k': self.game_over = True return "game over, " + self.current_turn + " has won" elif self.current_turn == 'w': self.black_taken.append(destination_piece) elif self.current_turn == 'b': self.white_taken.append(destination_piece) else: pass mypiece = self.chessboard[start_cor0][start_cor1] self.chessboard[start_cor0][start_cor1] = '0-0' self.chessboard[finish_cor0][finish_cor1] = mypiece #if the move is a success, change the turn state if self.current_turn == "w": self.current_turn = "b" elif self.current_turn == "b": self.current_turn = "w" return self.chessboard
def tree_generator(self, depth_override=None): """ Brute force tree generation. Generates all possible moves (will probably need to add pruning later) input: current chess position (8 x 8 2d array) output: returns nothing but sets the current game state at self.current_game_state My Notes: We should be able to use the position_evaluator to prune and make the tree generation smarter... Tree generation needs to be done carefully, if we just generate trees based on all possible moves, the size of the tree can easily explode. For example, just assuming that we have around 20 possible moves at each turn, after around 6 moves the size of the tree explodes to 64 million moves (20^6). This is crazy! If I can somehow narrow down the tree search to about 5 moves per tree, then the size of the tree can be drastically reduced, and I could possible compute 10 moves into the future without running out of memory or taking up too much CPU power. I guess after the second move, I don't really need to store the position in the tree, I can just store the score... For the first iteration, just calculate three moves into the future """ #first, lets try to look one move into the future. Then we will expand the AI to look more moves into the future #initialize the tree by putting the current state into the parent node of the chessboard. self.current_game_state = TreeNode([copy.deepcopy(self.chessboard), 0]) current_positions = [self.current_game_state] #track the number of moves into the future you are calculating. current_depth = 1 #override the target depth if depth is explicitly defined target_depth = depth_override or self.depth #get the current turn current_turn = copy.copy(self.current_turn) #keep searching until the desired AI depth has been reached. while current_depth <= target_depth: for position in current_positions: #returns a dictionary of possible chess moves pos_moves = RulesEnforcer.all_possible_moves( position.data[0], current_turn) #now we need to generate all possible moves in the future... #we will do this by iterating through the pos moves dictionary for start, moves in pos_moves.items(): for move in moves: current_pos = position.data[0] new_pos = ChessAi.make_hypothetical_move( start, move, current_pos) if current_turn == 'w': score = ChessAi.position_evaluator(new_pos) else: #if black, store the negative score because black wants to play the best move score = -ChessAi.position_evaluator(new_pos) if current_depth > 1: position.add_child([new_pos, score]) else: position.add_child([new_pos, score, start, move]) current_depth += 1 #now, populate the new current positions list new_positions = [] for position in current_positions: new_positions += position.children current_positions = new_positions #now, switch the turn if current_turn == 'w': current_turn = 'b' else: current_turn = 'w' #run the heuristic algorithm on the list of possible moves you can make to narrow down your search space. #hmm...the problem with this is that unless the heuristic algorithm is very good #you might miss out on really good moves such as a queen sacrifice...I'm not sure what to do here... #pos_evaluated is an array of ints representing the quality of the moves e.g. [3,4,5,6,4] """
def __init__(self, payload, invalidPQ): """ Creates a chessboard with pieces params: ai_depth: max number of moves to search into the future Notation: ------------ 000 == empty space "b-p" == black pawn "b-r" == black rook "b-r" == black rook "b-n" == black knight "b-b" == black bishop "b-q" == black queen "b-k" == black king "w-k" == white king ... etc etc you get the idea As soon as the chess game is initialized, the chess computer will start calculating """ ChessAi.__init__(self, payload['level']) RulesEnforcer.__init__(self) #super(ChessGame, self).__init__() self.ai_depth = payload['level'] chessboard1 = [["0-0"] * 12 for i in range(12)] self.invalidPQ = invalidPQ def fill(color, code, position): chessboard1[position[0]][position[1]] = color + '-' if code == 'rook': chessboard1[position[0]][position[1]] += 'r' elif code == 'pawn': chessboard1[position[0]][position[1]] += 'p' elif code == 'knight': chessboard1[position[0]][position[1]] += 'n' elif code == 'bishop': chessboard1[position[0]][position[1]] += 'b' elif code == 'queen': chessboard1[position[0]][position[1]] += 'q' elif code == 'king': chessboard1[position[0]][position[1]] += 'k' def okPQ(i, j): for k in range(len(self.invalidPQ)): if i == self.invalidPQ[k][0] and j == self.invalidPQ[k][1]: return False return True #initialize the chessboard self.chessboard = [["0-0"] * 8 for i in range(8)] y = payload['board'] for row in y: for column, values in row.items( ) if type(row) is dict else enumerate(row): if values['ai'] == True: fill('b', values['code'], values['position']) else: fill('w', values['code'], values['position']) """Track aspects of the game""" #track which pieces have been taken self.white_taken = [] self.black_taken = [] #track whose turn it is (white always starts) self.current_turn = "b" maxCounter = 0 self.finalP = 0 self.finalQ = 0 for p in range(4): for q in range(4): if okPQ(p, q): counter = 0 hasAiPiece = False hasKing = False hasOpponentPiece = False for i in range(8): for j in range(8): if chessboard1[i + p][j + q] != '0-0': counter += 1 if chessboard1[i + p][j + q][0] == 'b': hasAiPiece = True if chessboard1[i + p][j + q][2] == 'k' and chessboard1[ i + p][j + q][0] == 'w': hasKing = True if chessboard1[i + p][j + q][0] == 'w': hasOpponentPiece = True if counter > maxCounter and hasAiPiece and hasKing and hasOpponentPiece: maxCounter = counter self.finalP = p self.finalQ = q for i in range(8): for j in range(8): self.chessboard[i][j] = chessboard1[i + self.finalP][j + self.finalQ] self.game_over = False