Skip to content

Commit 4ae9ce0

Browse files
cr fixes
1 parent 4bc74b7 commit 4ae9ce0

File tree

2 files changed

+115
-117
lines changed

2 files changed

+115
-117
lines changed

src/others/minimax.js

Lines changed: 92 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -3,116 +3,118 @@
33
/* eslint max-params: 0 */
44

55
/**
6-
* Minimax (sometimes MinMax, MM[1] or saddle point[2]) is a decision rule used in artificial intelligence,
7-
* decision theory, game theory, statistics, and philosophy for minimizing the possible loss for a worst case (maximum loss) scenario.
8-
* Optimized with alpha-beta pruning.
9-
* {@link https://en.wikipedia.org/wiki/Minimax}
10-
* {@link https://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning}
11-
*
12-
* @public
13-
* @module others/minimax
14-
*
15-
* @example
16-
*
17-
* var miniMax =
18-
* require('path-to-algorithms/src/others/minimax').minimax;
19-
* var result = minimax(
20-
* [1, 2, 3],
21-
* true,
22-
* 5,
23-
* -Infinity,
24-
* Infinity,
25-
* state => ({ move: 0, state: [2, 3, 4] }),
26-
* state => state[1] < 3,
27-
* state => state[1]
28-
* );
29-
*
30-
* @param {*} state Current game state
31-
* @param {Boolean} maximize Defines if the result should be maximized or minimized
32-
* @param {Number} depth Defines the maximum depth search
33-
* @param {Number} alpha Maximum score that the minimizing player is assured
34-
* @param {Number} beta Minimum score that the maximizing player is assured
35-
* @param {Function} getPossibleNextStatesFn Function which returns all possible next moves with states .
36-
* @param {Function} isGameOverFn Function which returns if game is over.
37-
* @param {Function} getScoreFn Function which returns score.
38-
* @return {{score: Number, move: *}} which contains the minimum coins from the given
39-
* list, required for the change.
6+
* @param {Function} getPossibleNextStatesFn Function which returns all possible next moves with states .
7+
* @param {Function} isGameOverFn Function which returns if game is over.
8+
* @param {Function} getScoreFn Function which returns score.
9+
* @return {Function} minimax function
4010
*/
41-
function minimax(
42-
state,
43-
maximize,
44-
depth,
45-
alpha,
46-
beta,
11+
function minimaxBuilder(
4712
getPossibleNextStatesFn,
4813
isGameOverFn,
4914
getScoreFn
5015
) {
51-
if (depth === 0 || isGameOverFn(state)) {
52-
const score = getScoreFn(state);
53-
return {score, move: null};
54-
}
16+
/**
17+
* Minimax (sometimes MinMax, MM[1] or saddle point[2]) is a decision rule used in artificial intelligence,
18+
* decision theory, game theory, statistics, and philosophy for minimizing the possible loss for a worst case (maximum loss) scenario.
19+
* Optimized with alpha-beta pruning.
20+
* {@link https://en.wikipedia.org/wiki/Minimax}
21+
* {@link https://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning}
22+
*
23+
* @public
24+
* @module others/minimax
25+
*
26+
* @example
27+
*
28+
* var miniMax =
29+
* require('path-to-algorithms/src/others/minimax').minimax;
30+
* var result = minimax(
31+
* [1, 2, 3],
32+
* true,
33+
* 5,
34+
* -Infinity,
35+
* Infinity,
36+
* state => ({ move: 0, state: [2, 3, 4] }),
37+
* state => state[1] < 3,
38+
* state => state[1]
39+
* );
40+
*
41+
* @param {*} state Current game state
42+
* @param {Boolean} maximize Defines if the result should be maximized or minimized
43+
* @param {Number} depth Defines the maximum depth search
44+
* @param {Number} alpha Maximum score that the minimizing player is assured
45+
* @param {Number} beta Minimum score that the maximizing player is assured
46+
* @return {{score: Number, move: *}} which contains the minimum coins from the given
47+
* list, required for the change.
48+
*/
49+
const minimax = (
50+
state,
51+
maximize,
52+
depth,
53+
alpha,
54+
beta
55+
) => {
56+
if (depth === 0 || isGameOverFn(state)) {
57+
const score = getScoreFn(state);
58+
return {score, move: null};
59+
}
5560

56-
const possibleMoveResults = getPossibleNextStatesFn(state);
61+
const possibleMoveResults = getPossibleNextStatesFn(state);
5762

58-
if (maximize) {
63+
if (maximize) {
5964

60-
let maxResult = {score: -Infinity, move: null};
65+
let maxResult = {score: -Infinity, move: null};
6166

62-
for (const next of possibleMoveResults) {
63-
const result = minimax(
64-
next.state,
65-
false,
66-
depth - 1,
67-
alpha,
68-
beta,
69-
getPossibleNextStatesFn,
70-
isGameOverFn,
71-
getScoreFn
72-
);
67+
for (const next of possibleMoveResults) {
68+
const result = minimax(
69+
next.state,
70+
false,
71+
depth - 1,
72+
alpha,
73+
beta,
74+
);
7375

74-
if (result.score > maxResult.score) {
75-
maxResult = {score: result.score, move: next.move};
76-
}
76+
if (result.score > maxResult.score) {
77+
maxResult = {score: result.score, move: next.move};
78+
}
7779

78-
alpha = Math.max(alpha, result.score);
80+
alpha = Math.max(alpha, result.score);
7981

80-
if (alpha >= beta) {
81-
break;
82+
if (alpha >= beta) {
83+
break;
84+
}
8285
}
83-
}
8486

85-
return maxResult;
86-
} else {
87-
let minResult = {score: Infinity, move: null};
88-
89-
for (const next of possibleMoveResults) {
90-
const result = minimax(
91-
next.state,
92-
true,
93-
depth - 1,
94-
alpha,
95-
beta,
96-
getPossibleNextStatesFn,
97-
isGameOverFn,
98-
getScoreFn
99-
);
100-
101-
if (result.score < minResult.score) {
102-
minResult = {score: result.score, move: next.move};
103-
}
87+
return maxResult;
88+
} else {
89+
let minResult = {score: Infinity, move: null};
10490

105-
beta = Math.min(beta, result.score);
91+
for (const next of possibleMoveResults) {
92+
const result = minimax(
93+
next.state,
94+
true,
95+
depth - 1,
96+
alpha,
97+
beta,
98+
);
10699

107-
if (beta <= alpha) {
108-
break;
100+
if (result.score < minResult.score) {
101+
minResult = {score: result.score, move: next.move};
102+
}
103+
104+
beta = Math.min(beta, result.score);
105+
106+
if (beta <= alpha) {
107+
break;
108+
}
109109
}
110-
}
111110

112-
return minResult;
111+
return minResult;
112+
}
113113
}
114+
115+
return minimax;
114116
}
115117

116-
exports.minimax = minimax;
118+
exports.minimaxBuilder = minimaxBuilder;
117119

118120
})(typeof window === 'undefined' ? module.exports : window);

test/others/minimax.spec.js

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
var minimax = require('../../src/others/minimax.js').minimax;
1+
const minimaxBuilder = require('../../src/others/minimax.js').minimaxBuilder;
22

33
describe('Minimax with tic tac toe', function () {
44
'use strict';
@@ -14,33 +14,28 @@ describe('Minimax with tic tac toe', function () {
1414
}));
1515
}
1616

17-
function minimaxFor(player, state) {
18-
const getScoreFn = player === 'x'
19-
? state => game.getScore(state).x - game.getScore(state).o
20-
: state => game.getScore(state).o - game.getScore(state).x;
21-
22-
return minimax(
23-
state,
24-
true,
25-
5,
26-
-Infinity,
27-
Infinity,
28-
getAllNextStates,
29-
state => game.isGameOver(state),
30-
getScoreFn
31-
);
32-
}
17+
const minimaxForX = minimaxBuilder(
18+
getAllNextStates,
19+
state => game.isGameOver(state),
20+
state => game.getScore(state).x - game.getScore(state).o
21+
)
22+
23+
const minimaxForO = minimaxBuilder(
24+
getAllNextStates,
25+
state => game.isGameOver(state),
26+
state => game.getScore(state).o - game.getScore(state).x
27+
)
3328

3429
it('should be defined', function () {
35-
expect(minimax).toBeDefined();
30+
expect(minimaxBuilder).toBeDefined();
3631
});
3732

3833
it('should win versus dumb agent as first player', function () {
3934
let state = game.newState('x');
4035

4136
while (!game.isGameOver(state)) {
4237
if (state.turn === 'x') {
43-
state = game.nextState(state, minimaxFor(state.turn, state).move);
38+
state = game.nextState(state, minimaxForX(state, true, 5, -Infinity, Infinity).move);
4439
} else {
4540
const move = game.emptyCells(state)[0];
4641
state = game.nextState(state, move);
@@ -56,7 +51,7 @@ describe('Minimax with tic tac toe', function () {
5651

5752
while (!game.isGameOver(state)) {
5853
if (state.turn === 'o') {
59-
state = game.nextState(state, minimaxFor(state.turn, state).move);
54+
state = game.nextState(state, minimaxForO(state, true, 5, -Infinity, Infinity).move);
6055
} else {
6156
const move = game.emptyCells(state)[0];
6257
state = game.nextState(state, move);
@@ -72,7 +67,11 @@ describe('Minimax with tic tac toe', function () {
7267
let state = game.newState('x');
7368

7469
while (!game.isGameOver(state)) {
75-
state = game.nextState(state, minimaxFor(state.turn, state).move);
70+
if (state.turn === 'o') {
71+
state = game.nextState(state, minimaxForO(state, true, 5, -Infinity, Infinity).move);
72+
} else {
73+
state = game.nextState(state, minimaxForX(state, true, 5, -Infinity, Infinity).move);
74+
}
7675
}
7776
expect(game.isGameOver(state)).toBe(true);
7877
expect(game.getScore(state)).toEqual({x: 0, o: 0});
@@ -149,13 +148,10 @@ function ticTacToe() {
149148
}
150149

151150
function nextState(state, move) {
152-
const board = state.board;
151+
const newBoard = state.board.map(row => row.slice());
152+
newBoard[move.y][move.x] = state.turn;
153153
return {
154-
board: [
155-
...board.slice(0, move.y),
156-
[...board[move.y].slice(0, move.x), state.turn, ...board[move.y].slice(move.x + 1)],
157-
...board.slice(move.y + 1)
158-
],
154+
board: newBoard,
159155
turn: state.turn === 'x' ? 'o' : 'x',
160156
};
161157
}

0 commit comments

Comments
 (0)