Extremely small search strategies are generally used in NLP games:
In this way, the deep search policy is used in essence, so it can be implemented using recursive methods. In the search process, the maximum value should be obtained for the search point advantageous to the local party, and the minimum value should be obtained for the search point unfavorable to the local party.
The minimum and maximum values are relative.
In the search process, you need to properly control the search depth. The deeper the search depth, the lower the efficiency. But in general, the better the search method.
Extremely small searches can be written separately or written together.
The main algorithm steps are as follows:
Input: search depth
Output: the optimal route of the node and its corresponding optimal Valuation
Function Format: int minmaxsearch (INT depth) Here you can also add the int side parameter to indicate who is playing the game currently
If it is red
Initialize the optimal value best = negative infinity // very big point. Here we think that the Red Square goes first.
Otherwise
Initialize the optimal value best = positive infinity // minimum
If depth <= 0
Call evaluation function value
Otherwise
Generate all reasonable current steps
Step by step
Execution steps
Call minmaxsearch (depth-1) and assign the value to value
Withdrawal steps
If it is red
If value> Best
Best = Value
If depth = max_depth
Bestmove = mv
Otherwise
If value <best
Best = Value
If depth = max_depth
Bestmove = mv
Back to best
In the situation evaluation function, the difference between the advantages of both parties is generally returned as the evaluation value.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~
Here we will summarize the negative maximum search policy:
In a situation, X is the advantage of the red party, and-X is the advantage of the black party. In a situation, X is the advantage of the red party, and X is the advantage of the black party. In the negative maximum search algorithm, there is no minimum point, but only a very large point. It should be noted that when the advantages of one party are converted to those of the other, a negative sign is required. The estimated range is a symmetric range of 0 points:
[-Maxvalue, maxvalue]. it should be noted that, in order to make the negative maximum search algorithm get a correct evaluation, the return value of the situation evaluation function must be modified. The original result in the extremely small search algorithm always returns the advantage of the Red Square, now we want to change it to the advantages of the current player.
Negative maximum search algorithm:
Input: search depth
Output: the optimal route of the node and the corresponding optimal Valuation
Function Format: int negamaxsearch (INT depth)
Initialize the optimal value best = negative infinity // All are very big points
If depth is less than or equal to 0
Call the evaluation function and assign the result to the value
Return Value
Otherwise
Generate all valid steps
Steps for each step
Execution steps
Value =-negamaxsearch (depth-1) // note that there is a negative number before the Function
Withdrawal steps
If value> Best
Best = Value
If depth = max_depth
Bestmove = mv
Return best // return the optimal evaluation value of a search Branch
Algorithm for evaluating functions:
Input: Game Board
Output: Advantages of the situation on the current party
Rvalue: Sum of advantages of the Red Square
Bvalue: Total strengths of the black party
Evaluate the problem and design the problem separately to obtain the values of rvalue and bvalue.
If the current situation is a game player
Return rvalue-bvalue;
Otherwise
Return bvalue-rvalue;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~
Improvements to extremely small search strategies and negative maximum search strategies -- alpha-beta pruning search strategies
In general, in order to cause pruning during the search process, two parameters must be passed down in the recursive process. The first parameter is Alpha, which indicates the best value found by one of the current search nodes. Any value smaller than it makes no sense. The 2nd values are beta, indicating the current disadvantage of the opponent. This is the best result that the opponent can bear, and the values greater than it will be discarded.
Because it is a negative maximum value search, the larger the beta value, the more obvious the opponent's disadvantage, the smaller the beta value, the smaller the disadvantage.
For the alpha-beta search pruning algorithm, you can use-maxvalue ~ because there is no alpha-beta value in the initial state ~ Maxvalue.
The alpha-beta search pruning algorithm is as follows:
Input: search depth, alpha, beta
Output: the optimal route of the node and the corresponding optimal Valuation
Function Format: int alphabetasearch (INT depth, int Alpha, int beta)
If depth is less than or equal to 0
Call the evaluation function and assign the result to the value
Return Value
Otherwise
Generate all reasonable current steps
For every walk
Execution steps
Value =-alphabetasearch (depth-1,-Beta,-alpha );
// Note that there is a negative value before the function, and the alpha and beta parameters take negative values and exchange them.
Withdrawal steps
If value> = beta // you can search for a method first, because the search efficiency depends largely on the pruning effect.
Return to Beta
If value> alpha
Alpha = Value
If depth = max_depth
Bestmove = mv
Returns Alpha.
Call method: alphabetasearch (maxdepth,-maxvalue, maxvalue)
Traditionally, the interval that is passed to the alpha and beta values of a node to be searched is called a search window.
The narrower the search window, the higher the possibility of pruning, and the higher the search efficiency.
Will the Alpha value passed to a node to be searched be greater than the beta value? (Absolutely not. It is impossible to even wait ).
The following is the question of poj3317 stake your claim, which uses the negative maximum value search and alpha-beta pruning optimization code. From this experiment, we found that alpha-beta pruning is very helpful for improving efficiency.
The following is a defined situation evaluation class:
# Include <iostream> <br/> # include <cstring> <br/> using namespace STD; <br/>/* <br/> * class for situation evaluation <br/> */<br/> class Evaluater {</P> <p> public: </P> <p> Evaluater (char map [] [8], int S): size (s) {</P> <p> // memcpy (square, map, sizeof (MAP); <br/> for (INT I = 0; I <8; I ++) <br/> for (Int J = 0; j <8; j ++) <br/> Square [I] [J] = map [I] [J]; </P> <p >}</P> <p>/* <br/> * situation evaluation function <br/> */<br/> int getvaluated (int side) {</P> <p> int p0v Alue = 0, p1value = 0; <br/> for (INT I = 0; I <size; I ++) {</P> <p> for (Int J = 0; j <size; j ++) {</P> <p> If ('0' = square [I] [J]) {</P> <p> getmostsquares ('0', I, j); <br/> If (count> p0value) <br/> p0value = count; <br/> COUNT = 1; <br/>}</P> <p> If ('1' = square [I] [J]) {</P> <p> getmostsquares ('1', I, j); <br/> If (count> p1value) <br/> p1value = count; <br/> COUNT = 1; <br/>}</P> <p> if (1 = side) <br/> return p0va Lue-p1value; <br/> else <br/> return p1value-p0value; </P> <p >}</P> <p> PRIVATE: </P> <p>/* <br/> * search for the number of chess pieces in the largest region <br/> */<br/> void getmostsquares (char flag, int X, int y) {</P> <p> static int dir [4] [2] = {0,-1 }, {1, 0 };</P> <p> int I, j; </P> <p> Square [x] [Y] = 'X '; // space marked as 'X' </P> <p> for (int K = 0; k <4; k ++) {</P> <p> I = x + dir [k] [0]; <br/> J = Y + dir [k] [1]; </P> <p> If (I <0 | I> = size | j <0 | J> = size | square [I] [J]! = Flag) <br/> continue; </P> <p> else {</P> <p> count ++; <br/> getmostsquares (flag, I, j); <br/>}</P> <p> Enum tablesize {max_size = 8 }; // maximum size of the Board <br/> int size; // actual size of the Board <br/> char square [max_size] [max_size]; // chessboard <br/> static int count; <br/>}; </P> <p> int Evaluater: Count = 1; // static variables must be initialized outside the class </P> <p>
Below is the implementation of the negative maximum value search: At first, when using extremely large and small values, the results are always not correct, and then directly use the negative maximum value search policy. Note that, the evaluation function corresponding to the negative maximum search needs to return the evaluation value for different current players. In addition, in the search function, all vertices are the maximum vertices, therefore, the value of best can be directly assigned int_min. when returning a recursive function, you do not need to judge side.
/* <Br/> * poj 3317 stake your claim <br/> * this is a game question <br/> * search strategy: extremely small <br/> */<br/> # include <iostream> <br/> # include <fstream> <br/> # include <ctime> <br/> # DEFINE _ debug 0 <br/> # include "mapevaluate. H "<br/> using namespace STD; </P> <p> # define Max 8 </P> <p> char map [Max] [Max]; <br/> int num; // actual size of the Board <br/> // 1: player0,-1: player1 <br/> int currside; <br/> // maximum search depth <br/> int max_depth; </P> <p> typedef struct Mo Ve {</P> <p> int X; <br/> int y; <br/> move () {}< br/> move (INT X1, int Y1): X (X1), y (Y1) {}< br/> friend ostream & operator <(ostream & out, const Move & M) {</P> <p> return out <"(" <m. x <"," <m. Y <")"; <br/>}< br/>} Move; </P> <p> move bestmove; // The best movement </P> <p> void print () {</P> <p> for (INT I = 0; I <num; I ++) {<br/> for (Int J = 0; j <num; j ++) {</P> <p> cout <map [I] [J] <""; </P> <p >}</P> <p> cout <Endl; <br/>}</P> <p>/* <B R/> * generate all possible mobile solutions <br/> */<br/> int genallmove (move * array) {</P> <p> int COUNT = 0; <br/> for (INT I = 0; I <num; I ++) <br/> for (Int J = 0; j <num; j ++) {</P> <p> If ('. '= map [I] [J]) {</P> <p> array [count] = move (I, j); <br/> count ++; <br/>}</P> <p> return count; </P> <p >}</P> <p>/* <br/> * Go <br/> */<br/> inline void makemove (move mV, int side) {</P> <p> map [mV. x] [mV. y] = (1 = side )? '0': '1 '; <br/>}</P> <p>/* <br/> * restore the chessboard <br/> */<br/> inline void unmakemove (move mV) {</P> <p> map [mV. x] [mV. y] = '. '; <br/>}</P> <p>/* <br/> * Core algorithms of extremely small search functions <br/> */<br/> int negmaxsearch (int depth, int side) {</P> <p> int best, value; <br/> move movearray [12]; // a maximum of 1 ~ 10 spaces <br/> move tmpmv; </P> <p> // when using the negative maximum value for search, the best value is always int_min <br/> Best = int_min; </P> <p> If (0 = depth) {</P> <p> Evaluater V (MAP, num); <br/> return v. getvaluated (side); <br/>}</P> <p> int num = genallmove (movearray); </P> <p> for (INT I = 0; I <num; I ++) {</P> <p> tmpmv = movearray [I]; <br/> makemove (tmpmv, side ); </P> <p> If (_ Debug) {<br/> Print (); <br/> cout <Endl ;} </P> <p> // note "~ Side ", otherwise the side value is always true <br/> // After side is returned recursively, side restores the original value because "! Side "the original value of side is not changed <br/> // value = minmaxsearch (depth-1 ,! Side); // each time the game moves, <br/> value =-minmaxsearch (depth-1,-1 * side); </P> <p> unmakemove (tmpmv ); </P> <p> If (_ Debug) {<br/> Print (); <br/> cout <Endl ;} </P> <p> // negative maximum value search <br/> If (value> Best) {</P> <p> Best = value; <br/> If (depth = max_depth) <br/> bestmove = tmpmv; <br/>}</P> <p >}< br/> // returns the optimal extreme value <br/> If (depth = max_depth) <br/> cout <bestmove; <br/> return best; </P> <p >}</P> <p> int main () {</P> <p> ifstream in ("test. TXT "); <br/> int count0, count1; </P> <p> while (1) {</P> <p> in> num; <br/> If (0 = num) <br/> break; </P> <p> // initialization starts at the beginning of each round. <br/> count1 = 0; <br/> count0 = 0; </P> <p> for (INT I = 0; I <num; I ++) {</P> <p> for (Int J = 0; j <num; j ++) {</P> <p> in> map [I] [J]; <br/> If ('1' = map [I] [J]) {</P> <p> count1 ++; <br/>}< br/> If ('0' = map [I] [J]) {</P> <p> count0 ++; <br/>}</P> <p> currside = (count0> count1 )? -1: 1; <br/> // search all the vacant spaces <br/> max_depth = num * num-count1-count0; <br/> If (_ Debug) {<br/> cout <currside <Endl; <br/> cout <max_depth <Endl; <br/>}</P> <p> clock_t time = clock (); <br/> cout <"" <negmaxsearch (max_depth, currside) <Endl; <br/> cout <"computing time:" <clock ()-time <"Ms" <Endl; </P> <p >}< br/>
The code after Alpha-beta pruning is added. Note that the pruning method is as follows:
/* <Br/> * poj 3317 stake your claim <br/> * this is a game question <br/> * search strategy: extremely small <br/> */<br/> # include <iostream> <br/> # include <fstream> <br/> # include <ctime> <br/> # DEFINE _ debug 0 <br/> # include "mapevaluate. H "<br/> using namespace STD; </P> <p> # define Max 8 </P> <p> char map [Max] [Max]; <br/> int num; // actual size of the Board <br/> // 1: player0,-1: player1 <br/> int currside; <br/> // maximum search depth <br/> int max_depth; </P> <p> typedef struct Mo Ve {</P> <p> int X; <br/> int y; <br/> move () {}< br/> move (INT X1, int Y1): X (X1), y (Y1) {}< br/> friend ostream & operator <(ostream & out, const Move & M) {</P> <p> return out <"(" <m. x <"," <m. Y <")"; <br/>}< br/>} Move; </P> <p> move bestmove; // The best movement </P> <p> void print () {</P> <p> for (INT I = 0; I <num; I ++) {<br/> for (Int J = 0; j <num; j ++) {</P> <p> cout <map [I] [J] <""; </P> <p >}</P> <p> cout <Endl; <br/>}</P> <p>/* <B R/> * generate all possible mobile solutions <br/> */<br/> int genallmove (move * array) {</P> <p> int COUNT = 0; <br/> for (INT I = 0; I <num; I ++) <br/> for (Int J = 0; j <num; j ++) {</P> <p> If ('. '= map [I] [J]) {</P> <p> array [count] = move (I, j); <br/> count ++; <br/>}</P> <p> return count; </P> <p >}</P> <p>/* <br/> * Go <br/> */<br/> inline void makemove (move mV, int side) {</P> <p> map [mV. x] [mV. y] = (1 = side )? '0': '1 '; <br/>}</P> <p>/* <br/> * restore the chessboard <br/> */<br/> inline void unmakemove (move mV) {</P> <p> map [mV. x] [mV. y] = '. '; <br/>}</P> <p>/* <br/> * Core algorithms of extremely small search functions <br/> */<br/> int alphabetasearch (int depth, int side, int Alpha, int beta) {</P> <p> int best, value; <br/> move movearray [12]; // a maximum of 1 ~ 10 spaces <br/> move tmpmv; </P> <p> If (0 = depth) {</P> <p> Evaluater V (MAP, num ); <br/> return v. getvaluated (side); <br/>}</P> <p> int num = genallmove (movearray); </P> <p> for (INT I = 0; I <num; I ++) {</P> <p> tmpmv = movearray [I]; <br/> makemove (tmpmv, side ); </P> <p> If (_ Debug) {<br/> Print (); <br/> cout <Endl ;} </P> <p> // note "~ Side ", otherwise the side value is always true <br/> // After side is returned recursively, side restores the original value because "! Side "the original value of side is not changed <br/> // value = minmaxsearch (depth-1 ,! Side); // change the player Every time <br/> value =-minmaxsearch (depth-1,-1 * side,-Beta,-alpha ); </P> <p> unmakemove (tmpmv); </P> <p> If (_ Debug) {<br/> Print (); <br/> cout <Endl ;}</P> <p> // alpha-beta pruning search <br/> If (value> = beta) <br/> return Beta; <br/> If (value> alpha) {</P> <p> alpha = value; <br/> If (depth = max_depth) <br/> bestmove = tmpmv; <br/>}</P> <p >}< br/> // returns the optimal extreme value <br/> If (depth = max_depth) <br/> cout <bestmo Ve; <br/> return Alpha; </P> <p >}</P> <p> int main () {</P> <p> ifstream in ("test.txt"); <br/> int count0, count1; </P> <p> while (1) {</P> <p> in> num; <br/> If (0 = num) <br/> break; </P> <p> // initialization starts from each bureau. <br/> count1 = 0; <br/> count0 = 0; </P> <p> for (INT I = 0; I <num; I ++) {</P> <p> for (Int J = 0; j <num; j ++) {</P> <p> in> map [I] [J]; <br/> If ('1' = map [I] [J]) {</P> <p> count1 ++; <br/>}< br/> If ('0' = map [I] [J]) {</P> <p> count0 ++; <BR/>}< br/>}</P> <p> currside = (count0> count1 )? -1: 1; <br/> // search all the vacant spaces <br/> max_depth = num * num-count1-count0; <br/> If (_ Debug) {<br/> cout <currside <Endl; <br/> cout <max_depth <Endl; <br/>}</P> <p> clock_t time = clock (); <br/> cout <"" <alphabetasearch (max_depth, currside,-100,100) <Endl; <br/> cout <"computing time:" <clock ()-time <"Ms" <Endl; </P> <p >}< br/>