Skip to content

Commit

Permalink
basic support for objective-based AI
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolodavis committed Jul 28, 2018
1 parent 6a8b657 commit 36fc47f
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 1 deletion.
32 changes: 31 additions & 1 deletion src/ai/bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,14 @@ export class RandomBot extends Bot {
}

export class MCTSBot extends Bot {
constructor({ enumerate, seed, game, iterations, playoutDepth }) {
constructor({ enumerate, seed, objectives, game, iterations, playoutDepth }) {
super({ enumerate, seed });

if (objectives === undefined) {
objectives = () => ({});
}

this.objectives = objectives;
this.reducer = CreateGameReducer({ game });
this.iterations = iterations || 1000;
this.playoutDepth = playoutDepth || 50;
Expand All @@ -116,12 +122,15 @@ export class MCTSBot extends Bot {
const { G, ctx } = state;

let actions = [];
let objectives = [];

if (playerID !== undefined) {
actions = this.enumerate(G, ctx, playerID);
objectives = this.objectives(G, ctx, playerID);
} else {
for (let playerID of ctx.actionPlayers) {
actions = actions.concat(this.enumerate(G, ctx, playerID));
objectives = objectives.concat(this.objectives(G, ctx, playerID));
}
}

Expand All @@ -134,6 +143,8 @@ export class MCTSBot extends Bot {
parentAction,
// Unexplored actions.
actions,
// Current objectives.
objectives,
// Children of the node.
children: [],
// Number of simulations that pass through this node.
Expand Down Expand Up @@ -202,6 +213,21 @@ export class MCTSBot extends Bot {
const { G, ctx } = state;
const moves = this.enumerate(G, ctx, ctx.actionPlayers[0]);

// Check if any objectives are met.
const objectives = this.objectives(G, ctx);
const score = Object.keys(objectives).reduce((score, key) => {
const objective = objectives[key];
if (objective.checker(G, ctx)) {
return score + objective.weight;
}
return score;
}, 0.0);

// If so, stop and return the score.
if (score > 0) {
return { score };
}

if (!moves || moves.length == 0) {
return undefined;
}
Expand All @@ -217,6 +243,10 @@ export class MCTSBot extends Bot {
backpropagate(node, result = {}) {
node.visits++;

if (result.score !== undefined) {
node.value += result.score;
}

if (result.draw === true) {
node.value += 0.5;
}
Expand Down
26 changes: 26 additions & 0 deletions src/ai/bot.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,30 @@ describe('MCTSBot', () => {
expect(endState.ctx.gameover).toEqual({ draw: true });
}
});

test('objectives', () => {
const objectives = () => ({
'play-on-square-0': {
checker: G => G.cells[0] !== null,
weight: 10.0,
},
});

const reducer = CreateGameReducer({ game: TicTacToe });
const state = reducer(undefined, { type: 'init' });

for (let i = 0; i < 10; i++) {
const bot = new MCTSBot({
iterations: 200,
seed: i,
game: TicTacToe,
enumerate,
objectives,
playerID: '0',
});

const { action } = bot.play(state, '0');
expect(action.payload.args).toEqual([0]);
}
});
});

1 comment on commit 36fc47f

@nicolodavis
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#7

Please sign in to comment.