Gobang evaluation function

foreword

Recently, I wrote a backgammon human-machine AI, but it has been very slow. In fact, I feel that the bottleneck of performance is not the problem of minimizing the maximum algorithm and α-β pruning, but the evaluation function is too poor. In fact, the evaluation function is much more difficult than the minimax algorithm and the α-β pruning algorithm. The specific performance is that I started to follow this blog (the explanation is very detailed, although the α-β pruning is a bit wrong , someone pointed it out in the comment area, but other places, including the evaluation function, are worth learning from. I would also like to express my thanks to the author here.) When learning the game tree, the minimization algorithm and α-β pruning, I only read one Introduced and the data structure model of the node, and then fumbled to write the code (although I don't know if it is correct or not). As for the evaluation function, I have no clue at all, so I had to copy the code from the tutorial. Later I studied those codes carefully and finally understood. And when I was bored after writing the test paper yesterday, I came up with a method that can slightly increase the speed.
I also combined this AI with the two-player backgammon I made before to form a man-machine game program with MFC graphical interface, click here to download .

old version valuation function

The original version of the idea is basically as follows:
traverse the chessboard, find all the chess chains composed of five points, and execute the following logic for each chess chain:

  1. If there are both white and black stones in the chain, the value of the chain is 0;
  2. If there are only n white pieces of one color in the chain (default computer is white), if n ≤ 4 n≤ 4n4 ,get knk^nkn points; ifn = 5 n=5n=5 , get INT_MAX points and immediately end the entire algorithm (not the evaluation of a chain, but the evaluation of the entire board). k can be adjusted freely, I am 10 here.
  3. If there are only n black pieces of one color in the chain (default computer is white), if n ≤ 4 n≤ 4n4 ,get − pkn -pk^npkn points; ifn = 5 n=5n=5 , get INT_MIN points and immediately end the whole algorithm (not the evaluation of a chess chain, but the evaluation of the entire chessboard). p can be adjusted freely, I am -1 here. If you want the computer to give priority to defense, set p to a large value, such as 1.1.

Finally, add up the scores of all the chess chains.
The C++ code is as follows:

/*
一个节点类的成员函数,State是枚举,有BLACK,WHITE,SPACE;Board是棋盘,State[15][15]类型的。
*/
int Evaluate()const//估价函数
{
    
    
	int result = 0;
	static auto EvaluateSome = [](const std::array<State, 5>& v)//假定自己是白方
	{
    
    
		//判断颜色并记录棋子个数
		State lastColor = SPACE;
		uint8_t count = 0;
		for (State i : v)
		{
    
    
			if (i != SPACE)
			{
    
    
				++count;
				if (i != lastColor)
				{
    
    
					if (lastColor == SPACE)//遇到的第一个棋子
					{
    
    
						lastColor = i;
					}
					else//有不同颜色的棋子
					{
    
    
						return 0;
					}
				}
			}
		}
		if (!count)//没有棋子
			return 0;
		if (count == 5)
		{
    
    
			return lastColor == WHITE ? INT_MAX : INT_MIN;//一定不要认为-INT_MAX就是INT_MIN!
		}
		const int result = static_cast<int>(std::pow(10, count - 1));
		return lastColor == WHITE ? result : static_cast<int>(-1.1 * result);//对手返回负值,我方返回正值,乘以1.1后优先防守
	};
	for (uint8_t i = 0; i < 15; i++)//分别从四个方向判断
	{
    
    
		for (uint8_t j = 0; j < 15; j++)
		{
    
    
			if (j + 4 < 15)
			{
    
    
				std::array<State, 5>v;
				for (uint8_t k = 0; k < 5; k++)
					v[k] = board[i][j + k];
				const int t = EvaluateSome(v);
				if (t == INT_MAX || t == INT_MIN)//决出胜负直接返回
					return t;
				result += t;
			}
			if (i + 4 < 15)
			{
    
    
				std::array<State, 5>v;
				for (uint8_t k = 0; k < 5; k++)
					v[k] = board[i + k][j];
				const int t = EvaluateSome(v);
				if (t == INT_MAX || t == INT_MIN)//决出胜负直接返回
					return t;
				result += t;
			}
			if (i + 4 < 15 && j + 4 < 15)
			{
    
    
				std::array<State, 5>v;
				for (uint8_t k = 0; k < 5; k++)
					v[k] = board[i + k][j + k];
				const int t = EvaluateSome(v);
				if (t == INT_MAX || t == INT_MIN)//决出胜负直接返回
					return t;
				result += t;
			}
			if (i + 4 < 15 && j - 4 >= 0)
			{
    
    
				std::array<State, 5>v;
				for (uint8_t k = 0; k < 5; k++)
					v[k] = board[i + k][j - k];
				const int t = EvaluateSome(v);
				if (t == INT_MAX || t == INT_MIN)//决出胜负直接返回
					return t;
				result += t;
			}
		}
	}
	return result;
}

This code needs to traverse the entire chessboard once. Roughly estimate, the traversal range is 15×15=225.

The new version of the valuation function

It suddenly occurred to me that when you go down to a place, you can only affect the surrounding 11×11 range. We can save the valuation of each node, and then, when creating a new node, first estimate the 11×11 according to the original method. 11 range, then evaluate the parent node’s 11×11 range, subtract the parent node’s 11×11 range score from the parent node’s original evaluation score, plus the child node’s 11×11 range score, which is the child node’s score. In the worst case (the place where the move is close to the middle of the chessboard), this algorithm needs to traverse a range of 11×11×2=242, which seems to be slow, but if the place where the move is close to the corner of the chessboard makes the range of 11×11 Zoom out, then you can save time. In the best case (down to the corner of the board), the traversal range is only 6×6×2=72. In reality, the corners are traversed many times, so the speed is much faster. This also has the advantage that the worst case is always 242, and it will not become larger because the chessboard becomes larger. If the chessboard is 100×100, this algorithm still traverses 242, and the first algorithm traverses 100×100=10000 indivual. After testing, this method is much faster than the original. Here is the C++ code:

int Evaluate()const//估价函数
{
    
    
	static auto EvaluateSome = [](State board[BOARDSIZE][BOARDSIZE], uint8_t beginX, uint8_t endX, uint8_t beginY, uint8_t endY) {
    
    
		static auto EvaluateList = [](const std::array<State, 5>& v)//假定自己是白方
		{
    
    
			//判断颜色并记录棋子个数
			State lastColor = SPACE;
			uint8_t count = 0;
			for (State i : v)
			{
    
    
				if (i != SPACE)
				{
    
    
					++count;
					if (i != lastColor)
					{
    
    
						if (lastColor == SPACE)//遇到的第一个棋子
						{
    
    
							lastColor = i;
						}
						else//有不同颜色的棋子
						{
    
    
							return 0;
						}
					}
				}
			}
			if (!count)//没有棋子
				return 0;
			if (count == 5)
			{
    
    
				return lastColor == WHITE ? INT_MAX : INT_MIN;//一定不要认为-INT_MAX就是INT_MIN!
			}
			const int result = static_cast<int>(std::pow(10, count - 1));
			return lastColor == WHITE ? result : -result;//对手返回负值,我方返回正值
		};
		int result = 0;
		for (uint8_t i = beginX; i < endX; i++)//分别从四个方向判断
		{
    
    
			for (uint8_t j = beginY; j < endY; j++)
			{
    
    
				if (j + 4 < endY)
				{
    
    
					std::array<State, 5>v;
					for (uint8_t k = 0; k < 5; k++)
						v[k] = board[i][j + k];
					const int t = EvaluateList(v);
					if (t == INT_MAX || t == INT_MIN)//决出胜负直接返回
						return t;
					result += t;
				}
				if (i + 4 < endX)
				{
    
    
					std::array<State, 5>v;
					for (uint8_t k = 0; k < 5; k++)
						v[k] = board[i + k][j];
					const int t = EvaluateList(v);
					if (t == INT_MAX || t == INT_MIN)//决出胜负直接返回
						return t;
					result += t;
				}
				if (i + 4 < endX && j + 4 < endY)
				{
    
    
					std::array<State, 5>v;
					for (uint8_t k = 0; k < 5; k++)
						v[k] = board[i + k][j + k];
					const int t = EvaluateList(v);
					if (t == INT_MAX || t == INT_MIN)//决出胜负直接返回
						return t;
					result += t;
				}
				if (i + 4 < endX && j >= 4)
				{
    
    
					std::array<State, 5>v;
					for (uint8_t k = 0; k < 5; k++)
						v[k] = board[i + k][j - k];
					const int t = EvaluateList(v);
					if (t == INT_MAX || t == INT_MIN)//决出胜负直接返回
						return t;
					result += t;
				}
			}
		}
		return result;
	};
	uint8_t beginX, endX, beginY, endY;
	if (lastX <= 5)
		beginX = 0;
	else
		beginX = lastX - 5;
	endX = lastX + 5;
	if (endX > BOARDSIZE)
		endX = BOARDSIZE;
	if (lastY <= 5)
		beginY = 0;
	else
		beginY = lastY - 5;
	endY = lastY + 5;
	if (endY > BOARDSIZE)
		endY = BOARDSIZE;
	const int t = EvaluateSome((State(*)[15])board, beginX, endX, beginY, endY);
	if (t == INT_MAX || t == INT_MIN)//决出胜负直接返回
		return t;
	return  t - EvaluateSome((State(*)[15])father->board, beginX, endX, beginY, endY) + father->evaluateValue;
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324153839&siteId=291194637