microzero-play / index.html
ARotting's picture
Publish Playable neural MCTS Tic-Tac-Toe agent
c7928dd verified
Raw
History Blame Contribute Delete
11.5 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Microzero Play</title>
<style>
:root { color-scheme: dark; font-family: Inter, ui-sans-serif, system-ui; }
* { box-sizing: border-box; }
body { margin: 0; min-height: 100vh; background: #060817; color: #edf4ff; }
canvas { position: fixed; inset: 0; width: 100%; height: 100%; opacity: .55; }
main { position: relative; z-index: 1; width: min(1080px, 92vw); margin: auto;
padding: 72px 0 96px; }
.eyebrow { color: #73e6ff; letter-spacing: .18em; text-transform: uppercase;
font-size: .75rem; font-weight: 800; }
h1 { font-size: clamp(3rem, 8vw, 7rem); line-height: .9; margin: 14px 0 24px;
background: linear-gradient(120deg,#fff,#74e7ff 55%,#b48cff);
-webkit-background-clip: text; color: transparent; }
.lead { max-width: 760px; color: #b8c7e6; font-size: 1.2rem; line-height: 1.65; }
.actions { display: flex; flex-wrap: wrap; gap: 12px; margin: 30px 0 48px; }
a { color: inherit; }
.button { padding: 12px 18px; border-radius: 999px; text-decoration: none;
background: #eaf8ff; color: #07101c; font-weight: 800; }
.button.alt { background: #171d38cc; color: #dce8ff; border: 1px solid #415078; }
.grid { display: grid; grid-template-columns: 1.1fr .9fr; gap: 20px; }
.card { border: 1px solid #344269; background: #0c1128dd; border-radius: 24px;
padding: 24px; backdrop-filter: blur(18px); box-shadow: 0 24px 80px #0008; }
h2 { margin-top: 0; }
pre { white-space: pre-wrap; word-break: break-word; color: #a9bddf;
max-height: 520px; overflow: auto; }
ul { max-height: 520px; overflow: auto; padding-left: 1.2rem; color: #a9bddf; }
li { margin: 8px 0; }
input { width: 100%; padding: 12px; border-radius: 12px; border: 1px solid #344269;
background: #070b1a; color: white; margin-bottom: 12px; }
@media (max-width: 780px) { .grid { grid-template-columns: 1fr; } }
</style>
</head>
<body>
<canvas id="field"></canvas>
<main>
<div class="eyebrow">Jacob Garcia · Hugging Face Model Foundry</div>
<h1>Microzero Play</h1>
<p class="lead">Playable neural MCTS Tic-Tac-Toe agent. This showcase backs up the
trained artifacts, measured evaluation, and complete runnable source.</p>
<div class="actions">
<a class="button" href="https://huggingface.co/spaces/ARotting/microzero-play/tree/main">Explore every file</a>
<a class="button alt" href="https://huggingface.co/ARotting">View the full foundry</a>
</div>
<div class="grid">
<section class="card">
<h2>Verified project card</h2>
<pre># MicroZero
MicroZero is a tiny AlphaZero-style Tic-Tac-Toe agent. A shared neural trunk predicts
both move priors and position value. Monte Carlo Tree Search converts those estimates
into stronger self-play targets, while terminal outcomes supervise value learning.
The training loop uses:
- neural-guided PUCT search;
- root Dirichlet exploration;
- policy targets from MCTS visit counts;
- terminal win/draw/loss value targets;
- all eight square-board symmetries;
- a bounded replay buffer.
Evaluation alternates playing first and second against random and exact minimax
opponents.
## Verified results
- 7,626 trainable parameters;
- 980 neural-MCTS self-play games;
- 24,000-example bounded replay buffer after symmetry augmentation;
- 384 wins, 16 draws, and zero losses over 400 games against random play;
- 200 draws and zero losses over 200 games against exact minimax.
The exact opponent is used only for final evaluation, not as a source of training
targets. Full metrics and the per-iteration learning history are stored in
`artifacts/microzero/evaluation.json`.
## Reproduce
```powershell
uv run python projects/microzero/train.py
```
</pre>
<h2>Evaluation snapshot</h2>
<pre>{
&quot;model&quot;: &quot;MicroZero&quot;,
&quot;parameters&quot;: 7626,
&quot;self_play_iterations&quot;: 14,
&quot;self_play_games&quot;: 980,
&quot;final_replay_examples&quot;: 24000,
&quot;training_history&quot;: [
{
&quot;iteration&quot;: 1,
&quot;replay_examples&quot;: 4128,
&quot;x_win_rate&quot;: 0.5428571428571428,
&quot;o_win_rate&quot;: 0.14285714285714285,
&quot;draw_rate&quot;: 0.3142857142857143,
&quot;average_game_moves&quot;: 7.371428571428571,
&quot;policy_loss&quot;: 2.1499555636854732,
&quot;value_loss&quot;: 0.5162482239744243
},
{
&quot;iteration&quot;: 2,
&quot;replay_examples&quot;: 8128,
&quot;x_win_rate&quot;: 0.5142857142857142,
&quot;o_win_rate&quot;: 0.0,
&quot;draw_rate&quot;: 0.4857142857142857,
&quot;average_game_moves&quot;: 7.142857142857143,
&quot;policy_loss&quot;: 1.9038925133645535,
&quot;value_loss&quot;: 0.3203776921145618
},
{
&quot;iteration&quot;: 3,
&quot;replay_examples&quot;: 12328,
&quot;x_win_rate&quot;: 0.42857142857142855,
&quot;o_win_rate&quot;: 0.014285714285714285,
&quot;draw_rate&quot;: 0.5571428571428572,
&quot;average_game_moves&quot;: 7.5,
&quot;policy_loss&quot;: 1.4543613067695074,
&quot;value_loss&quot;: 0.26955098012576295
},
{
&quot;iteration&quot;: 4,
&quot;replay_examples&quot;: 16456,
&quot;x_win_rate&quot;: 0.5142857142857142,
&quot;o_win_rate&quot;: 0.0,
&quot;draw_rate&quot;: 0.4857142857142857,
&quot;average_game_moves&quot;: 7.371428571428571,
&quot;policy_loss&quot;: 1.229265012878638,
&quot;value_loss&quot;: 0.22285609325537314
},
{
&quot;iteration&quot;: 5,
&quot;replay_examples&quot;: 20952,
&quot;x_win_rate&quot;: 0.2857142857142857,
&quot;o_win_rate&quot;: 0.0,
&quot;draw_rate&quot;: 0.7142857142857143,
&quot;average_game_moves&quot;: 8.028571428571428,
&quot;policy_loss&quot;: 1.1138035529633847,
&quot;value_loss&quot;: 0.1854344406960214
},
{
&quot;iteration&quot;: 6,
&quot;replay_examples&quot;: 24000,
&quot;x_win_rate&quot;: 0.3142857142857143,
&quot;o_win_rate&quot;: 0.014285714285714285,
&quot;draw_rate&quot;: 0.6714285714285714,
&quot;average_game_moves&quot;: 7.957142857142857,
&quot;policy_loss&quot;: 1.0146886479664357,
&quot;value_loss&quot;: 0.15539901554980812
},
{
&quot;iteration&quot;: 7,
&quot;replay_examples&quot;: 24000,
&quot;x_win_rate&quot;: 0.2571428571428571,
&quot;o_win_rate&quot;: 0.0,
&quot;draw_rate&quot;: 0.7428571428571429,
&quot;average_game_moves&quot;: 8.085714285714285,
&quot;policy_loss&quot;: 0.8795330280319174,
&quot;value_loss&quot;: 0.11288299490796759
},
{
&quot;iteration&quot;: 8,
&quot;replay_examples&quot;: 24000,
&quot;x_win_rate&quot;: 0.24285714285714285,
&quot;o_win_rate&quot;: 0.0,
&quot;draw_rate&quot;: 0.7571428571428571,
&quot;average_game_moves&quot;: 8.314285714285715,
&quot;policy_loss&quot;: 0.7982094690520712,
&quot;value_loss&quot;: 0.09204166978993948
},
{
&quot;iteration&quot;: 9,
&quot;replay_examples&quot;: 24000,
&quot;x_win_rate&quot;: 0.3,
&quot;o_win_rate&quot;: 0.014285714285714285,
&quot;draw_rate&quot;: 0.6857142857142857,
&quot;average_game_moves&quot;: 7.985714285714286,
&quot;policy_loss&quot;: 0.7609958442601752,
&quot;value_loss&quot;: 0.09048273768077822
},
{
&quot;iteration&quot;: 10,
&quot;replay_examples&quot;: 24000,
&quot;x_win_rate&quot;: 0.2857142857142857,
&quot;o_win_rate&quot;: 0.0,
&quot;draw_rate&quot;: 0.7142857142857143,
&quot;average_game_moves&quot;: 8.17142857142857,
&quot;policy_loss&quot;: 0.7380844823857571,
&quot;value_loss&quot;: 0.08676699136799955
},
{
&quot;iteration&quot;: 11,
&quot;replay_examples&quot;: 24000,
&quot;x_win_rate&quot;: 0.18571428571428572,
&quot;o_win_rate&quot;: 0.0,
&quot;draw_rate&quot;: 0.8142857142857143,
&quot;average_game_moves&quot;: 8.371428571428572,
&quot;policy_loss&quot;: 0.7269294456915653,
&quot;value_loss&quot;: 0.074583658938633
},
{
&quot;iteration&quot;: 12,
&quot;replay_examples&quot;: 24000,
&quot;x_win_rate&quot;: 0.3142857142857143,
&quot;o_win_rate&quot;: 0.0,
&quot;draw_rate&quot;: 0.6857142857142857,
&quot;average_game_moves&quot;: 8.142857142857142,
&quot;policy_loss&quot;: 0.7146875767948779,
&quot;value_loss&quot;: 0.07327091548235175
},
{
&quot;iteration&quot;: 13,
&quot;replay_examples&quot;: 24000,
&quot;x_win_rate&quot;: 0.22857142857142856,
&quot;o_win_rate&quot;: 0.0,
&quot;draw_rate&quot;: 0.7714285714285715,
&quot;average_game_moves&quot;: 8.17142857142857,
&quot;policy_loss&quot;: 0.7104355310823055,
&quot;value_loss&quot;: 0.07133727120433717
},
{
&quot;iteration&quot;: 14,
&quot;replay_examples&quot;: 24000,
&quot;x_win_rate&quot;: 0.2,
&quot;o_win_rate&quot;: 0.0,
&quot;draw_rate&quot;: 0.8,
&quot;average_game_moves&quot;: 8.371428571428572,
&quot;policy_loss&quot;: 0.7074488862714869,
&quot;value_loss&quot;: 0.06895742299867437
}
],
&quot;versus_random&quot;: {
&quot;games&quot;: 400,
&quot;wins&quot;: 384,
&quot;draws&quot;: 16,
&quot;losses&quot;: 0,
&quot;win_rate&quot;: 0.96,
&quot;non_loss_rate&quot;: 1.0,
&quot;average_moves&quot;: 6.065
},
&quot;versus_exact_minimax&quot;: {
&quot;games&quot;: 200,
&quot;wins&quot;: 0,
&quot;draws&quot;: 200,
&quot;losses&quot;: 0,
&quot;win_rate&quot;: 0.0,
&quot;non_loss_rate&quot;: 1.0,
&quot;average_moves&quot;: 9.0
}
}</pre>
</section>
<section class="card">
<h2>Backed-up artifact tree</h2>
<input id="filter" placeholder="Filter files…" autocomplete="off">
<ul id="files"><li><code>README.md</code></li>
<li><code>__pycache__/app.cpython-311.pyc</code></li>
<li><code>__pycache__/game.cpython-311.pyc</code></li>
<li><code>__pycache__/mcts.cpython-311.pyc</code></li>
<li><code>__pycache__/model.cpython-311.pyc</code></li>
<li><code>__pycache__/train.cpython-311.pyc</code></li>
<li><code>app.py</code></li>
<li><code>artifacts/microzero/evaluation.json</code></li>
<li><code>artifacts/microzero/model.safetensors</code></li>
<li><code>game.py</code></li>
<li><code>mcts.py</code></li>
<li><code>model.py</code></li>
<li><code>requirements.txt</code></li>
<li><code>train.py</code></li></ul>
</section>
</div>
</main>
<script>
const canvas=document.querySelector('#field'),ctx=canvas.getContext('2d');
let dots=[];
function resize(){canvas.width=innerWidth;canvas.height=innerHeight;
dots=Array.from({length:90},()=>({x:Math.random()*innerWidth,
y:Math.random()*innerHeight,vx:(Math.random()-.5)*.35,vy:(Math.random()-.5)*.35}));}
function draw(){ctx.clearRect(0,0,canvas.width,canvas.height);
for(const d of dots){d.x=(d.x+d.vx+innerWidth)%innerWidth;
d.y=(d.y+d.vy+innerHeight)%innerHeight;ctx.fillStyle='#65dcff99';
ctx.beginPath();ctx.arc(d.x,d.y,1.4,0,7);ctx.fill();}requestAnimationFrame(draw);}
addEventListener('resize',resize);resize();draw();
document.querySelector('#filter').addEventListener('input',e=>{
const q=e.target.value.toLowerCase();for(const li of document.querySelectorAll('li'))
li.hidden=!li.textContent.toLowerCase().includes(q);});
</script>
</body>
</html>