Write a chess AI program in Python _python

Source: Internet
Author: User
Tags in python

I recently made a chess program with Python and posted the code on the GitHub. This code is less than 1000 lines, and about 20% is used to implement AI. In this article I will explain how the AI works, what each part does, and why it works like that. You can read through this article directly, or download the code, while reading the code. Although it may be helpful to look at the AI-dependent classes in other files, the AI part is all in the ai.py file.

General statement of the AI section

AI passes three different steps before making a decision. First, he found all the rules allowed for the chess steps (usually at the start of the 20-30, and then down to several). Second, it generates a backgammon tree to determine the best decision later. Although the size of the tree grows with the depth index, the depth of the tree can be arbitrary. Assuming that each decision has an average of 20 optional chess steps, that the depth for 1 corresponds 20 chess steps, the depth for 2 corresponds 400 chess steps, the depth for 3 corresponds 8000 chess steps. Finally, it traverses the tree, takes the x step to the best of the chess step, and X is the depth of the tree we choose. For the sake of simplicity, I would assume that the tree is deeply 2.

Build a chess step tree

The Chess step tree is the core of this AI. The class that makes up this tree is the MoveNode in the movenode.py file. His initialization method is as follows:

def __init__ (self, move, children, parent):
  self.move = move
  Self.children = children self.parent
  = parent
  pointadvantage = None
  depth = 1

This class has five properties. The first is move, which is the chess step that it contains, it is a moving class, it is not very important to know that it is a chess step to tell a screwdriver where to go, what to eat, and so on. Then there is the children, which is also a MoveNode class. The third attribute is parent, so it is possible to know what MoveNode are on the previous layer. The Pointadvantage attribute is used by AI to determine whether this move is good or bad. The Depth property indicates how many nodes the node is on at the first level. The code to generate the backgammon tree is as follows:

def generatemovetree (self):
  MoveTree = [] for move in
  Self.board.getAllMovesLegal (self.side):
    Movetree.append (MoveNode (move, [], None)) for
 
  node in MoveTree:
    self.board.makeMove (node.move)
    Self.populatenodechildren (node)
    self.board.undoLastMove () return
  MoveTree

The variable MoveTree first is an empty list, and then it loads an instance of the MoveNode class. After the first loop, it's just an array of movenode with no parent nodes or subnodes, which is the root node. The second loop iterates through the MoveTree, adding child nodes to each node with the Populatenodechildren function:

def populatenodechildren (Self, node):
  node.pointadvantage = Self.board.getPointAdvantageOfSide (self.side)
  Node.depth = node.getdepth ()
  if node.depth = = self.depth: return
 
  side = Self.board.currentSide
 
  Legalmoves = Self.board.getAllMovesLegal (side)
  if not legalmoves:
    if Self.board.isCheckmate ():
      Node.move.checkmate = True
      return
    elif self.board.isStalemate ():
      node.move.stalemate = True
      Node.pointadvantage = 0
      return
 
  to move in legalmoves:
    node.children.append (MoveNode (move, [], node)) C17/>self.board.makemove (move)
    Self.populatenodechildren (node.children[-1])
    Self.board.undoLastMove ( )

This function is recursive, and it's a bit difficult to express with images. A MoveNode object was passed to it at first. This MoveNode object will have a depth of 1 because it does not have a parent node. Let's say the AI is set to a depth of 2. Therefore, the first node to pass to this function will skip the initial if statement.

Then, decide on the chess steps allowed by all the rules. But this is beyond the scope of this article, if you want to see the code is on the GitHub. The next if statement checks to see if there is a rule-compliant chess step. If there is no one, it will either be dead or draw. If it is to be dead, the Node.move.checkmate property is set to true and return because there is no other chess step to go. Draw is similar, but since neither side has an advantage, we set the Node.pointadvantage to 0.

If it is not dead or draw, then all the moves in the legalmoves variable are added as MoveNode in the child nodes of the current node, and the function is called to add their own movenode to these subnodes.

When the node's depth equals self.depth (this example is 2), nothing is done, and the child nodes of the current node are left as empty arrays.

Traverse Tree

Assuming/We have a movenode tree, we need to traverse him to find the best chess step. This logic is a bit tricky, and it takes a little time to figure it out (I should use Google more when I know it's a good algorithm). So I'll explain it as fully as I can. Let's say this is our backgammon tree:

If this AI is stupid, only depth 1, he will choose to take "like" to eat "car", resulting in it to get 5 points and the total advantage of +7. Then the next "soldier" will eat its "after", now the advantage from +7 to 2, because it did not think ahead of the next step.

On the assumption that its depth is 2. will see it with "after" eating "horse" leading to a score of 4, moving "after" leading to a score of +1, "like" eating "car" leading to a score-2. So he chose to move after. This is a common technique for designing AI, where you can find more information (Minimax algorithms).

So when we turn to AI, let it choose the best move, and assume that the AI opponent will choose the most disadvantageous to the AI's chess steps. Here's how this is achieved:

def getoptimalpointadvantagefornode (Self, node):
  if Node.children: For child in
    Node.children:
      Child.pointadvantage = Self.getoptimalpointadvantagefornode (child)
 
    #If The depth was divisible by 2, it's a move for th E AI ' s side, so return max
    if node.children[0].depth% 2 = 1: Return
      (Max (Node.children). Pointadvantage)
    Els E: Return
      (min (node.children). Pointadvantage)
  else: return
    node.pointadvantage

This is also a recursive function, so it's hard to see what it's doing at a glance. There are two scenarios: the current node has child nodes or no child nodes. Let's say the backgammon tree is just the way it is in the previous picture (there will be more nodes on each branch in practice).

In the first case, the current node has child nodes. Take the first step for example, Q eats n. It has a child node depth of 2, so 2 is not 1 except 2. This means that the child node contains one move of the opponent, so the minimum number of steps is returned (assuming the opponent will step out of the most unfavorable move to the AI).

The child nodes of the node will not have their own nodes because we assume that the depth is 2. As a result, they will have their true score (-4 and +5). The smallest of them is-4, so the first step, Q eat N, is given a score of 4.

The other two steps also repeat this step, moving the "after" score to +1, "like" to eat "car" score to 2.

Choose the best chess step

The hardest part is done, and now the AI has to do is choose from the highest score in the chess step.

def bestmoveswithmovetree (self, MoveTree):
  bestmovenodes = [] for
  MoveNode in MoveTree:
    Movenode.pointadvantage = Self.getoptimalpointadvantagefornode (movenode)
    if not bestmovenodes:
      Bestmovenodes.append (MoveNode)
    elif movenode > Bestmovenodes[0]:
      bestmovenodes = []
      Bestmovenodes.append (MoveNode)
    elif MoveNode = = Bestmovenodes[0]:
      bestmovenodes.append (MoveNode)
 
  return [Node.move to node in Bestmovenodes]

There are three kinds of situations at this time. If the variable bestmovenodes is empty, then the value of the MoveNode is added to the list. If the value of the MoveNode is higher than the first element of bestmovenodes, empty the list and add the MoveNode. If the value of the MoveNode is the same, add it to the list.

The final step is to select one randomly from the best chess step (AI can be predicted to be very bad)

Bestmoves = Self.bestmoveswithmovetree (movetree)
randombestmove = Random.choice (bestmoves)

That's all the content. AI generates a tree, fills it with child nodes to any depth, traverses the tree to find the score of each chess step, and then randomly chooses the best. There are a variety of places to optimize, pruning, razors, stationary search, and so on, but hopefully this article is a good way to explain how the basic brute force algorithm of the chess AI works.

This article by Bole online - Xu Shihao translation from Mbuffett.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.