-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Minimax implementation #171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@krzysztof-grzybek looks good! I'll add a reminder for myself to take a look at the PR in the next few days. |
test/others/minimax.spec.js
Outdated
const board = state.board; | ||
return { | ||
board: [ | ||
...board.slice(0, move.y), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit hard to read. Something like this for updating the board should be easier to understand:
const newBoard = state.board.map(row => row.slice());
newBoard[move.x][move.y] = state.turn;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clever! Fixed.
src/others/minimax.js
Outdated
* @return {{score: Number, move: *}} which contains the minimum coins from the given | ||
* list, required for the change. | ||
*/ | ||
function minimax( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are some parameters which don't change over time:
getPossibleNextStatesFn
isGameOverFn
getScoreFn
We can have a higher-order (builder) function, which accepts them and returns a new function, which does the rest:
const minimaxBuilder = (getPossibleNextStatesFn, isGameOverFn, getScoreFn) => {
const minimax = (state, maximize, depth, alpha, beta) => {
// ...
};
return minimax;
};
This way we'll change the public API a bit:
const minimax = minimaxBuilder(...);
const result = minimax(...);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea! Fixed.
src/others/minimax.js
Outdated
|
||
alpha = Math.max(alpha, result.score); | ||
|
||
if (alpha >= beta) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this used for an optimization so we stop the loop earlier?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we stop when alpha === beta
can't we miss later cases when alpha
is larger than beta
, which will lead to a better outcome?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's right, it's an optimization called "Alpha-beta pruning".
That's not the case. If alpha is larger then beta, then we know that this whole leaf won't be chosen anyway. I couldn't explain it better than 20 seconds of this video.
test/others/minimax.spec.js
Outdated
@@ -0,0 +1,170 @@ | |||
var minimax = require('../../src/others/minimax.js').minimax; | |||
|
|||
describe('Minimax with tic tac toe', function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a fantastic example! As you suggested in your comment, it's leaning a bit towards an integration test.
I think, having a simpler game is fine as well. This way we won't need that much logic to setup our tests and they'll be less error prone. Consider a game where each next step is a binary decision, and the leafs of the binary tree determine which player is the winner.
I really like this example, so I'd love to keep it, and also add another test group which tests the minimax
example with a simpler binary game.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm glad you like it!
Sounds reasonable. I'm not sure if I follow the idea of the simpler game. What do you mean by the leafs of the binary tree determine which player is the winner
? Should I create some binary tree with one "winning leaf"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't have to be a real game, it can be any game. My proposal is for something like this:
o
/ \
o o
/ \ / \
1 -1 1 -1
If we get 1
player "A" wins, otherwise, if we get -1
player "B" wins. It's pretty much pre-defined, depends on who would start first. It'll be easier to reason about IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I got it. Indeed, it's much simpler.
src/others/minimax.js
Outdated
|
||
beta = Math.min(beta, result.score); | ||
|
||
if (beta <= alpha) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left few comments. Great work!
Co-Authored-By: Minko Gechev <[email protected]>
00f0d27
to
c995e08
Compare
c995e08
to
a6e4eb1
Compare
I fixed all the issues. |
Fixes #167
Hi Minko,
As I started implementing, I turned out that this algorithm can't be tested without any game. It just needs some game rules to decide which move will be the best.
To fix that, I implemented a simple tic tac toe game.
Another weird looking thing might be the fact, that algorithm function receives 3 function arguments. This is needed because of the same reasons.
I realize that this might be odd. If You think that it doesn't fit this repo, that's fine.