Answers.Body: <p>There are two simple adjustments that need to be made for this to work properly with the computer playing the white pieces. I'm not totally familiar with the interfaces you're using so bear with me.</p>
  
  <pre><code>while not board.is_game_over():
      if board.turn == True:
  </code></pre>
  
  <p>This if statement is used to determine at which point the computer starts evaluating moves and at which point it waits for user input. By changing this to</p>
  
  <pre><code>while not board.is_game_over():
      if board.turn == False:
  </code></pre>
  
  <p>We can make the computer evaluate moves on white's turn instead of black's. Obviously it would be best to handle this with a value that wasn't hardcoded so that based on some user input we could determine which side the user wanted to play. I'll leave that as an exercise for the reader (since you're using selenium perhaps it's possible to detect when the player flips the board around).</p>
  
  <p>Now onto the minimax algorithm. The minimax function itself is doing what it is meant to do, however your evaluation function is locked into evaluating <em>black's</em> position on the board. This means that even though the computer is controlling white's pieces, it is still trying to make black have the best position. This is due to the piece values and the position tables that you are using.</p>
  
  <p>The simple fix for this is something like the following:</p>
  
  <pre><code>def minimax(position, depth, alpha, beta, maximizingPlayer, bestMove = 'h1h3'):
      if depth == 0 or position.is_game_over():
          if (computer == "BLACK"):
              return positionEvaluation(position, piece_values, position_values), bestMove
          else:
              return -1*positionEvaluation(position, piece_values, position_values), bestMove
  </code></pre>
  
  <p>Where "computer" is some variable you've defined that stores the current player that the computer is evaluating.</p>
  
  <p>We simply invert the evaluation when white is being controlled and the minimax algorithm gives the expected results.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-11-28T01:53:29.947Z
Answers.LastActivityDate: 2019-11-29T00:25:46.747Z
Answers.LastEditDate: 2019-11-29T00:25:46.747Z
Answers.UserDisplayName: Rhys
Body: <p><strong>Motivation:</strong></p>
  
  <p>I am trying to make a basic AI agent that can play chess against an opponent. The goal is to see how good it can become through the use of machine learning later on and also learn the fine details in chess that are hidden from us when we just play it, such as evaluation parameters. </p>
  
  <hr>
  
  <p><strong>Code:</strong></p>
  
  <p>Here is what I have so far:</p>
  
  <pre><code>import chess, chess.pgn, time, math, io
  import numpy as np 
  
  from selenium import webdriver
  from selenium.webdriver.common.keys import Keys
  from selenium.webdriver.common.action_chains import ActionChains
  from selenium.webdriver.support.ui import Select
  
  piece_values = {'P': 10, 'N': 30, 'B': 30, 'R': 50, 'Q': 90, 'K': 100, 'p': -10, 'n': -30, 'b': -30, 'r': -50, 'q': -90, 'k': -100}
  
  # These are all flipped
  position_values = {
          'P' : np.array([ [0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0],
                          [5.0,  5.0,  5.0,  5.0,  5.0,  5.0,  5.0,  5.0],
                          [1.0,  1.0,  2.0,  3.0,  3.0,  2.0,  1.0,  1.0],
                          [0.5,  0.5,  1.0,  2.5,  2.5,  1.0,  0.5,  0.5],
                          [0.0,  0.0,  0.0,  2.0,  2.0,  0.0,  0.0,  0.0],
                          [0.5, -0.5, -1.0,  0.0,  0.0, -1.0, -0.5,  0.5],
                          [0.5,  1.0, 1.0,  -2.0, -2.0,  1.0,  1.0,  0.5],
                          [0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0] ]),
  
          'N' : np.array([[-5.0, -4.0, -3.0, -3.0, -3.0, -3.0, -4.0, -5.0],
                         [-4.0, -2.0,  0.0,  0.0,  0.0,  0.0, -2.0, -4.0],
                         [-3.0,  0.0,  1.0,  1.5,  1.5,  1.0,  0.0, -3.0],
                         [-3.0,  0.5,  1.5,  2.0,  2.0,  1.5,  0.5, -3.0],
                         [-3.0,  0.0,  1.5,  2.0,  2.0,  1.5,  0.0, -3.0],
                         [-3.0,  0.5,  1.0,  1.5,  1.5,  1.0,  0.5, -3.0],
                         [-4.0, -2.0,  0.0,  0.5,  0.5,  0.0, -2.0, -4.0],
                         [-5.0, -4.0, -3.0, -3.0, -3.0, -3.0, -4.0, -5.0] ]),
  
          'B' : np.array([[-2.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -2.0],
                         [-1.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0, -1.0],
                         [-1.0,  0.0,  0.5,  1.0,  1.0,  0.5,  0.0, -1.0],
                         [-1.0,  0.5,  0.5,  1.0,  1.0,  0.5,  0.5, -1.0],
                         [-1.0,  0.0,  1.0,  1.0,  1.0,  1.0,  0.0, -1.0],
                         [-1.0,  1.0,  1.0,  1.0,  1.0,  1.0,  1.0, -1.0],
                         [-1.0,  0.5,  0.0,  0.0,  0.0,  0.0,  0.5, -1.0],
                         [-2.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -2.0] ]),
  
          'R' : np.array([[ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,  0.0],
                         [ 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,  0.5],
                         [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                         [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                         [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                         [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                         [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                         [ 0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0,  0.0]]),
  
          'Q' : np.array([[-2.0, -1.0, -1.0, -0.5, -0.5, -1.0, -1.0, -2.0],
                         [-1.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0, -1.0],
                         [-1.0,  0.0,  0.5,  0.5,  0.5,  0.5,  0.0, -1.0],
                         [-0.5,  0.0,  0.5,  0.5,  0.5,  0.5,  0.0, -0.5],
                         [-0.5,  0.0,  0.5,  0.5,  0.5,  0.5,  0.0, -0.5],
                         [-1.0,  0.5,  0.5,  0.5,  0.5,  0.5,  0.0, -1.0],
                         [-1.0,  0.0,  0.5,  0.0,  0.0,  0.0,  0.0, -1.0],
                         [-2.0, -1.0, -1.0, -0.5, -0.5, -1.0, -1.0, -2.0]]),
  
          'K' : np.array([[ -3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0],
                         [ -3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0],
                         [ -3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0],
                         [ -3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0],
                         [ -2.0, -3.0, -3.0, -4.0, -4.0, -3.0, -3.0, -2.0],
                         [ -1.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -1.0],
                         [  2.0,  2.0,  0.0,  0.0,  0.0,  0.0,  2.0,  2.0 ],
                         [  2.0,  3.0,  1.0,  0.0,  0.0,  1.0,  3.0,  2.0 ]])}
  
  class LichessBot:
      def __init__(self, fen):
          self.fen = fen
          self.bot = webdriver.Firefox(executable_path=r'geckodriver.exe')
  
      def initialize(self):
          bot = self.bot
          bot.get('https://lichess.org/editor/rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR_w_KQkq_-')
          time.sleep(3)
          analysis = bot.find_element_by_css_selector(".actions &gt; a:nth-child(2)").click()
          time.sleep(1)
  
      def gameSelect(self, fen):
          bot = self.bot
  
          fen_area = bot.find_element_by_class_name("analyse__underboard__fen")
          bot.execute_script('arguments[0].setAttribute("value", arguments[1]);', fen_area, fen)
  
          # Refresh the page to enter new fen number properly every time
          fen_new = bot.find_element_by_class_name("analyse__underboard__fen").get_attribute('value').replace(' ', '_')
          bot.get('https://lichess.org/analysis/standard/{}'.format(fen_new))
  
      def gameReturn(self):
          bot = self.bot
  
          fen_return = bot.find_element_by_class_name("analyse__underboard__fen").get_attribute('value')
          time.sleep(1)
          return fen_return
  
  def positionEvaluation(position, piece_values=piece_values, position_values=position_values):
      # Position of pieces is not taken into account for their strength
      if position_values == 'None':
          total_eval = 0
          pieces = list(position.piece_map().values())
  
          for piece in pieces:
              total_eval += piece_values[str(piece)]
  
          return total_eval
  
      else:
          positionTotalEval = 0
          pieces = position.piece_map()
  
          for j in pieces:
              file = chess.square_file(j)
              rank = chess.square_rank(j)
  
              piece_type = str(pieces[j])
              positionArray = position_values[piece_type.upper()]
  
              if piece_type.isupper():
                  flippedPositionArray = np.flip(positionArray, axis=0)
                  positionTotalEval += piece_values[piece_type] + flippedPositionArray[rank, file]
  
              else:
                  positionTotalEval += piece_values[piece_type] - positionArray[rank, file]
  
          return positionTotalEval
  
  def minimax(position, depth, alpha, beta, maximizingPlayer, bestMove = 'h1h3'):
      if depth == 0 or position.is_game_over():
          return positionEvaluation(position, piece_values, position_values), bestMove
  
      if maximizingPlayer:
          maxEval = -np.inf
          for child in [str(i).replace("Move.from_uci(\'", '').replace('\')', '') for i in list(position.legal_moves)]:
              position.push(chess.Move.from_uci(child))
              eval_position = minimax(position, depth-1, alpha, beta, False)[0]
              position.pop()
              maxEval = np.maximum(maxEval, eval_position)
              alpha = np.maximum(alpha, eval_position)
              if beta &lt;= alpha:
                  break
          return maxEval
  
      else:
          minEval = np.inf
          minMove = np.inf
          for child in [str(i).replace("Move.from_uci(\'", '').replace('\')', '') for i in list(position.legal_moves)]:
              position.push(chess.Move.from_uci(child))
              eval_position = minimax(position, depth-1, alpha, beta, True)
              position.pop()
              minEval = np.minimum(minEval, eval_position)
              if minEval &lt; minMove:
                  minMove = minEval
                  bestMin = child
  
              beta = np.minimum(beta, eval_position)
              if beta &lt;= alpha:
                  break
  
          return minEval, bestMin
  
  # # To check evaluation
  # board = chess.Board()
  # print(positionEvaluation(board))
  # quit()
  
  # Initialize and set up position
  lichess = LichessBot('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -')
  lichess.initialize()
  
  board = chess.Board()
  fen = board.fen()
  lichess.gameSelect(fen)
  
  while not board.is_game_over():
      if board.turn == True:
          print('\n[INFO] Your Turn\n=========================')
          fen_new = fen
          while fen_new == fen:
              fen_new = lichess.gameReturn()
          board = chess.Board(fen_new)
  
      else:
          print('[INFO] AI\'s Turn\n')
          minimaxEval, bestMove = minimax(board, 4, -np.inf, np.inf, False)
          print("AI Evaluation: {}\nAI Best Move: {}".format(minimaxEval, bestMove))
          board.push(chess.Move.from_uci(bestMove))
          print("{}\n=========================".format(board))
          fen = board.fen()
          lichess.gameSelect(fen)
  </code></pre>
  
  <p>This is what the code does:</p>
  
  <ul>
  <li><p>Open firefox terminal and go to lichess.org</p></li>
  <li><p>Enter the analysis mode for a starting chess position</p></li>
  <li><p>Wait for human player to make a move </p></li>
  <li><p>Send the FEN to the python program to make that move</p></li>
  <li><p>Apply minimax algorithm with corresponding depth and position values to evaluate the position and decide the best move</p></li>
  <li><p>Make this move in the python program</p></li>
  <li><p>Get the FEN of the current position</p></li>
  <li><p>Play the best move on the board by pasting FEN into the analysis on lichess</p></li>
  </ul>
  
  <hr>
  
  <p><strong>Question:</strong></p>
  
  <p>Right now this only lets me play as the white pieces (computer algorithm works on the black pieces). My question, although it seems basic, is how to make it so that at the start I have the choice of which side to choose? It seems like the minimax algorithm is baised towards computer playing with the black pieces and any attempt I make to adjust this failed to work.</p>
  
  <hr>
  
  <p><strong>Output:</strong></p>
  
  <p>Here is what a typical output on the console would look like while the game is going on.
  Nothing special happens when the game ends, I plan to include a summary of the game and outcome later on.</p>
  
  <p><a href="https://i.sstatic.net/snTy8.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/snTy8.png" alt="Console output during a game"></a></p>
  
  <p>As can be seen, I make sure to double check that the moves are correctly registered by printing the board setup position in the console output after every move.</p>
  
  <hr>
  
  <p><strong>Final Note:</strong></p>
  
  <p>I am aware the evaluation metric and maybe even the efficiency of the algorithm might not be the best but these will be adjusted once all the fine details, such as the one posted in the question, are answered. </p>
  
Tags: <engines><online-chess><chess-ai><minimax>
Title: Minimax algorithm - Play with Black pieces?
ThreadText: # Minimax algorithm - Play with Black pieces?
  
  (Asked by: username_0 on 2019-11-26)
  
  Motivation:
  I am trying to make a basic AI agent that can play chess against an opponent. The goal is to see how good it can become through the use of machine learning later on and also learn the fine details in chess that are hidden from us when we just play it, such as evaluation parameters. 
  
  Code:
  Here is what I have so far:
  import chess, chess.pgn, time, math, io
  import numpy as np 
  
  from selenium import webdriver
  from selenium.webdriver.common.keys import Keys
  from selenium.webdriver.common.action_chains import ActionChains
  from selenium.webdriver.support.ui import Select
  
  piece_values = {'P': 10, 'N': 30, 'B': 30, 'R': 50, 'Q': 90, 'K': 100, 'p': -10, 'n': -30, 'b': -30, 'r': -50, 'q': -90, 'k': -100}
  
  # These are all flipped
  position_values = {
          'P' : np.array([ [0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0],
                          [5.0,  5.0,  5.0,  5.0,  5.0,  5.0,  5.0,  5.0],
                          [1.0,  1.0,  2.0,  3.0,  3.0,  2.0,  1.0,  1.0],
                          [0.5,  0.5,  1.0,  2.5,  2.5,  1.0,  0.5,  0.5],
                          [0.0,  0.0,  0.0,  2.0,  2.0,  0.0,  0.0,  0.0],
                          [0.5, -0.5, -1.0,  0.0,  0.0, -1.0, -0.5,  0.5],
                          [0.5,  1.0, 1.0,  -2.0, -2.0,  1.0,  1.0,  0.5],
                          [0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0] ]),
  
          'N' : np.array([[-5.0, -4.0, -3.0, -3.0, -3.0, -3.0, -4.0, -5.0],
                         [-4.0, -2.0,  0.0,  0.0,  0.0,  0.0, -2.0, -4.0],
                         [-3.0,  0.0,  1.0,  1.5,  1.5,  1.0,  0.0, -3.0],
                         [-3.0,  0.5,  1.5,  2.0,  2.0,  1.5,  0.5, -3.0],
                         [-3.0,  0.0,  1.5,  2.0,  2.0,  1.5,  0.0, -3.0],
                         [-3.0,  0.5,  1.0,  1.5,  1.5,  1.0,  0.5, -3.0],
                         [-4.0, -2.0,  0.0,  0.5,  0.5,  0.0, -2.0, -4.0],
                         [-5.0, -4.0, -3.0, -3.0, -3.0, -3.0, -4.0, -5.0] ]),
  
          'B' : np.array([[-2.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -2.0],
                         [-1.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0, -1.0],
                         [-1.0,  0.0,  0.5,  1.0,  1.0,  0.5,  0.0, -1.0],
                         [-1.0,  0.5,  0.5,  1.0,  1.0,  0.5,  0.5, -1.0],
                         [-1.0,  0.0,  1.0,  1.0,  1.0,  1.0,  0.0, -1.0],
                         [-1.0,  1.0,  1.0,  1.0,  1.0,  1.0,  1.0, -1.0],
                         [-1.0,  0.5,  0.0,  0.0,  0.0,  0.0,  0.5, -1.0],
                         [-2.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -2.0] ]),
  
          'R' : np.array([[ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,  0.0],
                         [ 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,  0.5],
                         [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                         [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                         [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                         [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                         [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5],
                         [ 0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0,  0.0]]),
  
          'Q' : np.array([[-2.0, -1.0, -1.0, -0.5, -0.5, -1.0, -1.0, -2.0],
                         [-1.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0, -1.0],
                         [-1.0,  0.0,  0.5,  0.5,  0.5,  0.5,  0.0, -1.0],
                         [-0.5,  0.0,  0.5,  0.5,  0.5,  0.5,  0.0, -0.5],
                         [-0.5,  0.0,  0.5,  0.5,  0.5,  0.5,  0.0, -0.5],
                         [-1.0,  0.5,  0.5,  0.5,  0.5,  0.5,  0.0, -1.0],
                         [-1.0,  0.0,  0.5,  0.0,  0.0,  0.0,  0.0, -1.0],
                         [-2.0, -1.0, -1.0, -0.5, -0.5, -1.0, -1.0, -2.0]]),
  
          'K' : np.array([[ -3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0],
                         [ -3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0],
                         [ -3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0],
                         [ -3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0],
                         [ -2.0, -3.0, -3.0, -4.0, -4.0, -3.0, -3.0, -2.0],
                         [ -1.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -1.0],
                         [  2.0,  2.0,  0.0,  0.0,  0.0,  0.0,  2.0,  2.0 ],
                         [  2.0,  3.0,  1.0,  0.0,  0.0,  1.0,  3.0,  2.0 ]])}
  
  class LichessBot:
      def __init__(self, fen):
          self.fen = fen
          self.bot = webdriver.Firefox(executable_path=r'geckodriver.exe')
  
      def initialize(self):
          bot = self.bot
          bot.get('https://lichess.org/editor/rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR_w_KQkq_-')
          time.sleep(3)
          analysis = bot.find_element_by_css_selector(".actions > a:nth-child(2)").click()
          time.sleep(1)
  
      def gameSelect(self, fen):
          bot = self.bot
  
          fen_area = bot.find_element_by_class_name("analyse__underboard__fen")
          bot.execute_script('arguments[0].setAttribute("value", arguments[1]);', fen_area, fen)
  
          # Refresh the page to enter new fen number properly every time
          fen_new = bot.find_element_by_class_name("analyse__underboard__fen").get_attribute('value').replace(' ', '_')
          bot.get('https://lichess.org/analysis/standard/{}'.format(fen_new))
  
      def gameReturn(self):
          bot = self.bot
  
          fen_return = bot.find_element_by_class_name("analyse__underboard__fen").get_attribute('value')
          time.sleep(1)
          return fen_return
  
  def positionEvaluation(position, piece_values=piece_values, position_values=position_values):
      # Position of pieces is not taken into account for their strength
      if position_values == 'None':
          total_eval = 0
          pieces = list(position.piece_map().values())
  
          for piece in pieces:
              total_eval += piece_values[str(piece)]
  
          return total_eval
  
      else:
          positionTotalEval = 0
          pieces = position.piece_map()
  
          for j in pieces:
              file = chess.square_file(j)
              rank = chess.square_rank(j)
  
              piece_type = str(pieces[j])
              positionArray = position_values[piece_type.upper()]
  
              if piece_type.isupper():
                  flippedPositionArray = np.flip(positionArray, axis=0)
                  positionTotalEval += piece_values[piece_type] + flippedPositionArray[rank, file]
  
              else:
                  positionTotalEval += piece_values[piece_type] - positionArray[rank, file]
  
          return positionTotalEval
  
  def minimax(position, depth, alpha, beta, maximizingPlayer, bestMove = 'h1h3'):
      if depth == 0 or position.is_game_over():
          return positionEvaluation(position, piece_values, position_values), bestMove
  
      if maximizingPlayer:
          maxEval = -np.inf
          for child in [str(i).replace("Move.from_uci(\'", '').replace('\')', '') for i in list(position.legal_moves)]:
              position.push(chess.Move.from_uci(child))
              eval_position = minimax(position, depth-1, alpha, beta, False)[0]
              position.pop()
              maxEval = np.maximum(maxEval, eval_position)
              alpha = np.maximum(alpha, eval_position)
              if beta <= alpha:
                  break
          return maxEval
  
      else:
          minEval = np.inf
          minMove = np.inf
          for child in [str(i).replace("Move.from_uci(\'", '').replace('\')', '') for i in list(position.legal_moves)]:
              position.push(chess.Move.from_uci(child))
              eval_position = minimax(position, depth-1, alpha, beta, True)
              position.pop()
              minEval = np.minimum(minEval, eval_position)
              if minEval < minMove:
                  minMove = minEval
                  bestMin = child
  
              beta = np.minimum(beta, eval_position)
              if beta <= alpha:
                  break
  
          return minEval, bestMin
  
  # # To check evaluation
  # board = chess.Board()
  # print(positionEvaluation(board))
  # quit()
  
  # Initialize and set up position
  lichess = LichessBot('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -')
  lichess.initialize()
  
  board = chess.Board()
  fen = board.fen()
  lichess.gameSelect(fen)
  
  while not board.is_game_over():
      if board.turn == True:
          print('\n[INFO] Your Turn\n=========================')
          fen_new = fen
          while fen_new == fen:
              fen_new = lichess.gameReturn()
          board = chess.Board(fen_new)
  
      else:
          print('[INFO] AI\'s Turn\n')
          minimaxEval, bestMove = minimax(board, 4, -np.inf, np.inf, False)
          print("AI Evaluation: {}\nAI Best Move: {}".format(minimaxEval, bestMove))
          board.push(chess.Move.from_uci(bestMove))
          print("{}\n=========================".format(board))
          fen = board.fen()
          lichess.gameSelect(fen)
  
  This is what the code does:
  
  Open firefox terminal and go to lichess.org
  Enter the analysis mode for a starting chess position
  Wait for human player to make a move 
  Send the FEN to the python program to make that move
  Apply minimax algorithm with corresponding depth and position values to evaluate the position and decide the best move
  Make this move in the python program
  Get the FEN of the current position
  Play the best move on the board by pasting FEN into the analysis on lichess
  
  
  Question:
  Right now this only lets me play as the white pieces (computer algorithm works on the black pieces). My question, although it seems basic, is how to make it so that at the start I have the choice of which side to choose? It seems like the minimax algorithm is baised towards computer playing with the black pieces and any attempt I make to adjust this failed to work.
  
  Output:
  Here is what a typical output on the console would look like while the game is going on.
  Nothing special happens when the game ends, I plan to include a summary of the game and outcome later on.
  
  As can be seen, I make sure to double check that the moves are correctly registered by printing the board setup position in the console output after every move.
  
  Final Note:
  I am aware the evaluation metric and maybe even the efficiency of the algorithm might not be the best but these will be adjusted once all the fine details, such as the one posted in the question, are answered.
  
  **Tags:** engines, online-chess, chess-ai, minimax
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2019-11-28. Score: 3 | Accepted answer
  
  There are two simple adjustments that need to be made for this to work properly with the computer playing the white pieces. I'm not totally familiar with the interfaces you're using so bear with me.
  while not board.is_game_over():
      if board.turn == True:
  
  This if statement is used to determine at which point the computer starts evaluating moves and at which point it waits for user input. By changing this to
  while not board.is_game_over():
      if board.turn == False:
  
  We can make the computer evaluate moves on white's turn instead of black's. Obviously it would be best to handle this with a value that wasn't hardcoded so that based on some user input we could determine which side the user wanted to play. I'll leave that as an exercise for the reader (since you're using selenium perhaps it's possible to detect when the player flips the board around).
  Now onto the minimax algorithm. The minimax function itself is doing what it is meant to do, however your evaluation function is locked into evaluating black's position on the board. This means that even though the computer is controlling white's pieces, it is still trying to make black have the best position. This is due to the piece values and the position tables that you are using.
  The simple fix for this is something like the following:
  def minimax(position, depth, alpha, beta, maximizingPlayer, bestMove = 'h1h3'):
      if depth == 0 or position.is_game_over():
          if (computer == "BLACK"):
              return positionEvaluation(position, piece_values, position_values), bestMove
          else:
              return -1*positionEvaluation(position, piece_values, position_values), bestMove
  
  Where "computer" is some variable you've defined that stores the current player that the computer is evaluating.
  We simply invert the evaluation when white is being controlled and the minimax algorithm gives the expected results.
  
  
  ---
  
  