Skip to content

Commit

Permalink
make bot play functions async
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolodavis committed Nov 21, 2019
1 parent 457b29d commit 7d22a47
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 33 deletions.
4 changes: 2 additions & 2 deletions src/ai/ai.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export async function Step(client, bot) {
* @param {...object} bots - An array of bots.
* @param {...object} state - The game state to start from.
*/
export function Simulate({ game, bots, state, depth }) {
export async function Simulate({ game, bots, state, depth }) {
if (depth === undefined) depth = 10000;
const reducer = CreateGameReducer({ game, numPlayers: state.ctx.numPlayers });

Expand All @@ -53,7 +53,7 @@ export function Simulate({ game, bots, state, depth }) {
}

const bot = bots instanceof Bot ? bots : bots[playerID];
const t = bot.play(state, playerID);
const t = await bot.play(state, playerID);

if (!t.action) {
break;
Expand Down
44 changes: 28 additions & 16 deletions src/ai/ai.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,16 +153,20 @@ describe('Simulate', () => {
'1': new RandomBot({ seed: 'test', enumerate }),
};

test('multiple bots', () => {
test('multiple bots', async () => {
const state = InitializeGame({ game: TicTacToe });
const { state: endState } = Simulate({ game: TicTacToe, bots, state });
const { state: endState } = await Simulate({
game: TicTacToe,
bots,
state,
});
expect(endState.ctx.gameover).not.toBe(undefined);
});

test('single bot', () => {
test('single bot', async () => {
const bot = new RandomBot({ seed: 'test', enumerate });
const state = InitializeGame({ game: TicTacToe });
const { state: endState } = Simulate({
const { state: endState } = await Simulate({
game: TicTacToe,
bots: bot,
state,
Expand All @@ -171,7 +175,7 @@ describe('Simulate', () => {
expect(endState.ctx.gameover).not.toBe(undefined);
});

test('with activePlayers', () => {
test('with activePlayers', async () => {
const game = Game({
moves: {
A: G => {
Expand All @@ -190,7 +194,7 @@ describe('Simulate', () => {
});

const state = InitializeGame({ game });
const { state: endState } = Simulate({
const { state: endState } = await Simulate({
game,
bots: bot,
state,
Expand Down Expand Up @@ -233,15 +237,15 @@ describe('Bot', () => {
});

describe('MCTSBot', () => {
test('game that never ends', () => {
test('game that never ends', async () => {
const game = {};
const state = InitializeGame({ game });
const bot = new MCTSBot({ seed: 'test', game, enumerate: () => [] });
const { state: endState } = Simulate({ game, bots: bot, state });
const { state: endState } = await Simulate({ game, bots: bot, state });
expect(endState.ctx.turn).toBe(1);
});

test('RandomBot vs. MCTSBot', () => {
test('RandomBot vs. MCTSBot', async () => {
const bots = {
'0': new RandomBot({ seed: 'test', enumerate, playerID: '0' }),
'1': new MCTSBot({
Expand All @@ -257,12 +261,16 @@ describe('MCTSBot', () => {

for (let i = 0; i < 5; i++) {
const state = initialState;
const { state: endState } = Simulate({ game: TicTacToe, bots, state });
const { state: endState } = await Simulate({
game: TicTacToe,
bots,
state,
});
expect(endState.ctx.gameover).not.toEqual({ winner: '0' });
}
});

test('MCTSBot vs. MCTSBot', () => {
test('MCTSBot vs. MCTSBot', async () => {
const initialState = InitializeGame({ game: TicTacToe });
const iterations = 400;

Expand All @@ -285,12 +293,16 @@ describe('MCTSBot', () => {
}),
};
const state = initialState;
const { state: endState } = Simulate({ game: TicTacToe, bots, state });
const { state: endState } = await Simulate({
game: TicTacToe,
bots,
state,
});
expect(endState.ctx.gameover).toEqual({ draw: true });
}
});

test('with activePlayers', () => {
test('with activePlayers', async () => {
const game = Game({
setup: () => ({ moves: 0 }),
moves: {
Expand All @@ -311,7 +323,7 @@ describe('MCTSBot', () => {
});

const state = InitializeGame({ game });
const { state: endState } = Simulate({
const { state: endState } = await Simulate({
game,
bots: bot,
state,
Expand All @@ -320,7 +332,7 @@ describe('MCTSBot', () => {
expect(endState.ctx.gameover).not.toBe(undefined);
});

test('objectives', () => {
test('objectives', async () => {
const objectives = () => ({
'play-on-square-0': {
checker: G => G.cells[0] !== null,
Expand All @@ -340,7 +352,7 @@ describe('MCTSBot', () => {
playerID: '0',
});

const { action } = bot.play(state, '0');
const { action } = await bot.play(state, '0');
expect(action.payload.args).toEqual([0]);
}
});
Expand Down
30 changes: 16 additions & 14 deletions src/ai/mcts-bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,23 +200,25 @@ export class MCTSBot extends Bot {
iterations = this.iterations(state.G, state.ctx);
}

for (let i = 0; i < iterations; i++) {
const leaf = this.select(root);
const child = this.expand(leaf);
const result = this.playout(child);
this.backpropagate(child, result);
}
return new Promise(resolve => {
for (let i = 0; i < iterations; i++) {
const leaf = this.select(root);
const child = this.expand(leaf);
const result = this.playout(child);
this.backpropagate(child, result);
}

let selectedChild = null;
for (const child of root.children) {
if (selectedChild == null || child.visits > selectedChild.visits) {
selectedChild = child;
let selectedChild = null;
for (const child of root.children) {
if (selectedChild == null || child.visits > selectedChild.visits) {
selectedChild = child;
}
}
}

const action = selectedChild && selectedChild.parentAction;
const metadata = root;
const action = selectedChild && selectedChild.parentAction;
const metadata = root;

return { action, metadata };
resolve({ action, metadata });
});
}
}
2 changes: 1 addition & 1 deletion src/ai/random-bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ import { Bot } from './bot';
export class RandomBot extends Bot {
play({ G, ctx }, playerID) {
const moves = this.enumerate(G, ctx, playerID);
return { action: this.random(moves) };
return Promise.resolve({ action: this.random(moves) });
}
}

0 comments on commit 7d22a47

Please sign in to comment.