Skip to content

Commit 08404e2

Browse files
committed
change MCTS visualization to table format
1 parent 2d931e9 commit 08404e2

13 files changed

Lines changed: 257 additions & 276 deletions

File tree

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@
131131
"superagent": "^3.8.3",
132132
"supertest": "^3.1.0",
133133
"svelte": "^3.12.1",
134+
"svelte-icons": "^1.1.0",
134135
"ts-jest": "^24.0.2",
135136
"ts-transformer-imports": "^0.4.3",
136137
"ttypescript": "^1.5.7",
@@ -168,7 +169,7 @@
168169
"^.+\\.svelte$": "jest-transform-svelte"
169170
},
170171
"transformIgnorePatterns": [
171-
"node_modules/(?!(boardgame.io|flatted)/)"
172+
"node_modules/(?!(boardgame.io|flatted|svelte-icons)/)"
172173
],
173174
"testPathIgnorePatterns": [
174175
"examples/",

src/ai/ai.test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,11 @@ describe('MCTSBot', () => {
379379
expect(bot.opts()['iterations'].value).toBe(1);
380380
});
381381

382+
test('setOpt works on invalid key', () => {
383+
const bot = new RandomBot({ game: TicTacToe, enumerate: jest.fn() });
384+
bot.setOpt('unknown', 1);
385+
});
386+
382387
test('functions', () => {
383388
const state = InitializeGame({ game: TicTacToe });
384389

src/ai/bot.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ export class Bot {
3232
}
3333

3434
setOpt(key, value) {
35-
this._opts[key].value = value;
35+
if (key in this._opts) {
36+
this._opts[key].value = value;
37+
}
3638
}
3739

3840
opts() {

src/ai/mcts-bot.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,11 @@ export class MCTSBot extends Bot {
246246
this.backpropagate(child, result);
247247
this.iterationCounter++;
248248
}
249-
this.iterationCallback(this.iterationCounter, numIterations);
249+
this.iterationCallback({
250+
iterationCounter: this.iterationCounter,
251+
numIterations,
252+
metadata: root,
253+
});
250254
};
251255

252256
this.iterationCounter = 0;

src/client/debug/Debug.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
7373
.secondary-pane {
7474
background: #fefefe;
75+
overflow-y: scroll;
7576
}
7677
7778
.debug-panel :global(button, select) {

src/client/debug/ai/AI.svelte

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,26 @@
1717
'Random': RandomBot,
1818
};
1919
20+
let debug = false;
2021
let progress = null;
2122
let iterationCounter = 0;
22-
const iterationCallback = (counter, numIterations) => {
23-
iterationCounter = counter;
24-
progress = counter / numIterations;
23+
let metadata = null;
24+
const iterationCallback = ({ iterationCounter: c, numIterations, metadata: m }) => {
25+
iterationCounter = c;
26+
progress = c / numIterations;
27+
metadata = m;
28+
29+
if (debug && metadata) {
30+
secondaryPane.set({ component: MCTS, metadata });
31+
}
32+
}
33+
34+
function OnDebug() {
35+
if (debug && metadata) {
36+
secondaryPane.set({ component: MCTS, metadata });
37+
} else {
38+
secondaryPane.set(null);
39+
}
2540
}
2641
2742
let bot;
@@ -46,19 +61,27 @@
4661
});
4762
bot.setOpt('async', true);
4863
botAction = null;
64+
metadata = null;
65+
secondaryPane.set(null);
4966
iterationCounter = 0;
5067
}
5168
5269
async function Step() {
5370
botAction = null;
71+
metadata = null;
5472
iterationCounter = 0;
73+
5574
const t = await _Step(client, bot);
56-
botAction = t.payload.type;
57-
botActionArgs = t.payload.args;
75+
76+
if (t) {
77+
botAction = t.payload.type;
78+
botActionArgs = t.payload.args;
79+
}
5880
}
5981
6082
function Simulate(iterations = 10000, sleepTimeout = 100) {
6183
botAction = null;
84+
metadata = null;
6285
iterationCounter = 0;
6386
const step = async () => {
6487
for (let i = 0; i < iterations; i++) {
@@ -71,27 +94,16 @@
7194
return step();
7295
}
7396
74-
function DebugLastMove() {
75-
const { log } = client.getState();
76-
const renderedLogEntries = log.filter(e => e.action.type == MAKE_MOVE);
77-
78-
if (renderedLogEntries.length > 0) {
79-
const index = renderedLogEntries.length - 1;
80-
const { metadata } = renderedLogEntries[index].action.payload;
81-
if (metadata) {
82-
secondaryPane.set({ component: MCTS, metadata });
83-
}
84-
}
85-
}
86-
8797
function Exit() {
8898
client.overrideGameState(null);
8999
secondaryPane.set(null);
100+
debug = false;
90101
}
91102
92103
function Reset() {
93104
client.reset();
94105
botAction = null;
106+
metadata = null;
95107
iterationCounter = 0;
96108
Exit();
97109
}
@@ -116,6 +128,15 @@
116128
h3 {
117129
text-transform: uppercase;
118130
}
131+
132+
label {
133+
font-weight: bold;
134+
color: #999;
135+
}
136+
137+
input[type='checkbox'] {
138+
vertical-align: middle;
139+
}
119140
</style>
120141

121142
<svelte:window on:keydown={OnKeyDown}/>
@@ -147,6 +168,8 @@
147168
{#if Object.keys(bot.opts()).length}
148169
<section>
149170
<h3>Options</h3>
171+
<label>debug</label>
172+
<input type=checkbox bind:checked={debug} on:change={OnDebug}>
150173
<Options bot={bot}/>
151174
</section>
152175
{/if}
@@ -161,9 +184,6 @@
161184
{#if botAction}
162185
<li>Action: {botAction}</li>
163186
<li>Args: {JSON.stringify(botActionArgs)}</li>
164-
<p>
165-
<button on:click={DebugLastMove}>Debug</button>
166-
</p>
167187
{/if}
168188
</section>
169189
{/if}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<script>
2+
export let action;
3+
4+
let text;
5+
6+
$: {
7+
const { type, args } = action.payload;
8+
const argsFormatted = (args || []).join(',');
9+
text = `${type}(${argsFormatted})`;
10+
}
11+
</script>
12+
13+
<style>
14+
div {
15+
white-space: nowrap;
16+
text-overflow: ellipsis;
17+
overflow: hidden;
18+
max-width: 500px;
19+
}
20+
</style>
21+
22+
<div alt={text}>{text}</div>

src/client/debug/mcts/MCTS.svelte

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,78 @@
11
<script>
22
export let metadata;
3-
import Tree from './Tree.svelte';
3+
4+
import Arrow from 'svelte-icons/fa/FaArrowAltCircleDown.svelte'
5+
6+
let nodes = [];
7+
let prevNodes = [];
8+
let preview = null;
9+
10+
import Table from './Table.svelte';
11+
12+
$: {
13+
prevNodes = [];
14+
nodes = [{ node: metadata }];
15+
}
16+
17+
function SelectNode({ node, selectedIndex }, i) {
18+
preview = null;
19+
nodes[i].selectedIndex = selectedIndex;
20+
nodes = [...nodes.slice(0, i + 1), { node }];
21+
}
22+
23+
function PreviewNode({ node }, i) {
24+
preview = node;
25+
}
426
</script>
527

6-
<Tree root={metadata} />
28+
<style>
29+
.visualizer {
30+
display: flex;
31+
flex-direction: column;
32+
align-items: center;
33+
padding: 50px;
34+
}
35+
36+
.preview {
37+
opacity: 0.5;
38+
}
39+
40+
.icon {
41+
color: #777;
42+
width: 32px;
43+
height: 32px;
44+
margin-bottom: 20px;
45+
}
46+
</style>
47+
48+
<div class="visualizer">
49+
{#each nodes as { node, selectedIndex }, i}
50+
{#if i !== 0}
51+
<div class="icon">
52+
<Arrow/>
53+
</div>
54+
{/if}
55+
56+
<section>
57+
{#if i === nodes.length - 1}
58+
<Table on:select={e => SelectNode(e.detail, i)}
59+
on:preview={e => PreviewNode(e.detail, i)}
60+
root={node}/>
61+
{:else}
62+
<Table on:select={e => SelectNode(e.detail, i)}
63+
root={node}
64+
selectedIndex={selectedIndex}/>
65+
{/if}
66+
</section>
67+
{/each}
68+
69+
{#if preview}
70+
<div class="icon">
71+
<Arrow/>
72+
</div>
73+
74+
<section class="preview">
75+
<Table root={preview}/>
76+
</section>
77+
{/if}
78+
</div>

src/client/debug/mcts/Node.svelte

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
 (0)