diff --git a/examples/react-web/src/tic-tac-toe/advanced-ai.js b/examples/react-web/src/tic-tac-toe/advanced-ai.js new file mode 100644 index 000000000..89cde9741 --- /dev/null +++ b/examples/react-web/src/tic-tac-toe/advanced-ai.js @@ -0,0 +1,115 @@ +/* + * Copyright 2017 The boardgame.io Authors. + * + * Use of this source code is governed by a MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + */ + +import React, { useEffect, useState } from 'react'; +import { Client as PlainJSClient } from 'boardgame.io/client'; +import { Client } from 'boardgame.io/react'; +import { Local } from 'boardgame.io/multiplayer'; +import { MCTSBot, Step } from 'boardgame.io/ai'; +import TicTacToe from './game'; +import Board from './board'; + +const App = Client({ + game: TicTacToe, + board: Board, + debug: false, + // Use Local transport for communication with bots. + multiplayer: Local(), +}); + +/** + * Component that controls and runs a custom bot instance. + */ +const BotControls = ({ playerID, matchID }) => { + const difficulties = { + easy: { + iterations: 1, + playoutDepth: 1, + }, + hard: { + iterations: 1000, + playoutDepth: 50, + }, + }; + const [difficulty, setDifficulty] = useState('easy'); + const [client, setClient] = useState(); + + // Create a plain Javascript boardgame.io client on mount. + useEffect(() => { + const newClient = PlainJSClient({ + game: TicTacToe, + debug: false, + multiplayer: Local(), + matchID, + playerID, + }); + newClient.start(); + setClient(newClient); + // Clean up client on unmount. + return () => newClient.stop(); + }, []); + + // Update the client subscription when bot difficulty changes. + useEffect(() => { + if (!client) return; + // Subscribe to the client with a function that will run AI on a bot + // player’s turn. + return client.subscribe(state => { + if (!state) return; + if (state.ctx.currentPlayer === playerID) { + const { iterations, playoutDepth } = difficulties[difficulty]; + const bot = new MCTSBot({ + game: TicTacToe, + enumerate: TicTacToe.ai.enumerate, + iterations, + playoutDepth, + }); + // Delay AI stepping by a tick to allow React to render before the + // main thread gets blocked by AI iterations. + setTimeout(() => Step(client, bot), 0); + } + }); + }, [client, difficulty]); + + // Render AI difficulty toggle buttons. + return ( +

+ AI Difficulty:{' '} + + +

+ ); +}; + +const AdvancedAI = () => { + return ( +
+

Advanced AI

+

+ This example shows how to use a custom bot instance to play against a + local player. +
+ In the future, this will be made much simpler! +

+ + +
+ ); +}; + +export default AdvancedAI; diff --git a/examples/react-web/src/tic-tac-toe/bots.js b/examples/react-web/src/tic-tac-toe/bots.js new file mode 100644 index 000000000..025f42a92 --- /dev/null +++ b/examples/react-web/src/tic-tac-toe/bots.js @@ -0,0 +1,34 @@ +/* + * Copyright 2017 The boardgame.io Authors. + * + * Use of this source code is governed by a MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + */ + +import React from 'react'; +import { Client } from 'boardgame.io/react'; +import { Local } from 'boardgame.io/multiplayer'; +import { MCTSBot } from 'boardgame.io/ai'; +import TicTacToe from './game'; +import Board from './board'; + +const App = Client({ + game: TicTacToe, + board: Board, + debug: false, + multiplayer: Local({ + bots: { + '1': MCTSBot, + }, + }), +}); + +const Bots = () => ( +
+

Singleplayer vs AI

+ +
+); + +export default Bots; diff --git a/examples/react-web/src/tic-tac-toe/index.js b/examples/react-web/src/tic-tac-toe/index.js index fdc97be2a..563582cd2 100644 --- a/examples/react-web/src/tic-tac-toe/index.js +++ b/examples/react-web/src/tic-tac-toe/index.js @@ -10,6 +10,8 @@ import Singleplayer from './singleplayer'; import Multiplayer from './multiplayer'; import Spectator from './spectator'; import Authenticated from './authenticated'; +import Bots from './bots'; +import AdvancedAI from './advanced-ai'; const routes = [ { @@ -32,6 +34,16 @@ const routes = [ text: 'Spectator', component: Spectator, }, + { + path: '/bots', + text: 'Singleplayer vs AI', + component: Bots, + }, + { + path: '/advanced-ai', + text: 'Advanced AI', + component: AdvancedAI, + }, ]; export default { routes };