Lai Yonghao (http://laiyonghao.com)
First, I did not write this code, but I added the comments. Author is shaofei Cheng, his website: http://shaofei.name
Second, currently, this code only uses alpha-beta pruning, which has a weak playing force and has a lot of room for optimization. However, the Code is clearly written. If a friend is interested in the topic of man-machine chess and has not been started yet, this code is a great example.
Third, at present, computers can only search for three layers. I think it is not a problem to search for five layers after iterative deepening and historical inspiration algorithms are added. The performance of modern Javascript is good.
Fourth, the author shows a lot of skills in the code, which is worth learning and learning from. Even if you don't understand JavaScript, you can easily understand the code (I don't understand it ).
Fifth, try this AI chess: http://shaofei.name/OthelloAI/othello.html
The following code is used:
VaR AI ={}; <br/> New Function () {<br/> AI. pattern = pattern; <br/> // defines eight offsets. <br/> // you can simply use addition to obtain the coordinates of any eight points around a point. <br/> //-11-10 -9 <br/> //-1x1 <br/> // 9 10 11 <br/> // If the coordinate in the upper left corner is x + (-11) <br/> var directions = [-11,-10,-9,-, 9, 10, 11]; <br/> function pattern () <br/> {<br/> // fill the entire board with 0 <br/> for (VAR I = 0; I <100; I ++) this [I] = 0; <br/> // four grids in the middle, first put two black and white pawns <br/> This [54] = This [45] = 1; this [55] = This [44] = 2; <br/> // The number of external finalists (black minus white), which is used for valuation. <Br/> This. divergence = 0; <br/> // currently, you can play black games. <br/> This. color = 1; <br/> // a few moves have been taken <br/> This. moves = 0; <br/> // stable prototype <br/> // 0 is blank, 1 is black, 2 is white, 3 is the border <br/> // expands the 8*8 chessboard to 10*10, it is a technique <br/> // which can simplify the determination of coordinate validity <br/> var stableproto = [<br/> 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, <br/> 3, 0, 0, 0, 0, 0, 0, 0, 3, <br/> 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, <br/> 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, <br/> 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, <B R/> 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, <br/> 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, <br/> 3, 0, 0, 0, 0, 0, 0, 0, 3, <br/> 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, <br/> 3, 3, 3, 3, 3, 3, 3 <br/>] <br/> // load status from an 8x8 chessboard <br/> This. load = function (ARR) <br/>{< br/> for (var y = 1; y <= 8; y ++) <br/> {<br/> for (VAR x = 1; x <= 8; X ++) <br/> {<br/> This [y * 10 + x] = arr [Y-1] [x-1]; <br/>}< br/> // determines whether a pass is allowed. <br/> // If yes Chess party change <br/> This. pass = function () <br/>{< br/> for (var y = 1; y <= 8; y ++) <br/> {<br/> for (VAR x = 1; x <= 8; X ++) <br/>{< br/> If (this [y * 10 + x] = 0) <br/>{< br/> // you can take any step in chess, none of them can pass <br/> If (this. move (X, Y, this. color) <br/>{< br/> return false; <br/>}< br/> // alert ("pass "); <br/> // This is a technique because this. the value range of color is {1, 2} <br/> // so when color is 1, after the next statement is executed, 2 <br/> // when the color is 2, 1 is after the next statement is executed. <br/> This. c Olor = 3-this. color; <br/> return true; <br/>}< br/> This. clone = function () <br/>{< br/> function pattern () {}< br/> pattern. prototype = This; <br/> return New Pattern (); <br/>}< br/> This. tostring = function () <br/>{< br/> var icon = ["", "*", "O"] <br/> var r = ""; <br/> for (var y = 1; y <= 8; y ++) <br/> {<br/> for (VAR x = 1; x <= 8; X ++) <br/> {<br/> r + = icon [this [y * 10 + x] + ""; <br/> // R + = stablediscs [y * 10 + x] + ""; <br/>}< br/> r + = "/N"; <br/>}< br/> return r + this. exact (); <br/>}</P> <p> // Number of winners <br/> This. exact = function () <br/>{< br/> // This is a technique. R [0] is not used, R [1] R [2] corresponds to the number of black and white pawns <br/> var r = [0, 0]; <br/> for (var y = 1; Y <= 8; y ++) <br/>{< br/> for (VAR x = 1; x <= 8; X ++) <br/>{< br/> r [this [y * 10 + x] ++; // Add one to the number <br/>}< br/> // the current number of colors is 0, negative value returned <br/> If (R [this. color] = 0) Return-64; <br/> // The number of hostile colors is 0, returns the extreme value <br/> If (R [3-this.color] = 0) Return 64; <br/> // return the number of players playing the game more than the other party. <br/> return R [this. color]-R [3-this.color]; <br/>}< br/> // valuation of the Board <br/> This. calculate = function () <br/>{< br/> // basic valuation method: <br/> // 1. It is very valuable to occupy the four corners of the board. <br/> // 2. The seat near the four corners of the board is very poor. <br/> // 3. Stability sub-<br/> // 4. Number of excircle sub-Victories <br/> var r = [0, 0, 0]; <br/> var r = This. divergence; <br/> // if there is a pawn in the upper-left corner, + 30 points for the pawn,-30 points for the enemy <br/> If (this [11]) R + = (this [11] = This. color )? 1:-1) * 30; <br/> // upper left corner, score:-15 <br/> else if (this [22] = This. color) r-= 15; <br/> // upper right corner, score 30 <br/> If (this [18]) R + = (this [18] = This. color )? 1:-1) * 30; <br/> // time upper right corner, score-15 <br/> else if (this [27] = This. color) r-= 15; <br/> // lower left corner, score 30 <br/> If (this [81]) R + = (this [81] = This. color )? 1:-1) * 30; <br/> // lower left corner, score-15 <br/> else if (this [72] = This. color) r-= 15; <br/> // bottom right corner, score 30 <br/> If (this [88]) {R + = (this [88] = This. color )? 1:-1) * 30;} <br/> // lower right corner of the time, score-15 <br/> else if (this [77] = This. color) r-= 15; <br/> // search for the stability sub-item. <br/> // The stability Sub-item is placed at Four Corner Points and the surrounding sub-pieces are either in the same color, either the boundary <br/> // var color = This. color; <br/> var stablediscs = stableproto. slice (); </P> <p> var queue = []; <br/> If (this [11]! = 0) queue. Push ([11, this [11]); <br/> If (this [18]! = 0) queue. Push ([18, this [18]); <br/> If (this [81]! = 0) queue. Push ([81, this [81]); <br/> If (this [88]! = 0) queue. push ([88, this [88]); <br/> while (queue. length) <br/> {<br/> var position = queue [0] [0]; <br/> var c = queue [0] [1]; <br/> // the memory management algorithm of the JS array is not understood, but it is definitely slow to delete the array from the header. <br/> // I think it would be better to delete the array from the back, alternatively, it is better to use the tag-not-deleted method. <br/> queue. shift (); <br/> // If (stablediscs [position] = 0 | stablediscs [position] = 3) continue; <br/> stablediscs [position] = C; <br/> If (stablediscs [position-10] = 3 | stablediscs [Position + 10] = 3 | Stablediscs [position-10] = c | stablediscs [Position + 10] = C) & <br/> (stablediscs [position-1] = 3 | stablediscs [Position + 1] = 3 | stablediscs [position-1] = c | stablediscs [Position + 1] = C) & <br/> (stablediscs [position-11] = 3 | stablediscs [Position + 11] = 3 | stablediscs [position-11] = c | stablediscs [Position + 11] = C) & <br/> (stablediscs [position-9] = 3 | stablediscs [Position + 9] = 3 | S Tablediscs [position-9] = c | stablediscs [Position + 9] = C) <br/>{< br/> stablediscs [position] = C; <br/> // the score of the stabilized Sub-item is 7 <br/> r + = (C = This. color )? 1:-1) * 7; <br/> // further expand to search for stable sub-accounts <br/> for (VAR I = 0; I <directions. length; I ++) <br/> If (stablediscs [directions [I] + position] = 0 & this [directions [I] + position] = C) <br/> queue. push ([directions [I] + position, C]); <br/>}</P> <p> // return value <br/> return r; <br/>}< br/> This. tolocalstring = function (depth) <br/>{< br/> var r = ""; <br/> If (! Depth) depth = 0; </P> <p> for (var y = 1; y <= 8; y ++) <br/> {<br/> for (VAR x = 1; x <= 8; X ++) <br/>{< br/> If (this [y * 10 + x]! = 0) R + = (this [y * 10 + x] = 1? "*": "O") + ""; <br/> else <br/>{< br/> var TMP = This. move (X, Y, this. color); <br/> If (TMP) <br/>{< br/> var tmp2 =-TMP. search (-infinity, infinity, depth); <br/> If (tmp2 <0 | tmp2> 9) R + = tmp2; <br/> else R + = "" + tmp2; <br/>}< br/> else R + = "X "; <br/>}< br/> r + = "/N"; <br/>}< br/> return r + this. exact (); <br/>}< br/> // go to the computer to find a step to go <br/> // enter the AI part here <br/> This. computer = function (depth, exactdepth) {<br/> If (! Depth) depth = 0; <br/> If (! Exactdepth) exactdepth = depth; <br/> var r = []; <br/> var max =-infinity; <br/> for (var y = 1; Y <= 8; y ++) <br/>{< br/> for (VAR x = 1; x <= 8; X ++) <br/>{< br/> If (this [y * 10 + x]) continue; <br/> // find a blank grid. <br/> else <br/> {<br/> // try this grid. <br/> var TMP = This. move (X, Y, this. color); <br/> // failed, illegal <br/> If (! TMP) continue; <br/> // number of steps + search depth> = There are 60 steps <br/> // use exact search to get more accurate results <br/> If (this. moves + exactdepth> = 60) <br/>{< br/> var v =-TMP. exactsearch (-infinity, infinity); <br/> // alert ([x, y] + ":" + V ); <br/>}< br/> // 3*4 grids closest to four corners, search for one more layer <br/> // because the opponent may be at the bottom of the corner in the next hand, a large flip occurs. <Br/> else if (x = 2 | x = 7) & (y = 2 | Y = 7 )) <br/> var v =-TMP. search (-infinity, infinity, depth + 1); <br/> else <br/> var v =-TMP. search (-infinity, infinity, depth); <br/> // It is not as good as the previous chess step <br/> If (v <max) continue; <br/> // better than the previous steps <br/> If (V> MAX) {<br/> // save it <br/> r = [[x, y]; max = V; <br/>}< br/> // another optional chess step <br/> else R. push ([x, y]); <br/>}< br/> // In all optional chess steps, the random selection of one option makes players feel a lot of changes, not so monotonous. <Br/> var TMP = math. floor (math. random () * R. length); <br/> return R [TMP]; <br/>}< br/> // search algorithm <br/> // use the alpha-beta pruning Search Algorithm in the form of negative maximum value <br/> This. search = function (alpha, beta, depth, pass) {<br/> // leaf node, returns the value <br/> If (depth = 0) return this. calculate (); <br/> var canmove = false; <br/> for (var y = 1; y <= 8; y ++) <br/> {<br/> for (VAR x = 1; x <= 8; X ++) <br/>{< br/> If (this [y * 10 + x]! = 0) R + = (this [y * 10 + x] = 1? "*": "O") + ""; <br/> else <br/>{< br/> var TMP = This. move (X, Y, this. color); <br/> If (! TMP) continue; <br/> canmove = true; <br/> // deeper search <br/> var r =-TMP. search (-Beta,-Alpha, depth-1); <br/> // If (depth = 4) wscript. echo (r); <br/> // Receiving Window <br/> If (r> = alpha) alpha = R; <br/> // wins <br/> If (alpha> beta) return infinity; <br/>}< br/> // return the optimal valuation of the Current Situation <br/> If (canmove) return Alpha; <br/> // if no sub-account is available, the number of sub-accounts is returned. <br/> If (PASS) return this. exact (); <br/> // pass once to search for this. color = 3-this.color; <Br/> return-this. search (-Beta,-Alpha, depth-1, true); <br/>}< br/> // exact search, the principle of this section is the same as that of search. <br/> This. exactsearch = function (alpha, beta, pass) {<br/> // you have taken 60 steps and returned the number of winners <br/> If (this. moves = 60) return this. exact (); <br/> var canmove = false; <br/> for (var y = 1; y <= 8; y ++) <br/> {<br/> for (VAR x = 1; x <= 8; X ++) <br/>{< br/> If (this [y * 10 + x]! = 0); // R + = (this [y * 10 + x] = 1? "*": "O") + ""; <br/> else <br/>{< br/> var TMP = This. move (X, Y, this. color); <br/> If (! TMP) continue; <br/> canmove = true; <br/> var r =-TMP. exactsearch (-Beta,-alpha); <br/> If (r> = alpha) alpha = r; <br/> If (alpha> beta) return infinity; <br/>}< br/> If (canmove) return Alpha; <br/> If (PASS) return this. exact (); <br/> This. color = 3-this.color; <br/> return-this. exactsearch (-Beta,-Alpha, true); <br/>}< br/> // try to put this in X and Y. color-colored pawns. the next board is returned successfully. Otherwise, null is returned. <br/> This. move = function (x, y) <br/ >{< Br/> // copy the current status <br/> var pattern = This. clone (); <br/> pattern. color = 3-this.color; <br/> // pay attention to this negative number <br/> pattern. divergence =-pattern. divergence; <br/> // move number + <br/> pattern. moves ++; <br/> var canmove; <br/> canmove = false; <br/> // put it at the function entry, performance can be optimized <br/> // put 10 * Y + X into a temporary variable to optimize performance <br/> If (pattern [10 * Y + x]! = 0) return NULL; <br/> // 8 direction judgment <br/> for (VAR I = 0; I <8; I ++) <br/> {<br/> // convert to a one-dimensional index <br/> var P = 10 * Y + x + directions [I]; <br/> // different colors of the pawns on the adjacent lattice <br/> If (pattern [p] = 3-this.color) <br/> while (pattern [p]! = 0) <br/>{< br/> // search in the same direction <br/> P + = directions [I]; <br/> // the other end has its own pawn, which is a walking point. <Br/> If (pattern [p] = This. color) <br/>{< br/> canmove = true; <br/> // turn the Middle pawn over <br/> while (p + =-directions [I])! = 10 * Y + x) <br/>{< br/> pattern [p] = This. color; <br/> // alert (p); <br/> for (var d = 0; D <8; D ++) <br/>{< br/> // non-empty <br/> If (! Pattern [p + directions [d] <br/> // non-boundary <br/> & P + directions [d]> 10 <br/> & P + directions [D] <89 <br/> & (p + directions [d]) % 10! = 0 <br/> & (p + directions [d])! = 9) <br/> // The number of peripheral winning sub-accounts increases. <br/> pattern. divergence ++; <br/>}< br/> break; <br/>}< br/> // return the new checkerboard status <br/> If (canmove) {pattern [10 * Y + x] = This. color; Return Pattern ;}< br/> else return NULL; <br/>}< br/> // pattern. prototype = emptyboard; <br/> // wscript. echo (New Pattern (). move (5, 6 ). move (6, 4 ). move (4, 3 ). move (3, 4 ). tolocalstring (3); <br/> // wscript. echo (New Pattern (). move (5, 6 ). search (-infinity, infinity, 2); <br/>}() <br/>