-
Notifications
You must be signed in to change notification settings - Fork 24
/
Copy pathai.js
118 lines (93 loc) · 3.84 KB
/
ai.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
const ai = (aiTurn) => {
const ranks = { pawn: 1, king: 2, bishop: 3, knight: 3, rook: 5, queen: 9 };
const simulationGame = new SimulationGame([], 'white');
const deepest = 3;
const humanTurn = aiTurn === 'white' ? 'black' : 'white';
const middleSquares = [44, 45, 54, 55];
const widerMiddleSquares = [43, 46, 53, 56];
const isPieceInMiddle = piece => middleSquares.indexOf(piece.position) !== -1;
const isPieceInWiderMiddle = piece => widerMiddleSquares.indexOf(piece.position) !== -1;
const score = pieces => {
return pieces.reduce( (total, piece) => {
let weight = piece.color === aiTurn ? ranks[piece.rank] : -1 * ranks[piece.rank];
if ( isPieceInMiddle(piece) ) {
weight *= 1.05;
}
else if ( isPieceInWiderMiddle(piece) ) {
weight *= 1.02;
}
total += weight;
return total;
}, 0 );
}
const isBetterScore = (score1, score2, turn) => turn === aiTurn ? score1 >= score2 : score1 <= score2;
const isScoreGoodEnough = (score, turn) => turn === aiTurn ? score > 7 : score < -7;
const minimax = (pieces, turn, depth = 0) => {
simulationGame.startNewGame(pieces, turn);
if ( !simulationGame.getPieceByName(humanTurn + 'King') || simulationGame.king_dead(humanTurn ) ) {
return {score: -Infinity, depth};
}
if ( !simulationGame.getPieceByName(aiTurn + 'King') || simulationGame.king_dead(aiTurn ) ) {
return {score: Infinity, depth};
}
let bestPlay = { move: null, score: turn === aiTurn ? -Infinity : Infinity };
for (const piece of pieces) {
const allowedMoves = simulationGame.getPieceAllowedMoves(piece.name);
for (const move of allowedMoves) {
const currentTestPlayInfo = {};
currentTestPlayInfo.move = {pieceName: piece.name, position: move};
simulationGame.movePiece(piece.name, move);
const curScore = score(simulationGame.pieces);
if ( depth === deepest || isBetterScore(bestPlay.score, curScore, turn) || isScoreGoodEnough(curScore, turn) ) {
currentTestPlayInfo.score = curScore;
}
else if (turn === aiTurn) {
const result = minimax(simulationGame.pieces, humanTurn, depth + 1);
currentTestPlayInfo.score = result.score;
} else {
const result = minimax(simulationGame.pieces, aiTurn, depth + 1);
currentTestPlayInfo.score = result.score;
}
if ( isBetterScore(currentTestPlayInfo.score, bestPlay.score, turn) ){
bestPlay.move = currentTestPlayInfo.move;
bestPlay.score = currentTestPlayInfo.score;
}
simulationGame.startNewGame(pieces, turn);
}
}
return bestPlay;
}
let play;
if ( isTestEnv() ) {
play = (pieces, callback) => {
setTimeout( () => {
testFuncTime( () => {
const aiPlay = minimax(pieces, aiTurn);
callback(aiPlay);
});
}, 100);
}
}
else {
play = (pieces, callback) => {
setTimeout( () => {
const aiPlay = minimax(pieces, aiTurn);
callback(aiPlay);
}, 100);
}
}
return {
play
}
}
const isTestEnv = function() {
const url = new URL(location.href);
const params = new URLSearchParams(url.searchParams);
return Boolean(params.get('testing'))
}
const testFuncTime = func => {
const label = 'Timer ' + Date.now();
console.time(label);
console.log( 'Output:', func() );
console.timeLog(label);
}