# [artificial intelligence] utilization α-β Search the game tree algorithm to write a word chess game (QDU)

Posted by WhiteCube on Thu, 06 Jan 2022 15:42:13 +0100

## Experimental purpose

Understand and master the heuristic search process of game tree, and be able to realize simple game with selected programming language.

1. Learning minimax search and α − β \alpha-\beta α − β prune.
2. Use the learned algorithm to realize one word chess

## Experimental principle

### Rules of the game

"One character chess" game (also known as "Sanzi chess" or "Jingzi chess") is a very classic puzzle game. The chessboard of "tic tac toe" is very simple. It is a 3 × The grid of 3 is very similar to the word "well" in Chinese characters, so it is named "well chess" The rules of the tic tac toe game are very similar to those of Gobang. The rule of Gobang is that one party will win when the five pieces are connected into a line first "Tic tac toe" is a game in which one side wins when three sons form a line The age of tic tac toe chess is estimated to be unknown. Westerners believe that it was invented by the ancient Romans; But we Chinese believe that since we have all invented go and Gobang, it is natural to invent a backgammon. These are purely verbal disputes, not to mention for the time being.

### Minimax analysis

There are nine spaces. MAX and MIN play chess. Whoever takes the turn to play chess will put his own chess piece on the space. Whoever makes his chess pieces form "three pieces into a line" (the same row, column or diagonal are all someone's chess pieces) will win.

MAX is represented by a circle and MIN is represented by a cross. For example, in the following figure, MAX wins the chess game:

The valuation function is defined as follows:

Set the chess game as P P P. The valuation function is e ( P ) e(P) e(P).

1. if P P P is not the winning position for either party, then $e § = e (the total number of complete rows, columns or diagonals that are still empty for MAX) - E (the total number of complete rows, columns or diagonals that are still empty for MIN)$
2. if P P P is the chess game that MAX must win, then e ( P ) ＝ + ∞ e(P)＝+∞ e(P) = + ∞ (actually assigned 60)
3. if P P P is the chess game that B must win, then e ( P ) ＝ − ∞ e(P)＝-∞ e(P) = − ∞ (actually assigned - 20)

such as P P P as shown below, then e ( P ) = 5 − 4 = 1 e(P)=5-4=1 e(P)=5−4=1

It should be noted that, + ∞ +∞ +∞ assignment 60 60 60， − ∞ -∞ − ∞ assignment − 20 -20 − 20 is because if the machine wins, no matter whether the player will win in the next step, he will take this step and win the chess.

### α-β Pruning algorithm

The above minimax analysis method is actually a game tree, and then calculate its backward value, so that the efficiency of minimax analysis method is low. Based on the minimax analysis method α − β \alpha-\beta α − β Pruning technology.

α − β \alpha-\beta α − β The basic idea or algorithm of pruning technology is to calculate and evaluate the backward value of each node while generating the game tree, and stop expanding those unnecessary sub nodes in time according to the evaluated backward value range, that is, it is equivalent to cutting off some branches on the game tree, so as to save machine overhead and improve search efficiency.

The specific pruning methods are as follows:

1. For a and node MIN, if the supremum of its backward value can be estimated β \beta β， And this β \beta β The infimum of the estimated backward value of the parent node (must be or node) whose value is not greater than MIN α \alpha α， Namely α ≥ β α≥β α ≥ β， Then there is no need to expand the other child nodes of the MIN node (because the estimation of these nodes has no impact on the back extrapolation value of the MIN parent node). This process is called α α α prune.
2. For a node or MAX, if the infimum of its backstepping value can be estimated α α α， And this α α α The supremum of the estimated backward value of the parent node (must be the same as the node) whose value is not less than MAX β β β， Namely α ≥ β α≥β α ≥ β， Then there is no need to expand the other child nodes of the MAX node (because the estimation of these nodes has no impact on the back value of the MAX parent node). This process is called β β β prune.

From the algorithm:

1. MAX node (including start node) α α α Value never decreases
2. Of MIN node (including starting node) β β β Value never increases

During the search, α α α and β β β The values are calculated as follows:

1. Of a MAX node α α α Value is equal to the current maximum final backstepping value of its successor node
2. Of a MIN node β β β Value is equal to the current minimum final backstepping value of its successor node

Take an example to understand the process:

Detailed steps:

• The tree should be processed from the penultimate layer, and all processing should be "left to right"
• The value of MIN layer node is the minimum value of its child nodes. If any child nodes are cut, the minimum value of the remaining child nodes will be taken
• The value of the MAX layer node is the maximum value of its child nodes. If any child nodes are cut, the maximum value of the remaining child nodes will be taken
• Pruning can only be carried out if the following criteria are met
• Assuming that there is node x, the value range of node x is determined according to a child node of node X
• Node x also has parent nodes (including direct parent nodes and indirect parent nodes). When you determine the value of node x, these parent nodes may or may not have a value range
• If these parent nodes already have a value range, if there is no intersection between the aggregate value range of node X and the value range of the parent node (directly or indirectly) of node x, or there is only one element in the intersection, other child nodes of node x will be cut off. Other child nodes refer to all child nodes of node x except the node from which the range of node x is derived
• Pruning ends when the values of all nodes that have not been pruned are determined

### Design of winning or losing judgment algorithm

Because only the currently placed pieces will win or lose each time, the winning or losing algorithm only needs to scan from the current point to judge whether three pieces have been formed. For the eight directions of this sub, judge whether it has formed three sub. If yes, it means that one party wins. If not, continue to search until one party wins or search the whole chessboard.

## experimental design

Two versions are implemented, one is the C + + version and the other is the Java version.

The C + + version has poor visualization and less functions, but α − β \alpha-\beta α − β The minimax algorithm of pruning is relatively clear, which is convenient for learning and reviewing the algorithm;

The Java version wraps a visual shell on the basis of implementing the algorithm, making the whole program more product-oriented.

The following core algorithms are shared by the two versions, and the explanation of program design does not include the introduction to the C + + version.

### Core algorithm

PvP:

Players and players do not need any algorithm in wartime. They only need to monitor each click on the chessboard. When one side wins or the chessboard is full (draw), the game ends.

PvE:

When the player fights with the computer, the player's position still does not need algorithm support, but only needs to be controlled by the player.

For computers, the minimax analysis method is realized by recursion to select the best placement. This algorithm not only needs to define the function of the simulation computer to find the best place, but also needs to define the function of the simulation player to find the best place, and simulate the process of taking turns by calling two functions in turn (recursion). For a certain state encountered by the computer, that is, when you want to select the placement position, recursion is carried out. After finding the best placement position, recursion is returned.

When to return recursively is the pruning condition. In this algorithm, the number of pruning layers is specified as two. When reaching the third layer, if it is impossible to lock the chess game (lock the chess game: you can determine the winner or draw), call the evaluation function for the recursive node to evaluate. One of the information returned recursively is the evaluation in this state, The valuation function is defined as: the difference between the number of the last three sub lines of the chess pieces filled with the computer side and the number of the last three sub lines of the chess pieces filled with the player side.

The function of the computer in the function of the computer drop is to find the function of the best falling place, that is, to simulate the rotation of the falling ball to get the best position.

In the function of the computer looking for the best position, first judge whether a position can be found to make the computer win directly. If such a position exists, select this position, and the valuation of this state is positive infinite, indicating that it is bound to win; If it does not exist, judge the number of pruning layers. If it reaches the number of pruning layers, directly call the valuation function to calculate the value of the state; If the number of pruning layers is not reached, try to drop in a space, that is, mark a position on the chessboard as a piece of the computer, and then call the function of the player to find the best place to drop, that is, the computer drop is completed, and it is the simulated player's turn to drop. The player will also call the computer to find the function at the best location to realize recursion, and pass back the evaluation of the child node (child state) to the upper recursive layer by reference, and update the evaluation of the current node through the evaluation of the child node. No matter whether the current node's valuation is updated or not, backtracking is required, that is, because it is a simulated drop, the state needs to be restored to the previous state after recursive return, and the drop position needs to be empty.

If you try each method every time the simulation looks for the falling position, that is, try to fall on each empty position. The recursive process is understood as a tree building process. It can be imagined that such tree building will lead to very high time complexity (in fact, although the time complexity is very high, the time-consuming is acceptable because there are at most nine squares in tic tac toe chess) α − β \alpha-\beta α − β Prune to reduce unnecessary expenses.

Every time we recurse, we need to α \alpha α Value or β \beta β Value is passed in, so that when the next layer (i.e. the opponent) selects the drop position, it can prune to reduce the drop position to be judged.

The pruning strategy of MAX node is based on the incoming β \beta β Value, this β \beta β The value is the last state of the current MAX node, that is, the maximum value of the MIN node, because it is necessary to ensure that there is an intersection between the range of the child node (MAX) and the ancestor node (MIN), that is, to ensure that the state estimation of the MAX node must be less than β \beta β， such ( − ∞ , β ] (-∞,\beta] (−∞, β] And [ α , + ∞ ) [\alpha,+∞) [ α,+ ∞) before there is intersection. If there is already disjoint, there is no need to judge the latter, that is, pruning.

The pruning strategy of the MIN node is similar to that of the MAX node and will not be repeated. In this way, not only the best location of the computer is obtained, but also the time complexity is minimized.

### Pruning process

Complete state of the first three layers:

through α − β \alpha-\beta α − β State of the first three layers of pruning:

Pruning process in the program:

Pruning involves at least three layers of nodes. The implementation of pruning function is described below with three layers as an example.

The main purpose is to understand the relationship between the parameters of each layer. The top-level MAX node needs the sub node (MIN node) to transfer its parameters α \alpha α Value is used for pruning, that is, if the current MIN node β \beta β Value less than or equal to α \alpha α Pruning occurs, and there is no need to calculate the subsequent nodes in the child nodes of the MIN node; The valuation of each MAX node provided by the MIN node's child MAX node to the MIN node is not only used to update the MIN node's value β \beta β Value, also used for pruning; When the MIN node is completely updated β \beta β Value (i.e. the valuation of MIN node is determined) needs to be passed to the upper layer to update the value of MIN's parent node and MAX node α \alpha α Value.

### Programming

Expected flow chart:

Flow chart of final realization:

The final implementation uses the flow shown in the second flow chart to control the whole program.

#### Initial interface

The initial interface consists of four parts: logo "tic tac toe", "PvP" player vs. player options, "PvE" player vs. computer options, "exit".

#### Game interface

The game interface mainly consists of two parts, a scoreboard at the top and a chessboard at the bottom. The scoreboard records the scores of P1 (or P) and P2 (E) respectively.

In order to easily distinguish whether the current sub square is P1 (or P) or P2 (or E), the identification of the sub square of the scoreboard is highlighted.

Rules of the game:

1. When one side surpasses the other, the winner will add points, and the draw will not add points.
2. In PvP mode, P1 drop is X X 10. P2 drop is O O O； In PvE mode, the computer's position is X X 10. The player's fall is O O O.
3. Both parties take turns to start first and then start. P1 takes priority in PvP mode and computer takes priority in PvE mode.

PvP interface:

PvE interface:

### Aspects to be optimized

1. The pop-up dialog box is transparent. In order to achieve a better user experience, when the game is over, the pop-up opaque result prompt box will block the chessboard information. When users need to observe the game, they need to drag the prompt box. Therefore, it is of great significance to make the prompt box transparent, but it can not be realized in the end.

2. The default number of pruning layers in this algorithm is 2. An error will occur when the number of pruning layers is set to 3.

3. You can also add "prompt" function in PvP mode.

## Running screenshot

PvP:

The following status indicates that it is P1's turn to drop.

The following figure shows that P2 is turned to P1 after dropping the sub.

The following figure shows that P1 wins, and the pop-up prompt box shows that P1 wins.

When you click OK, the scoreboard will add one to the winner's score. At the same time, it can be seen that P1 took the lead in the last round, so P2 took the lead in this round.

PvE:

In PvE mode, the computer is given priority, and then take turns first and then.

For better user experience, the action of the computer will be delayed for 1s after calculating the optimal position of the computer, imitating the process of computer thinking.

In a draw, neither side will add points. At the same time, it will be the player's priority in the next round.

## Appendix 1 (C + + code)

C + + code simply α − β \alpha-\beta α − β The minimax analysis method of pruning is realized, and the output window is used for visualization. Before writing Java code, you are familiar with the core algorithm through the C + + program.

#include<bits/stdc++.h>
using namespace std;

int chessBoard[3][3];

int isWin();
int evaluate();
bool gameOver();
void playGame();
void printChessBoard();

void computerPlace();
void humanPlace();
bool computerImmidiateWin(int&);
bool humanImmidiateWin(int&);
void findComputerBestMove(int&, int&, int, int, int);
void findHumanBestMove(int&, int&, int, int, int);

void printChessBoard() {
cout << "ChessBoard:" << endl << endl;
for(int i = 0;i < 9;i ++) {
if(chessBoard[i/3][i%3] == 1)  cout << "○";
if(chessBoard[i/3][i%3] == 0)  cout << "□";
if(chessBoard[i/3][i%3] == -1) cout << "×";
if(i%3 == 2) cout << endl;
}
}

int isWin() {
for(int i = 0;i < 3;i ++) {
if(chessBoard[i][0] != 0 && chessBoard[i][0] == chessBoard[i][1] && chessBoard[i][1] == chessBoard[i][2]) return chessBoard[i][0];
if(chessBoard[0][i] != 0 && chessBoard[0][i] == chessBoard[1][i] && chessBoard[1][i] == chessBoard[2][i]) return chessBoard[0][i];
}
if(chessBoard[0][0] != 0 && chessBoard[0][0] == chessBoard[1][1] && chessBoard[1][1] == chessBoard[2][2]) return chessBoard[0][0];
if(chessBoard[2][0] != 0 && chessBoard[2][0] == chessBoard[1][1] && chessBoard[1][1] == chessBoard[0][2]) return chessBoard[2][0];
return 0; // 0 does not represent a draw
}

bool computerImmidiateWin(int& bestMove) {
for(int i = 0;i < 9;i ++) {
if(chessBoard[i/3][i%3]) continue;
chessBoard[i/3][i%3] = 1;
bool win = (isWin() == 1);
chessBoard[i/3][i%3] = 0;
if(win) {
bestMove = i;
return true;
}
}
return false;
}

bool humanImmidiateWin(int& bestMove) {
for(int i = 0;i < 9;i ++) {
if(chessBoard[i/3][i%3]) continue;
chessBoard[i/3][i%3] = -1;
bool win = (isWin() == -1);
chessBoard[i/3][i%3] = 0;
if(win) {
bestMove = i;
return true;
}
}
return false;
}

int evaluate() {
int tmp[3][3], computerValue = 0, playerValue = 0;
for(int i = 0;i < 9;i ++)
tmp[i/3][i%3] = (!chessBoard[i/3][i%3]?1:chessBoard[i/3][i%3]);
for(int i = 0;i < 3;i ++)
computerValue += (tmp[i][0]+tmp[i][1]+tmp[i][2])/3 + (tmp[0][i]+tmp[1][i]+tmp[2][i])/3;
computerValue += (tmp[0][0] + tmp[1][1] + tmp[2][2])/3 + (tmp[2][0]+tmp[1][1]+tmp[0][2])/3;

for(int i = 0;i < 9;i ++)
tmp[i/3][i%3] = (!chessBoard[i/3][i%3]?-1:chessBoard[i/3][i%3]);
for(int i = 0;i < 3;i ++)
playerValue += (tmp[i][0]+tmp[i][1]+tmp[i][2])/3 + (tmp[0][i]+tmp[1][i]+tmp[2][i])/3;
playerValue += (tmp[0][0] + tmp[1][1] + tmp[2][2])/3 + (tmp[2][0]+tmp[1][1]+tmp[0][2])/3;

return computerValue + playerValue;
}

void findComputerBestMove(int& bestMove, int& value, int alpha, int beta, int deep) {
if(computerImmidiateWin(bestMove)) {
value = 60;
return ;
}
if(deep == 3) {
value = evaluate();
return ;
}
value = alpha;
for(int i = 0;i < 9 && value < beta;i ++) {
if(chessBoard[i/3][i%3]) continue;
chessBoard[i/3][i%3] = 1;
int tmp = -1, response = -1;
findHumanBestMove(tmp, response, value, beta, deep+1);
chessBoard[i/3][i%3] = 0;
if(response > value) value = response, bestMove = i;
}
}

void findHumanBestMove(int& bestMove, int& value, int alpha, int beta, int deep) {
if(humanImmidiateWin(bestMove)) {
value = -20;
return ;
}
if(deep == 3) {
value = evaluate();
return ;
}
value = beta;
for(int i = 0;i < 9 && value > alpha;i ++) {
if(chessBoard[i/3][i%3]) continue;
chessBoard[i/3][i%3] = -1;
int tmp = -1, response = -1;
findComputerBestMove(tmp, response, alpha, value, deep+1);
chessBoard[i/3][i%3] = 0;
if(response < value) value = response, bestMove = i;
}
}

void computerPlace() {
int bestMove = 0, value = 0;
const int deep = 1, alpha = -20, beta = 60;
findComputerBestMove(bestMove, value, alpha, beta, deep);
chessBoard[bestMove/3][bestMove%3] = 1;
}

void humanPlace() {
int x, y;
while(true) {
cout << "It is your turn, please input where you want :" << endl;
cout << "for example: 2 2 mean you want to add position 2 row,2 col:" << endl;
cin >> x >> y;
if (x < 0 || x > 3 || y < 0 || y > 3 || chessBoard[x-1][y-1] != 0) cout << "error, please input again:" << endl;
else break;
}
chessBoard[x-1][y-1] = -1;
}

bool gameOver() {
if(isWin()) return true;
int i;
for(i = 0;i < 9;i ++)
if(chessBoard[i/3][i%3] == 0) break;
return i == 9;
}

void playGame() {
int computerFirst;

memset(chessBoard, 0, sizeof chessBoard);
printChessBoard();

while(true) {
cout << "\nWho take the first step:\n1)Player.  2)Computer.[ ]\b\b";
cin >> computerFirst;
if(computerFirst != 1 && computerFirst != 2) getchar();
else break;
}

if(computerFirst == 2) computerPlace();
while(true) {
printChessBoard();
if(gameOver()) break;
humanPlace();
printChessBoard();
if(gameOver()) break;
computerPlace();
}
}

int main()
{
int option;
while(1) {
playGame();
if(isWin() == 0) cout << "\nDraw" << endl;
else if(isWin() == 1) cout << "\nComputer Win!\n" << endl;
else cout << "\nPlayer Win!\n" << endl;

cout << "\nTry Again?\n1)Yeah.\t2)Exit.[ ]\b\b";
cin >> option;
if(option != 1 && option != 2) getchar();
if(option == 2) break;
}
}


## Appendix 2 (Java code)

The three public classes are the Main class of program entry, the Window class of interface design and the TicTacToe class of tic tac toe.

### Main.java

The TicTacToe object is created. In the constructor of the TicTacToe class, the function that implements all the functions is called.

package exp2;

/**
* @author LJR
* @create 2021-11-14 21:28
*/
public class Main {
public static void main(String[] args) {
new TicTacToe();
}
}


### Window.java

The interface design class also binds the click event of the button in the initial interface.

package exp2;

import javax.swing.*;
import java.awt.*;

/**
* @author LJR
* @create 2021-11-14 21:27
*/
public class Window extends JFrame {
private static final long serialVersionUID = -6740703588976621222L;

public JButton[] nineBoard = new JButton[9]; // The chessboard is full of buttons
public JButton[] buttons; // Convenient binding monitoring in tic tac toe chess class
public static boolean gameStart_PvE = false; // Does the game start
public static boolean gameStart_PvP = false; // Does the game start
public static int option = 0; // 1: PvP or 2: PvE

public JTextField[] sorceBoard_pve; // scoreboard
public JTextField[] sorceBoard_pvp;

public Window() {
super("utilizeα-βSearch the game tree algorithm to write a word chess game");
Container c = this.getContentPane();

c.setBackground(Color.white);

this.setSize(300, 330);
this.setUndecorated(false);
this.setLocationRelativeTo(null);
this.setVisible(true);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

public JTextField[] generateSorceBoard(int PorE) { // Generate scoreboard
String text[][] = new String[][]{{"P1", "0", ":", "0", "P2"}, {"P", "0", ":", "0", "E"}}; // string 0 : PvP   string 1 : pve
int bound_x[] = {65, 95, 125, 155, 185};
int align[] = {JTextField.LEFT, JTextField.RIGHT, JTextField.CENTER, JTextField.LEFT, JTextField.RIGHT};
int size_w = 30, size_h = 30, font_size = 20, font_flag = Font.PLAIN;
String font_style = "Forte";
JTextField[] jfs = new JTextField[5];

for (int i = 0; i < 5; i++) {
jfs[i] = new JTextField(text[PorE][i]);
jfs[i].setBounds(bound_x[i], 0, size_w, size_h);
jfs[i].setFont(new Font(font_style, font_flag, font_size));
jfs[i].setHorizontalAlignment(align[i]);

jfs[i].setForeground(new Color(255, 255, 255));
jfs[i].setOpaque(false);
jfs[i].setFocusable(false);
jfs[i].setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
jfs[i].setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); // Set borderless
}

return jfs;
}

public JButton[] generateButtons() { // Generate {"PvP", "PvE", "exit"} buttons
String[] text = new String[]{"PvP", "PvE", "exit"};
int[][] pos = new int[][]{{40, 140}, {160, 140}, {110, 210}}, size = new int[][]{{76, 35}, {76, 35}, {60, 35}};
JButton[] jbs = new JButton[3];
int font_size = 30, font_flag = Font.PLAIN, color_R = 255, color_G = 131, color_B = 62;
String font_style = "Forte";

for (int i = 0; i < 3; i++) {
jbs[i] = new JButton(text[i]);
jbs[i].setBounds(pos[i][0], pos[i][1], size[i][0], size[i][1]);
jbs[i].setFont(new Font(font_style, font_flag, font_size));
jbs[i].setForeground(new Color(color_R, color_G, color_B));
jbs[i].setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); // Mouse move in button smaller hand
jbs[i].setOpaque(false); // transparent
jbs[i].setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
jbs[i].setContentAreaFilled(false);
jbs[i].setFocusPainted(false);
jbs[i].setRolloverEnabled(true);
}

return jbs;
}

public JPanel getJButton() {

JPanel jP = new JPanel();
jP.setOpaque(false);
jP.setLayout(null);// Set an empty layout, that is, an absolute layout

// Game interface
JPanel chessboard_panel = new JPanel();
chessboard_panel.setLayout(null);
chessboard_panel.setBounds(0, 0, 300, 330);
chessboard_panel.setBackground(new Color(45, 45, 45));
JLabel jl = new JLabel();
Icon icon1 = new ImageIcon("src/board_2.jpg"); // Attention path
jl.setIcon(icon1);
jl.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
jl.setBounds(0, 30, icon1.getIconWidth(), icon1.getIconHeight());
chessboard_panel.setVisible(false);
// scoreboard
sorceBoard_pvp = generateSorceBoard(0);
sorceBoard_pve = generateSorceBoard(1);

// Start interface
JPanel start_panel = new JPanel();
start_panel.setLayout(null); // The panel must be set to an empty layout
start_panel.setBackground(new Color(50, 50, 50));
start_panel.setBounds(0, 0, 300, 330);
// logo
JLabel logo = new JLabel();
Icon icon = new ImageIcon("src/TicTacToe_1.png"); // Attention path
logo.setIcon(icon);
logo.setBounds(10, 50, icon.getIconWidth(), icon.getIconHeight());
buttons = generateButtons();
for (int i = 0; i < 3; i++) start_panel.add(buttons[i]);
buttons[0].addActionListener((actionEvent -> { // pvp / / bind and click listen
option = 1; // pvp
for (int i = 0; i < 5; i++) chessboard_panel.add(sorceBoard_pvp[i]);
chessboard_panel.setVisible(true);
start_panel.setVisible(false);

startGame_PvP(); // PvP start

}));
option = 2; // pve
for (int i = 0; i < 5; i++) chessboard_panel.add(sorceBoard_pve[i]);
chessboard_panel.setVisible(true);
start_panel.setVisible(false);
startGame_PvE(); // The game begins
}));
System.exit(0);
}));
// Each grid is a button
for (int i = 0; i < 9; i++) {
nineBoard[i] = new JButton();
nineBoard[i].setBounds(i % 3 * 72 + 37, i / 3 * 72 + 27, 65, 65);
nineBoard[i].setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); // Mouse move in button smaller hand
nineBoard[i].setOpaque(false); // Transparent, with or without?
nineBoard[i].setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); // Temporary notes
nineBoard[i].setContentAreaFilled(false);
nineBoard[i].setFocusPainted(false);
nineBoard[i].setRolloverEnabled(true);
}

return jP;
}

public void startGame_PvE() {
//        System.out.println("PvE");
Window.gameStart_PvE = true; // Only when the user clicks PvE can the content in the process be executed
}

public void startGame_PvP() {
//        System.out.println("PvP");
Window.gameStart_PvP = true;

}
}


### TicTacToe.java

Core class, which contains various internal classes and threads. This class implements α − β \alpha-\beta α − β Pruning minimax analysis algorithm, while monitoring some of the game interface drop click and other events.

package exp2;

import javax.swing.*;
import java.awt.*;

/**
* @author LJR
* @create 2021-11-15 15:50
*/
public class TicTacToe {

Window window; // Use members from

// lock
public Object lock = new Object();

// checkerboard
int chessBoard[][] = new int[3][3];

// Variable, PvE
boolean newgame = true; // Start a new game
int cnt = 0; // Parity controls whether youCanClick is true or false. I don't know why it is invalid to object to the assignment of youCanClick
int nowStatus = 0; // Is it PvE or PvP now
int beclickedButton = -1; // The number of the clicked button
boolean humanPlaceOver = false; // Are you waiting for the player to click and drop
boolean youCanClick = false; // Controls whether the player clicks on the grid effectively, that is, if false, the player cannot fall
boolean buttonsAlive[] = new boolean[9]; // Whether the button can be clicked and whether it is valid, that is, if false, it has been dropped
// It can be seen that if you are allowed to drop a child somewhere, you must first ensure that youCanClick=true and buttonsAlive[i]=true

// Variable, PvP
boolean inP1Turn = true; // Round at P1
boolean P1PlaceOver = false;
boolean P2PlaceOver = false;

// PvE
// PvP

// Constructor, the entrance to everything
public TicTacToe() {
for (int i = 0; i < 9; i++) chessBoard[i / 3][i % 3] = 0; // Initialize chessboard
for (int i = 0; i < 9; i++) buttonsAlive[i] = true; // Drop is allowed at any position initially
window = new Window(); // Interface design
thread_option.start(); // Determine whether the PvP or PvE clicked by the user
}

// Internal class, user-defined Integer class. Because the value of the Integer class of Java is final, you must use object to realize functions similar to C + + reference
class MyInteger {
private int value;

public MyInteger() {

}

public MyInteger(int value) {
this.value = value;
}

public void setValue(int value) {
this.value = value;
}

public int getValue() {
return value;
}
}

// Internal class, a thread that changes the color of the scoreboard of the current chess player, so that the chess player can know more clearly who should fall
public void run() {
while (true) {

try {
} catch (InterruptedException exception) {
exception.printStackTrace();
}

if (Window.gameStart_PvP) {
if (inP1Turn) {
window.sorceBoard_pvp[0].setForeground(new Color(255, 131, 62));
window.sorceBoard_pvp[4].setForeground(new Color(255, 255, 255));
} else {
window.sorceBoard_pvp[0].setForeground(new Color(255, 255, 255));
window.sorceBoard_pvp[4].setForeground(new Color(255, 131, 62));
}
}

if (Window.gameStart_PvE) {
if (youCanClick) {
window.sorceBoard_pve[0].setForeground(new Color(255, 131, 62));
window.sorceBoard_pve[4].setForeground(new Color(255, 255, 255));
} else {
window.sorceBoard_pve[0].setForeground(new Color(255, 255, 255));
window.sorceBoard_pve[4].setForeground(new Color(255, 131, 62));
}
}
}
}
}

// Internal class, top-level thread. In this thread, different threads are started according to whether the user clicks PvP or PvE
public void run() {
while (true) {

try {
} catch (InterruptedException exception) {
exception.printStackTrace();
}

if (Window.option == 1) {
nowStatus = 1;
Window.option = 0;
gameStart_PvP();
}
if (Window.option == 2) {
nowStatus = 2;
Window.option = 0;
gameStart_PvE();
}
}
}
}

// Internal class, PvE: the thread that determines the end of the game
public void run() {
while (true) {
try {
} catch (InterruptedException exception) {
exception.printStackTrace();
}

if (isWin() == 0) {
// Several spaces to center text
JOptionPane.showMessageDialog(null, "                    it ends in a draw", "Council results", 1);
//                        System.out.println("draw");
} else if (isWin() == 1) {
JOptionPane.showMessageDialog(null, "                 You failed", "Council results", 1);
window.sorceBoard_pve[3].setText(Integer.parseInt(window.sorceBoard_pve[3].getText()) + 1 + ""); // Computer integral plus one
//                        System.out.println("computer win");
} else {
JOptionPane.showMessageDialog(null, "                 You won", "Council results", 1);
window.sorceBoard_pve[1].setText(Integer.parseInt(window.sorceBoard_pve[1].getText()) + 1 + ""); // Player points plus one
//                        System.out.println("player win");
}

try {
} catch (InterruptedException exception) {
exception.printStackTrace();
}

clearChessBoard(); // Clear the chessboard and initialize some variables to facilitate the beginning of the next game

}
}
}
}

// Internal class, PvE: realize that the computer and players take turns first and then
public void run() {
while (true) {
try {
} catch (InterruptedException exception) {
exception.printStackTrace();
}

if (newgame) {
newgame = false;
youCanClick = (cnt % 2 != 0);
cnt++;
}
}

}
}

// Internal class, PvE: computer thread
public void run() {
while (true) {

if (gameOver()) {
Window.gameStart_PvE = false; // game over
//                    System.out.println("computer gameOver");
return;
}

try { // Not without a little sleep
Thread.sleep(50); // This sleep event can be understood as the refresh time
} catch (InterruptedException e) {
e.printStackTrace();
}
if (Window.gameStart_PvE) {
synchronized (lock) {
if (!youCanClick) { // Computer round
try { // Let the computer sleep more and imitate the tic tac toe game (if the computer is displayed directly after the player falls, the effect is not good)
} catch (InterruptedException e) {
e.printStackTrace();
}

computerPlace(); // Core algorithm

youCanClick = true;
} else {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}

// Internal class, PvE: player thread
public void run() {
while (true) {

if (gameOver()) {
Window.gameStart_PvE = false; // game over
//                    System.out.println("player gameOver");
return;
}

try { // Not without a little sleep
} catch (InterruptedException e) {
e.printStackTrace();
}
if (Window.gameStart_PvE) {
synchronized (lock) {
if (youCanClick) { // Player turns and

humanPlace(); // Wait for the click event to occur

youCanClick = false;
} else {
lock.notify();
}
}
}
}
}
}

// Internal class, PvP: judge whether the game is over
public void run() {
while (true) {
try {
} catch (InterruptedException exception) {
exception.printStackTrace();
}

if (isWin() == 0) {
// Several spaces to center text
JOptionPane.showMessageDialog(null, "                    it ends in a draw", "Council results", 1);
System.out.println("draw");
} else if (isWin() == 1) {
JOptionPane.showMessageDialog(null, "                 P1 Won", "Council results", 1);
window.sorceBoard_pvp[1].setText(Integer.parseInt(window.sorceBoard_pvp[1].getText()) + 1 + ""); // P1 integral plus one
System.out.println("P1 win");
} else {
JOptionPane.showMessageDialog(null, "                 P2 Won", "Council results", 1);
window.sorceBoard_pvp[3].setText(Integer.parseInt(window.sorceBoard_pvp[3].getText()) + 1 + ""); // P2 integral plus one
System.out.println("P2 win");
}

try {
} catch (InterruptedException exception) {
exception.printStackTrace();
}

clearChessBoard(); // Clear the chessboard and initialize some variables to facilitate the beginning of the next game

}
}
}
}

// Internal class, PvP: Player 1 and player 2 take turns first and then hand
public void run() {
while (true) {
try {
} catch (InterruptedException exception) {
exception.printStackTrace();
}

if (newgame) {
newgame = false;
inP1Turn = (cnt % 2 == 0);
cnt++;
System.out.println(inP1Turn);
}
}

}
}

// Internal class, PvP: Player 1 thread
public void run() {
while (true) {

if (gameOver()) {
Window.gameStart_PvP = false; // game over
//                    System.out.println("p1 gameOver");
return;
}

try {
} catch (InterruptedException exception) {
exception.printStackTrace();
}

if (Window.gameStart_PvP) {
synchronized (lock) {
if (inP1Turn) { // Round of P1
P1Place(); // P1 drop
inP1Turn = false;
} else {
try {
lock.wait();
} catch (InterruptedException exception) {
exception.printStackTrace();
}
}
}
}
}
}
}

// Internal class, PvP: Player 2 thread
public void run() {
while (true) {

if (gameOver()) {
Window.gameStart_PvP = false; // game over
//                    System.out.println("p2 gameOver");
return;
}

try {
} catch (InterruptedException exception) {
exception.printStackTrace();
}

if (Window.gameStart_PvP) {
synchronized (lock) {
if (!inP1Turn) { // Round of P2
P2Place(); // P2 drop
inP1Turn = true;
} else {
lock.notify();
}
}
}

}
}
}

// PvE: a top-level function that starts three threads: 1 Thread 2 to judge whether the Bureau ends Thread to change the color of the current player's scoreboard 3 Switch first hand thread
public void gameStart_PvE() {
thread_color.start(); // Whose scoreboard is displayed in orange
}

// PvP: a top-level function that starts three threads: 1 Thread 2 to judge whether the Bureau ends Thread to change the color of the current player's scoreboard 3 Switch first hand thread
public void gameStart_PvP() {
}

// Player 1 drop function
public void P1Place() {
if (!Window.gameStart_PvP) return; // So that the player thread can terminate
//        System.out.println("waiting for P1");
while (!P1PlaceOver) {
try { // Not without a little sleep
} catch (Exception e) {

}
}
chessBoard[beclickedButton / 3][beclickedButton % 3] = 1;
//        System.out.println("P1 place!");

P1PlaceOver = false;
}

// Player 2 drop function
public void P2Place() {
if (!Window.gameStart_PvP) return; // So that the player thread can terminate
//        System.out.println("waiting for P2");
while (!P2PlaceOver) {
try { // Not without a little sleep
} catch (Exception e) {

}
}
chessBoard[beclickedButton / 3][beclickedButton % 3] = -1;
//        System.out.println("P2 place!");

P2PlaceOver = false;
}

// Bind click events for tic tac toe buttons. Since only lambda expressions or constants can be used in listening, you need to bind and listen to each button separately
if (youCanClick && nowStatus == 2 && buttonsAlive[0]) { // Allow players to click
//                System.out.println("effective click 0");
buttonsAlive[0] = false; // Chess pieces already exist in this position (both sides can't play again)
youCanClick = false; // Players can't drop, so the click event has happened, indicating that the player has just finished
beclickedButton = 0; // No. 0 was clicked
humanPlaceOver = true; // Used to control waiting players
window.nineBoard[0].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button to indicate the drop
}

if (nowStatus == 1 && buttonsAlive[0]) { // PvP
//                System.out.println("effective click 0");
buttonsAlive[0] = false; // Chess pieces already exist in this position (both sides can't play again)
beclickedButton = 0; // No. 0 was clicked
if (inP1Turn) {
P1PlaceOver = true; // If P1 is currently dropped, P1 is finished
window.nineBoard[0].setIcon(new ImageIcon("src/X_1.gif")); // Click to modify the background of the button, indicating P1 drop
} else {
P2PlaceOver = true; // If P2 is currently dropped, P2 is finished
window.nineBoard[0].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button, indicating P2 drop
}
}
}));
if (youCanClick && nowStatus == 2 && buttonsAlive[1]) { // Allow players to click
//                System.out.println("effective click 1");
buttonsAlive[1] = false;
youCanClick = false;
beclickedButton = 1;
humanPlaceOver = true;
window.nineBoard[1].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button to indicate the drop
}

if (nowStatus == 1 && buttonsAlive[1]) { // PvP
//                System.out.println("effective click 1");
buttonsAlive[1] = false; // Chess pieces already exist in this position (both sides can't play again)
beclickedButton = 1;
if (inP1Turn) {
P1PlaceOver = true; // If P1 is currently dropped, P1 is finished
window.nineBoard[1].setIcon(new ImageIcon("src/X_1.gif")); // Click to modify the background of the button, indicating P1 drop
} else {
P2PlaceOver = true; // If P2 is currently dropped, P2 is finished
window.nineBoard[1].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button, indicating P2 drop
}
}
}));
if (youCanClick && nowStatus == 2 && buttonsAlive[2]) { // Allow players to click
//                System.out.println("effective click 2");
buttonsAlive[2] = false;
youCanClick = false;
beclickedButton = 2;
humanPlaceOver = true;
window.nineBoard[2].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button to indicate the drop
}

if (nowStatus == 1 && buttonsAlive[2]) { // PvP
//                System.out.println("effective click 2");
buttonsAlive[2] = false; // Chess pieces already exist in this position (both sides can't play again)
beclickedButton = 2;
if (inP1Turn) {
P1PlaceOver = true; // If P1 is currently dropped, P1 is finished
window.nineBoard[2].setIcon(new ImageIcon("src/X_1.gif")); // Click to modify the background of the button, indicating P1 drop
} else {
P2PlaceOver = true; // If P2 is currently dropped, P2 is finished
window.nineBoard[2].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button, indicating P2 drop
}
}
}));
if (youCanClick && nowStatus == 2 && buttonsAlive[3]) { // Allow players to click
//                System.out.println("effective click 3");
buttonsAlive[3] = false;
youCanClick = false;
beclickedButton = 3;
humanPlaceOver = true;
window.nineBoard[3].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button to indicate the drop
}

if (nowStatus == 1 && buttonsAlive[3]) { // PvP
//                System.out.println("effective click 3");
buttonsAlive[3] = false; // Chess pieces already exist in this position (both sides can't play again)
beclickedButton = 3;
if (inP1Turn) {
P1PlaceOver = true; // If P1 is currently dropped, P1 is finished
window.nineBoard[3].setIcon(new ImageIcon("src/X_1.gif")); // Click to modify the background of the button, indicating P1 drop
} else {
P2PlaceOver = true; // If P2 is currently dropped, P2 is finished
window.nineBoard[3].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button, indicating P2 drop
}
}
}));
if (youCanClick && nowStatus == 2 && buttonsAlive[4]) { // Allow players to click
//                System.out.println("effective click 4");
buttonsAlive[4] = false;
youCanClick = false;
beclickedButton = 4;
humanPlaceOver = true;
window.nineBoard[4].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button to indicate the drop
}

if (nowStatus == 1 && buttonsAlive[4]) { // PvP
//                System.out.println("effective click 4");
buttonsAlive[4] = false; // Chess pieces already exist in this position (both sides can't play again)
beclickedButton = 4;
if (inP1Turn) {
P1PlaceOver = true; // If P1 is currently dropped, P1 is finished
window.nineBoard[4].setIcon(new ImageIcon("src/X_1.gif")); // Click to modify the background of the button, indicating P1 drop
} else {
P2PlaceOver = true; // If P2 is currently dropped, P2 is finished
window.nineBoard[4].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button, indicating P2 drop
}
}
}));
if (youCanClick && nowStatus == 2 && buttonsAlive[5]) { // Allow players to click
//                System.out.println("effective click 5");
buttonsAlive[5] = false;
youCanClick = false;
beclickedButton = 5;
humanPlaceOver = true;
window.nineBoard[5].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button to indicate the drop
}

if (nowStatus == 1 && buttonsAlive[5]) { // PvP
//                System.out.println("effective click 5");
buttonsAlive[5] = false; // Chess pieces already exist in this position (both sides can't play again)
beclickedButton = 5;
if (inP1Turn) {
P1PlaceOver = true; // If P1 is currently dropped, P1 is finished
window.nineBoard[5].setIcon(new ImageIcon("src/X_1.gif")); // Click to modify the background of the button, indicating P1 drop
} else {
P2PlaceOver = true; // If P2 is currently dropped, P2 is finished
window.nineBoard[5].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button, indicating P2 drop
}
}
}));
if (youCanClick && nowStatus == 2 && buttonsAlive[6]) { // Allow players to click
//                System.out.println("effective click 6");
buttonsAlive[6] = false;
youCanClick = false;
beclickedButton = 6;
humanPlaceOver = true;
window.nineBoard[6].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button to indicate the drop
}

if (nowStatus == 1 && buttonsAlive[6]) { // PvP
//                System.out.println("effective click 6");
buttonsAlive[6] = false; // Chess pieces already exist in this position (both sides can't play again)
beclickedButton = 6;
if (inP1Turn) {
P1PlaceOver = true; // If P1 is currently dropped, P1 is finished
window.nineBoard[6].setIcon(new ImageIcon("src/X_1.gif")); // Click to modify the background of the button, indicating P1 drop
} else {
P2PlaceOver = true; // If P2 is currently dropped, P2 is finished
window.nineBoard[6].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button, indicating P2 drop
}
}
}));
if (youCanClick && nowStatus == 2 && buttonsAlive[7]) { // Allow players to click
//                System.out.println("effective click 7");
buttonsAlive[7] = false;
youCanClick = false;
beclickedButton = 7;
humanPlaceOver = true;
window.nineBoard[7].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button to indicate the drop
}

if (nowStatus == 1 && buttonsAlive[7]) { // PvP
//                System.out.println("effective click 7");
buttonsAlive[7] = false; // Chess pieces already exist in this position (both sides can't play again)
beclickedButton = 7;
if (inP1Turn) {
P1PlaceOver = true; // If P1 is currently dropped, P1 is finished
window.nineBoard[7].setIcon(new ImageIcon("src/X_1.gif")); // Click to modify the background of the button, indicating P1 drop
} else {
P2PlaceOver = true; // If P2 is currently dropped, P2 is finished
window.nineBoard[7].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button, indicating P2 drop
}
}
}));
if (youCanClick && nowStatus == 2 && buttonsAlive[8]) { // Allow players to click
//                System.out.println("effective click 8");
buttonsAlive[8] = false;
youCanClick = false;
beclickedButton = 8;
humanPlaceOver = true;
window.nineBoard[8].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button to indicate the drop
}

if (nowStatus == 1 && buttonsAlive[8]) { // PvP
//                System.out.println("effective click 8");
buttonsAlive[8] = false; // Chess pieces already exist in this position (both sides can't play again)
beclickedButton = 8;
if (inP1Turn) {
P1PlaceOver = true; // If P1 is currently dropped, P1 is finished
window.nineBoard[8].setIcon(new ImageIcon("src/X_1.gif")); // Click to modify the background of the button, indicating P1 drop
} else {
P2PlaceOver = true; // If P2 is currently dropped, P2 is finished
window.nineBoard[8].setIcon(new ImageIcon("src/O_1.gif")); // Click to modify the background of the button, indicating P2 drop
}
}
}));

}

// Clear chessboard and initialize variables
public void clearChessBoard() {
for (int i = 0; i < 9; i++) {
chessBoard[i / 3][i % 3] = 0; // Initialize chessboard
buttonsAlive[i] = true; // The buttons are clickable
window.nineBoard[i].setIcon(new ImageIcon("src/tm.png"));
}

// PvE
Window.gameStart_PvE = true; // The game begins
newgame = true;
beclickedButton = -1; // The number of the clicked button
humanPlaceOver = false; // Are you waiting for the player to click and drop

// PvP
Window.gameStart_PvP = true; // The game begins
beclickedButton = -1; // The number of the clicked button
P1PlaceOver = false;
P2PlaceOver = false;
}

// ---------------Minimax algorithm core (start)---------------

// Judge whether there is a victory
int isWin() { // Judge whether there is a victory
for (int i = 0; i < 3; i++) {
if (chessBoard[i][0] != 0 && chessBoard[i][0] == chessBoard[i][1] && chessBoard[i][1] == chessBoard[i][2])
return chessBoard[i][0];
if (chessBoard[0][i] != 0 && chessBoard[0][i] == chessBoard[1][i] && chessBoard[1][i] == chessBoard[2][i])
return chessBoard[0][i];
}
if (chessBoard[0][0] != 0 && chessBoard[0][0] == chessBoard[1][1] && chessBoard[1][1] == chessBoard[2][2])
return chessBoard[0][0];
if (chessBoard[2][0] != 0 && chessBoard[2][0] == chessBoard[1][1] && chessBoard[1][1] == chessBoard[0][2])
return chessBoard[2][0];
return 0; // 0 does not represent a draw
}

// Determine whether the game is over
public boolean gameOver() {
if (isWin() != 0) return true;
int i;
for (i = 0; i < 9; i++)
if (chessBoard[i / 3][i % 3] == 0) break;
return i == 9;
}

// Calculate valuation
int evaluate() {
int tmp[][] = new int[3][3], computerValue = 0, playerValue = 0;
// Calculate the valuation of MAX (computer)
for (int i = 0; i < 9; i++)
tmp[i / 3][i % 3] = (chessBoard[i / 3][i % 3] == 0 ? 1 : chessBoard[i / 3][i % 3]);
for (int i = 0; i < 3; i++)
computerValue += (tmp[i][0] + tmp[i][1] + tmp[i][2]) / 3 + (tmp[0][i] + tmp[1][i] + tmp[2][i]) / 3;
computerValue += (tmp[0][0] + tmp[1][1] + tmp[2][2]) / 3 + (tmp[2][0] + tmp[1][1] + tmp[0][2]) / 3;

// Calculate the valuation of MIN (player)
for (int i = 0; i < 9; i++)
tmp[i / 3][i % 3] = (chessBoard[i / 3][i % 3] == 0 ? -1 : chessBoard[i / 3][i % 3]);
for (int i = 0; i < 3; i++)
playerValue += (tmp[i][0] + tmp[i][1] + tmp[i][2]) / 3 + (tmp[0][i] + tmp[1][i] + tmp[2][i]) / 3;
playerValue += (tmp[0][0] + tmp[1][1] + tmp[2][2]) / 3 + (tmp[2][0] + tmp[1][1] + tmp[0][2]) / 3;

return computerValue + playerValue;
}

// Computer: try to find the best step. If this step can win, go to the next step. Otherwise, find the best position through the minimax algorithm of alpha beta pruning
public boolean computerImmidiateWin(MyInteger bestMove) {
for (int i = 0; i < 9; i++) {
if (chessBoard[i / 3][i % 3] != 0) continue;
chessBoard[i / 3][i % 3] = 1;
boolean win = (isWin() == 1);
chessBoard[i / 3][i % 3] = 0;
if (win) {
bestMove.setValue(i); // If you find a step that can make the computer win, bestMove is set to this step and returned through "reference"
return true;
}
}
return false;
}

// Player: try to find the best step. If this step can win, go to the next step. Otherwise, find the best position through the minimax algorithm of alpha beta pruning
public boolean humanImmidiateWin(MyInteger bestMove) {
for (int i = 0; i < 9; i++) {
if (chessBoard[i / 3][i % 3] != 0) continue;
chessBoard[i / 3][i % 3] = -1;
boolean win = (isWin() == -1);
chessBoard[i / 3][i % 3] = 0;
if (win) {
bestMove.setValue(i);
return true;
}
}
return false;
}

// Computer: alpha beta pruning minimax algorithm
public void findComputerBestMove(MyInteger bestMove, MyInteger value, int alpha, int beta, int deep) {
if (computerImmidiateWin(bestMove)) { // If there is a winning step, take it
value.setValue(60); // The valuation of pizza hut is 60
return;
}
if (deep == 3) { // Up to the third layer, it is actually two layers of pruning
value.setValue(evaluate()); // Valuation of current node
return;
}
value.setValue(alpha);
for (int i = 0; i < 9 && value.getValue() < beta; i++) { // If the value of the current node (this value represents the range of the MAX node [value, + ∞) > = the beta of its ancestor node (that is, the range (- ∞, beta) of the MIN node near the tree root)
if (chessBoard[i / 3][i % 3] != 0) continue; // Non zero
chessBoard[i / 3][i % 3] = 1;
MyInteger tmp = new MyInteger(-1), response = new MyInteger(-1);
findHumanBestMove(tmp, response, value.getValue(), beta, deep + 1); // -1 is a temporary value, which will be updated in recursion; response is the evaluation of the child node and returns the value; The value value of the current node is used as the alpha value, that is, the range of the MAX node [value, + ∞), for pruning within the function
chessBoard[i / 3][i % 3] = 0; // to flash back
if (response.getValue() > value.getValue()) { // The current node is a MAX node, so it needs to be updated with the maximum value of the child node
value.setValue(response.getValue());
bestMove.setValue(i); // Update the best drop point
}
}
}

// Player: alpha beta pruning minimax algorithm
public void findHumanBestMove(MyInteger bestMove, MyInteger value, int alpha, int beta, int deep) {
if (humanImmidiateWin(bestMove)) {
value.setValue(-20);
return;
}
if (deep == 3) {
value.setValue(evaluate());
return;
}
value.setValue(beta);
for (int i = 0; i < 9 && value.getValue() > alpha; i++) {
if (chessBoard[i / 3][i % 3] != 0) continue;
chessBoard[i / 3][i % 3] = -1;
MyInteger tmp = new MyInteger(-1), response = new MyInteger(-1);
findComputerBestMove(tmp, response, alpha, value.getValue(), deep + 1);
chessBoard[i / 3][i % 3] = 0;
if (response.getValue() < value.getValue()) {
value.setValue(response.getValue());
bestMove.setValue(i);
}
}
}

// Computer drop
public void computerPlace() {
MyInteger bestMove = new MyInteger(0), value = new MyInteger(0); // Through object passing, you can achieve the effect of reference similar to C + +
final int deep = 1, alpha = -20, beta = 60;
findComputerBestMove(bestMove, value, alpha, beta, deep);
chessBoard[bestMove.getValue() / 3][bestMove.getValue() % 3] = 1;
buttonsAlive[bestMove.getValue()] = false;
//        System.out.println(bestMove.getValue());
window.nineBoard[bestMove.getValue()].setIcon(new ImageIcon("src/X_1.gif"));
}

// Player drop
public void humanPlace() {
if (!Window.gameStart_PvE) return; // So that the player thread can terminate
//        System.out.println("waiting for human");
while (!humanPlaceOver) {
try { // Not without a little sleep
} catch (Exception e) {

}
}
chessBoard[beclickedButton / 3][beclickedButton % 3] = -1;
//        System.out.println("human place!");

humanPlaceOver = false;
}

// ---------------Minimax algorithm core (end)---------------
}


Topics: Algorithm AI