Compile a chess AI program in Python

Source: Internet
Author: User
In this article, I will introduce how AI works, what each part does, and why it works like that. You can directly read this article, or download the code and read the code. Although it may be helpful to look at the classes on which AI depends in other files, the AI part is all in AI. in the py file, I recently developed a chess program using Python and published the code on Github. This code contains less than 1000 lines, about 20% of which are used to implement AI. In this article, I will introduce how AI works, what each part does, and why it works like that. You can directly read this article, or download the code and read the code. Although it may be helpful to check the classes on which AI depends in other files, all the AI files are in the AI. py file.

Overview of AI

AI goes through three different steps before making a decision. First, he finds all the chess moves allowed by the rules (usually 20-30 at the start, and then drops to several ). Second, it generates a chess and walking tree to determine the optimal decision. Although the size of the tree increases with the depth index, the depth of the tree can be arbitrary. Assuming that each decision has an average of 20 optional chess steps, the depth of 1 corresponds to 20 chess steps, the depth of 2 corresponds to 400 chess steps, and the depth of 3 corresponds to 8000 chess steps. Finally, it traverses the tree and takes the best result of Step x, which is the depth of the selected tree. For the sake of simplicity, I will assume that the depth of the tree is 2.

Generate a chess and walking tree

The game tree is the core of this AI. The class that makes up this tree is MoveNode in the MoveNode. py file. The 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 attributes. The first is move, that is, the chess step it contains. it is a Move class. this is not very important. you only need to know where it is to go, what can I eat. Then there is children, which is also a MoveNode class. The third attribute is parent, so you can know which MoveNode exists on the previous layer. The pointAdvantage attribute is good or bad for AI to determine this step. The depth attribute specifies the number of nodes on the node, that is, the number of nodes on the node. The code for generating the chess and walking 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 is an empty list at the beginning, and then it loads the MoveNode class instance. After the first loop, it is just an array of MoveNode with no parent or child nodes, that is, some root nodes. The second loop traverses moveTree and uses the populateNodeChildren function to add subnodes to each node:

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   for move in legalMoves :    node.children.append(MoveNode(move, [], node))    self.board.makeMove(move)    self.populateNodeChildren(node.children[-1])    self.board.undoLastMove()

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

Then, determine the steps allowed by all rules. However, this is beyond the scope of this article. if you want to see it, the code is on Github. The next if statement checks whether there are any steps that comply with the rules. If none of them exist, they will either die or play chess. If the node is killed, the node. move. checkmate attribute is set to True and return because there are no other steps that can be taken. It is similar to chess, but because neither party has any advantage, we set node. pointAdvantage to 0.

If it is not about to die or play chess, all the chess steps in the legalMoves variable are added to the subnode of the current node as MoveNode, and then the function is called to add their own MoveNode to these subnodes.

When the depth of a node is equal to self. depth (2 in this example), nothing is done. the child nodes of the current node are retained as an empty array.

Traverse tree

Suppose/we have a MoveNode tree, and we need to traverse it to find the best game. This logic is somewhat subtle and takes a little time to understand it (I should use Google more before I understand it as a good algorithm ). So I will try to fully explain it. For example, this is our step tree:

If this AI is stupid and only has depth 1, he will choose to take "elephant" to eat "car", resulting in it scored 5 points and the total advantage is + 7. Then, the next "soldier" will eat its "post", and now the advantage is changed from + 7 to-2, because it did not think of the next step in advance.

Assume that its depth is 2. You will see that it uses "rear" to eat "horse" to lead to score-4, move "rear" to lead to score + 1, "elephant" to eat "car" to lead to score-2. Therefore, he chooses to move it. This is a general technique for designing AI, where you can find more information (minimization of extreme algorithms ).

So when we turn to AI, let it choose the best game, and assume that AI's opponent will choose the most unfavorable game step for AI. The following shows how this is implemented:

def getOptimalPointAdvantageForNode(self, node) :  if node.children:    for child in node.children :      child.pointAdvantage = self.getOptimalPointAdvantageForNode(child)     #If the depth is pisible by 2, it's a move for the AI's side, so return max    if node.children[0].depth % 2 == 1 :      return(max(node.children).pointAdvantage)    else :      return(min(node.children).pointAdvantage)  else :    return node.pointAdvantage

This is also a recursive function, so it is difficult to see at a glance what it is doing. There are two cases: the current node has a subnode or no subnode. Assume that the chess and walking tree is exactly like the one shown in the preceding figure (in reality, there are more nodes on each branch ).

In the first case, the current node has a subnode. Take the first step as an example. Q eats N. The depth of its sub-nodes is 2, so the remainder of Division 2 is not 1. This means that the child node contains the opponent's step, so the minimum number of steps is returned (assuming that the opponent will go out of the game which is the most unfavorable to AI ).

The subnode of the node does not have its own node, because we assume the depth is 2. Therefore, they actually have scores (-4 and + 5 ). The smallest of them is-4, so the first step is to take N for Q, and the score is-4.

The other two steps also Repeat this step. the score for moving "back" is + 1, and the score for "elephant" to eat "car" is-2.

Select the best chess step

The most difficult part has been completed, and now the AI is to choose from the top score.

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 for node in bestMoveNodes]

There are three cases. If the variable bestMoveNodes is empty, the value of moveNode is added to this list. If the value of moveNode is higher than the first element of bestMoveNodes, clear the list and add the moveNode. If the value of moveNode is the same, add it to the list.

The last step is to randomly select one from the best chess step (it is terrible for AI to be predicted)

bestMoves = self.bestMovesWithMoveTree(moveTree)randomBestMove = random.choice(bestMoves)

This is all the content. AI generates a tree, fills it with subnodes to any depth, traverses the tree, finds the score of each chess step, and then selects the best random. There are a variety of optimizations, such as pruning, razor, and static search, but I hope this article will explain how the basic brute-force algorithm chess AI works.

This article is translated by Bole online-Xu Shihao 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.