AlphaGo_Move37 / index.html
eaglelandsonce's picture
Update index.html
2c5f108 verified
Raw
History Blame Contribute Delete
38 kB
<!doctype html>
<html lang="en" data-visualize-standalone>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="referrer" content="no-referrer">
<title>Alphago Move 37</title>
<style>:root {
color-scheme: light dark;
background-color: transparent !important;
/* Agent-facing contract; keep in sync with SKILL.md. */
--background: var(
--main-surface-primary,
light-dark(rgb(255 255 255), rgb(24 24 24))
);
--foreground: light-dark(rgb(26 28 31), rgb(255 255 255));
--card: color-mix(in oklab, var(--foreground) 5%, transparent);
--card-foreground: var(--foreground);
--popover: light-dark(rgb(255 255 255), rgb(45 45 45));
--popover-foreground: var(--foreground);
--primary: light-dark(rgb(51 156 255), rgb(131 195 255));
--primary-foreground: light-dark(rgb(255 255 255), rgb(13 13 13));
--secondary: light-dark(rgb(255 255 255 / 96%), rgb(54 54 54 / 96%));
--secondary-foreground: var(--foreground);
--muted: color-mix(in srgb, var(--foreground) 10%, transparent);
--muted-foreground: light-dark(
rgb(26 28 31 / 49.4%),
rgb(255 255 255 / 49.8%)
);
--accent: light-dark(rgb(229 242 255), rgb(13 39 63));
--accent-foreground: var(--primary);
--destructive: light-dark(rgb(226 85 7), rgb(255 133 73));
--border: light-dark(rgb(26 28 31 / 8%), rgb(255 255 255 / 8.2%));
--input: light-dark(
rgb(26 28 31 / 11.8%),
color-mix(in oklab, rgb(0 0 0) 10%, transparent)
);
--ring: light-dark(rgb(51 156 255), rgb(131 195 255 / 76%));
--font-size-base: 14px;
--red: light-dark(rgb(186 38 35), rgb(250 66 62));
--green: light-dark(rgb(0 162 64), rgb(64 201 119));
/* Internal implementation variables; not part of the agent contract. */
--font-sans: -apple-system, system-ui, "Segoe UI", sans-serif;
--font-mono:
ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono",
monospace;
--font-size-normal: max(11px, var(--font-size-base));
--font-size-tooltip: calc(var(--font-size-base) - 1px);
--font-size-small: max(11px, calc(var(--font-size-base) - 2px));
--font-size-h1: calc(var(--font-size-normal) * 1.7142857143);
--font-size-h2: calc(var(--font-size-normal) * 1.4285714286);
--font-size-h3: calc(var(--font-size-normal) * 1.2857142857);
--font-weight-normal: 430;
--font-weight-medium: 500;
--line-height-normal: calc(var(--font-size-normal) * 1.5);
--line-height-tooltip: calc(var(--font-size-tooltip) * 1.4285714286);
--line-height-small: calc(var(--font-size-small) + 4px);
--radius: 10px;
--radius-sm: calc(var(--radius) * 0.6);
--radius-md: calc(var(--radius) * 0.8);
--radius-lg: var(--radius);
--radius-2xl: calc(var(--radius) * 1.6);
--radius-full: 9999px;
--shadow-sm: 0 1px 2px -1px rgb(0 0 0 / 8%);
--checkmark-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 17 17'%3E%3Cpath d='M12.8961 3.64101C13.1297 3.41418 13.4984 3.37523 13.7779 3.56581C14.0571 3.75635 14.1554 4.11331 14.0299 4.41347L13.9615 4.53847L7.71151 13.7045C7.59411 13.8767 7.4063 13.9877 7.19881 14.0072C6.99136 14.0267 6.78564 13.9533 6.63826 13.806L2.88826 10.056L2.79842 9.9457C2.6192 9.67407 2.64927 9.30496 2.88826 9.06581C3.12738 8.82669 3.49647 8.79676 3.76815 8.97597L3.8785 9.06581L7.03084 12.2182L12.8053 3.74941L12.8961 3.64101Z'/%3E%3C/svg%3E");
/* Legacy aliases; not part of the current agent contract. */
--viz-bg: transparent;
--viz-panel: var(--card);
--viz-border: var(--border);
--viz-text: var(--foreground);
--viz-muted: var(--muted-foreground);
--viz-accent: var(--primary);
--viz-accent-text: var(--primary-foreground);
--viz-accent-bg: var(--accent);
--viz-font-size: var(--font-size-base);
--viz-warning: var(--destructive);
--color-background-primary: var(--background);
--color-text-primary: var(--foreground);
--color-border-secondary: var(--border);
}
:root[data-visualize-standalone] {
background-color: var(--background) !important;
}
/* Generic mobile code-block hosts provide light/dark mode but no series palette.
AppBlock hosts set all six variables themselves. */
:root:not([data-app-block-surface]) {
--viz-series-1: var(--primary);
--viz-series-2: light-dark(#f3883b, #f59a56);
--viz-series-3: light-dark(#5dc977, #74d58b);
--viz-series-4: light-dark(#eb77b1, #f08fc0);
--viz-series-5: light-dark(#9b79ec, #aa91ef);
--viz-series-6: light-dark(#3ab9b1, #5acbc2);
}
:root[data-theme="light"] {
color-scheme: light;
}
:root[data-theme="dark"] {
color-scheme: dark;
}
* {
box-sizing: border-box;
}
html > body {
margin: 0;
padding: 5px;
color: var(--foreground);
background: transparent !important;
font-family: var(--font-sans);
font-size: var(--font-size-normal);
font-weight: var(--font-weight-normal);
line-height: var(--line-height-normal);
}
:root:not([data-app-block-surface]) > body {
padding: 12px;
}
a {
color: color-mix(in srgb, var(--viz-accent) 80%, var(--viz-text) 20%);
cursor: pointer;
font-weight: var(--font-weight-medium);
text-decoration: none;
text-underline-offset: 2px;
}
a:is(:hover, :focus-visible) {
text-decoration-line: underline;
text-decoration-style: dashed;
text-decoration-thickness: 0.5px;
}
h1,
h2,
h3,
h4,
h5,
h6,
p {
margin-block: 0;
}
h1 {
font-size: var(--font-size-h1);
font-weight: var(--font-weight-medium);
line-height: 1.25;
}
h2 {
font-size: var(--font-size-h2);
font-weight: var(--font-weight-medium);
line-height: 1.25;
}
h3,
h4,
h5,
h6 {
font-size: var(--font-size-h3);
font-weight: var(--font-weight-medium);
line-height: 1.3;
}
b,
strong,
th {
font-weight: var(--font-weight-medium);
}
code:not(pre code) {
display: inline;
padding: 1px 6px;
border-radius: var(--radius-sm);
background: var(--muted);
font-family: var(--font-mono);
font-size: 0.92em;
overflow-wrap: anywhere;
-webkit-box-decoration-break: clone;
box-decoration-break: clone;
word-break: break-word;
}
.table-responsive {
width: 100%;
overflow-x: auto;
scrollbar-width: thin;
}
.table {
width: 100%;
border-collapse: collapse;
color: var(--foreground);
font: inherit;
text-align: start;
}
.table :is(th, td) {
padding-block: 10px;
padding-inline: 0 24px;
overflow-wrap: anywhere;
border-bottom: 1px solid var(--border);
text-align: start;
vertical-align: top;
}
.table-responsive > .table :is(th, td) {
overflow-wrap: break-word;
}
.table :is(th, td):last-child {
padding-inline-end: 0;
}
.table :is(caption, thead th) {
font-weight: 600;
}
.table thead th {
padding-block: 8px;
border-bottom-color: color-mix(in srgb, var(--foreground) 16%, transparent);
}
.table tbody tr:last-child :is(th, td) {
border-bottom: 0;
}
.table.table-sm :is(th, td) {
padding-block: 6px;
}
.table.table-sm :is(th, td):not(:last-child) {
padding-inline-end: 16px;
}
.table :is(.text-end, [align="right"]) {
text-align: end;
font-variant-numeric: tabular-nums;
}
.table :is(.text-center, [align="center"]) {
text-align: center;
}
.table .text-nowrap {
white-space: nowrap;
}
#widget {
display: flex;
flex-direction: column;
gap: 12px;
width: 100%;
padding: 0;
background: transparent !important;
}
.card {
min-width: 0;
padding: 12px;
overflow: hidden;
overflow-wrap: break-word;
border-radius: var(--radius-2xl);
color: var(--card-foreground);
background: var(--card);
}
#widget > :not(.card) {
width: 100% !important;
max-width: none !important;
margin: 0 !important;
padding: 0 !important;
border: 0 !important;
border-radius: 0 !important;
background: transparent !important;
box-shadow: none !important;
}
.tooltip {
position: fixed;
z-index: 50;
top: 0;
left: 0;
width: max-content;
max-width: min(
20rem,
var(--tooltip-available-width, calc(100vw - 10px)),
calc(100vw - 10px)
);
max-height: min(
var(--tooltip-available-height, calc(100vh - 10px)),
calc(100vh - 10px)
);
padding: 4px 8px;
border: 1px solid var(--border);
border-radius: var(--radius-lg);
color: var(--popover-foreground);
background: var(--popover);
box-shadow: none;
font-size: var(--font-size-tooltip);
line-height: var(--line-height-tooltip);
overflow-wrap: break-word;
white-space: normal;
pointer-events: none;
user-select: none;
}
/* The bundled tooltip runtime owns [data-tooltip]. AppBlock's CSS-only
pseudo-tooltip can widen dense grids even while hidden. */
main [data-tooltip]::after {
content: none;
}
.viz-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(max(180px, 24%), 1fr));
gap: 10px;
}
.viz-stat {
display: flex;
flex-direction: column;
gap: 2px;
}
.viz-stat-value {
font-size: var(--font-size-h2);
font-weight: var(--font-weight-medium);
line-height: 1.25;
}
.viz-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
}
.viz-badge {
padding: 3px 8px;
border-radius: var(--radius-full);
color: var(--accent-foreground);
background: var(--accent);
font-weight: var(--font-weight-medium);
}
small,
.text-small,
.viz-badge {
font-size: var(--font-size-small);
line-height: var(--line-height-small);
}
.text-muted {
color: var(--muted-foreground);
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.viz-controls {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
}
.viz-controls > .form-label {
display: grid;
min-width: min(100%, 260px);
flex: 1 1 280px;
grid-template-columns: minmax(0, 1fr) auto;
align-items: baseline;
gap: 2px 12px;
margin-bottom: 0;
}
.viz-controls > .form-label > :is(.form-control, .form-range, .form-select) {
grid-column: 1 / -1;
}
.btn,
.form-check-input,
.form-control,
.form-range,
.form-select {
font: inherit;
}
.btn {
appearance: button;
display: inline-flex;
inline-size: fit-content;
max-inline-size: 100%;
min-height: 28px;
align-items: center;
justify-content: center;
gap: 4px;
margin: 0;
padding: 0 8px;
-webkit-app-region: no-drag;
border: 1px solid var(--input);
border-radius: var(--radius-lg);
color: var(--secondary-foreground);
background: var(--secondary);
cursor: var(--cursor-interaction, pointer);
text-align: center;
text-decoration: none;
white-space: nowrap;
user-select: none;
-webkit-font-smoothing: antialiased;
}
.btn:is(.btn-block, .viz-tile) {
inline-size: 100%;
}
.btn.viz-tile {
min-width: 0;
overflow-wrap: anywhere;
white-space: normal;
}
a.btn {
cursor: pointer;
text-decoration: none;
}
.btn:not(:disabled):hover {
background: color-mix(in srgb, var(--foreground) 6%, var(--secondary));
}
.btn-primary {
border-color: transparent;
color: var(--interactive-label-primary-default, var(--primary-foreground));
background: var(--interactive-bg-primary-default, var(--foreground));
background-clip: padding-box;
}
.btn-primary .text-muted {
color: color-mix(
in srgb,
var(--interactive-label-primary-default, var(--primary-foreground)) 50%,
transparent
);
}
.btn-primary:not(:disabled):hover {
background: color-mix(in srgb, var(--foreground) 80%, transparent);
background-clip: padding-box;
}
.btn-ghost {
border-color: transparent;
color: var(--muted-foreground);
background: transparent;
}
.btn-ghost:not(:disabled):hover {
color: var(--foreground);
background: color-mix(in srgb, var(--foreground) 6%, var(--secondary));
}
.btn:disabled {
cursor: not-allowed;
opacity: 0.4;
}
[data-lucide] {
stroke-width: 1.6;
}
.form-label {
display: block;
margin-bottom: 6px;
color: var(--foreground);
}
.form-control {
display: block;
width: 100%;
min-height: 28px;
padding: 0 8px;
outline: none;
border: 1px solid var(--input);
border-radius: var(--radius-lg);
color: var(--foreground);
background: var(--secondary);
}
.form-control::placeholder {
color: var(--muted-foreground);
}
.form-control[type="file"] {
padding: 0;
overflow: hidden;
cursor: var(--cursor-interaction, pointer);
}
.form-control[type="file"]::file-selector-button {
min-height: 26px;
margin-right: 8px;
padding: 0 8px;
border: 0;
border-right: 1px solid var(--input);
color: var(--secondary-foreground);
background: var(--secondary);
cursor: inherit;
font: inherit;
}
.form-control[type="file"]:not(:disabled):hover::file-selector-button {
background: color-mix(in srgb, var(--foreground) 6%, var(--secondary));
}
.form-control-color[type="color"] {
width: 40px;
height: 28px;
padding: 3px;
cursor: var(--cursor-interaction, pointer);
}
.form-control-color[type="color"]::-webkit-color-swatch-wrapper {
padding: 0;
}
.form-control-color[type="color"]::-webkit-color-swatch {
border: 0;
border-radius: calc(var(--radius-lg) - 4px);
}
textarea.form-control {
height: auto;
min-height: 72px;
padding: 8px 10px;
resize: vertical;
}
.form-control:focus-visible {
border-color: var(--ring);
box-shadow: inset 0 0 0 1px var(--ring);
}
.form-control:disabled,
.form-select:disabled {
cursor: not-allowed;
opacity: 0.4;
}
.form-select {
appearance: none;
display: block;
width: 100%;
min-height: 28px;
margin: 0;
padding: 0 32px 0 8px;
outline: none;
border: 1px solid var(--input);
border-radius: var(--radius-lg);
color: var(--foreground);
background-color: var(--secondary);
background-image:
linear-gradient(45deg, transparent 50%, var(--muted-foreground) 50%),
linear-gradient(135deg, var(--muted-foreground) 50%, transparent 50%);
background-position:
calc(100% - 14px) 50%,
calc(100% - 10px) 50%;
background-repeat: no-repeat;
background-size: 4px 4px;
cursor: var(--cursor-interaction, default);
}
.form-select:not(:disabled):hover {
background-color: color-mix(in srgb, var(--foreground) 6%, var(--secondary));
}
.form-select:focus-visible {
border-color: var(--ring);
box-shadow: inset 0 0 0 1px var(--ring);
}
.form-check {
display: flex;
min-height: 20px;
align-items: center;
gap: 6px;
}
.form-check-input {
appearance: none;
width: 14px;
height: 14px;
flex: 0 0 auto;
margin: 0;
border: 1px solid var(--input);
color: var(--primary-foreground);
background-color: transparent;
cursor: var(--cursor-interaction, default);
transition:
background-color 150ms,
border-color 150ms,
box-shadow 150ms;
}
.form-check:not(.form-switch) .form-check-input[type="checkbox"] {
border-color: var(--input);
border-radius: var(--radius-sm);
background-color: var(--secondary);
box-shadow: var(--shadow-sm);
}
.form-check:not(.form-switch)
.form-check-input:not(:disabled):not(:checked):hover {
background-color: var(--card);
}
.form-check:not(.form-switch) .form-check-input[type="checkbox"]:checked {
border-color: var(--primary);
background-color: var(--primary);
}
.form-check:not(.form-switch)
.form-check-input[type="checkbox"]:checked::before {
display: block;
width: 100%;
height: 100%;
background: var(--primary-foreground);
content: "";
mask: var(--checkmark-image) center / 12px 12px no-repeat;
}
.form-check-input[type="radio"] {
width: 14px;
height: 14px;
border-radius: var(--radius-full);
}
.form-check-input[type="radio"]:checked {
border: 2px solid var(--primary);
background:
radial-gradient(circle, var(--primary-foreground) 0 2.5px, transparent 3px),
var(--primary);
}
.form-check:not(.form-switch) .form-check-input:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 2px;
}
.form-check:not(.form-switch) .form-check-input:disabled {
cursor: not-allowed;
pointer-events: none;
}
.form-check:not(.form-switch) .form-check-input:disabled + .form-check-label {
cursor: not-allowed;
}
.form-switch .form-check-input:disabled,
.form-switch .form-check-input:disabled + .form-check-label {
cursor: not-allowed;
opacity: 0.6;
}
.form-check-label {
color: var(--foreground);
cursor: var(--cursor-interaction, default);
}
.form-switch .form-check-input {
position: relative;
width: 32px;
height: 20px;
border: 0;
border-radius: var(--radius-full);
background: var(--muted);
box-shadow: none;
transition: background-color 200ms cubic-bezier(0, 0, 0.2, 1);
}
.form-switch .form-check-input::before {
position: absolute;
top: 50%;
left: 0;
width: 16px;
height: 16px;
box-sizing: border-box;
border: 1px solid light-dark(var(--primary-foreground), var(--foreground));
border-radius: var(--radius-full);
background: light-dark(var(--primary-foreground), var(--foreground));
box-shadow: var(--shadow-sm);
content: "";
transform: translate(2px, -50%);
transition: transform 200ms cubic-bezier(0, 0, 0.2, 1);
}
.form-switch .form-check-input:checked {
background: var(--primary);
}
.form-switch .form-check-input:checked::before {
transform: translate(14px, -50%);
}
.form-switch .form-check-input:focus-visible {
box-shadow: 0 0 0 2px var(--ring);
}
.form-range {
appearance: none;
display: block;
width: 100%;
height: 28px;
flex: 1;
margin: 0;
padding: 0;
outline: none;
border: 0;
accent-color: var(--primary);
background: linear-gradient(
color-mix(in srgb, var(--foreground) 7%, transparent),
color-mix(in srgb, var(--foreground) 7%, transparent)
)
center / 100% 2px no-repeat;
}
.form-range::-webkit-slider-runnable-track {
height: 28px;
background: transparent;
}
.form-range::-webkit-slider-thumb {
appearance: none;
width: 20px;
height: 20px;
margin-top: 4px;
border: 1px solid var(--border);
border-radius: var(--radius-full);
background: light-dark(var(--primary-foreground), var(--foreground));
}
.form-range:focus-visible::-webkit-slider-thumb {
border-color: var(--ring);
box-shadow: inset 0 0 0 1px var(--ring);
}
.form-range::-moz-range-track {
height: 28px;
background: transparent;
}
.form-range::-moz-range-thumb {
width: 20px;
height: 20px;
border: 1px solid var(--border);
border-radius: var(--radius-full);
background: light-dark(var(--primary-foreground), var(--foreground));
}
.form-range:focus-visible::-moz-range-thumb {
border-color: var(--ring);
box-shadow: inset 0 0 0 1px var(--ring);
}
.form-range:disabled {
cursor: not-allowed;
opacity: 0.4;
}
.btn:not(.btn-primary, .viz-tile):is(
[aria-pressed="true"],
[aria-selected="true"],
.is-selected
) {
border-color: var(--primary);
color: var(--primary-foreground);
background: var(--primary);
}
.btn.viz-tile:is([aria-pressed="true"], [aria-selected="true"], .is-selected) {
border-color: var(--primary);
box-shadow: inset 0 0 0 1px var(--primary);
}
.btn:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 2px;
}
svg {
display: block;
max-width: 100%;
height: auto;
}
#widget > svg {
width: 100%;
}
.text-warning,
.text-destructive {
color: var(--destructive);
}
</style>
</head>
<body>
<div id="alphago-move-37" class="move37-shell">
<div class="viz-controls" aria-label="Replay controls">
<button type="button" class="btn" data-action="start"><i data-lucide="skip-back" aria-hidden="true"></i> Start</button>
<button type="button" class="btn" data-action="previous"><i data-lucide="chevron-left" aria-hidden="true"></i> Previous</button>
<button type="button" class="btn btn-primary" data-action="play"><i data-lucide="play" aria-hidden="true"></i> Play</button>
<button type="button" class="btn" data-action="next">Next <i data-lucide="chevron-right" aria-hidden="true"></i></button>
<button type="button" class="btn" data-action="move37"><i data-lucide="sparkles" aria-hidden="true"></i> Jump to 37</button>
</div>
<div class="move37-stage">
<svg class="move37-board" viewBox="0 0 640 640" role="img" aria-labelledby="move37-title move37-desc">
<title id="move37-title">AlphaGo versus Lee Sedol, Game 2 Go board</title>
<desc id="move37-desc">Interactive replay of the first 50 moves, highlighting AlphaGo's famous black move 37 at P10.</desc>
<defs>
<radialGradient id="move37-black" cx="35%" cy="28%" r="70%"><stop offset="0" stop-color="#626262"/><stop offset="0.38" stop-color="#202020"/><stop offset="1" stop-color="#050505"/></radialGradient>
<radialGradient id="move37-white" cx="35%" cy="28%" r="70%"><stop offset="0" stop-color="#ffffff"/><stop offset="0.62" stop-color="#e8e8e8"/><stop offset="1" stop-color="#bdbdbd"/></radialGradient>
<filter id="move37-shadow" x="-30%" y="-30%" width="160%" height="160%"><feDropShadow dx="1.5" dy="2.5" stdDeviation="2" flood-color="#000000" flood-opacity="0.28"/></filter>
</defs>
<rect x="18" y="18" width="604" height="604" rx="8" class="board-field"/>
<g data-layer="grid"></g>
<g data-layer="stars"></g>
<g data-layer="coordinates"></g>
<g data-layer="stones"></g>
<g data-layer="highlight"></g>
</svg>
<div class="card move37-detail" aria-live="polite">
<div class="move37-kicker" data-role="kicker">Opening position</div>
<div class="viz-stat-value" data-role="headline">Move 0 of 50</div>
<div data-role="detail">AlphaGo plays Black. Lee Sedol plays White.</div>
</div>
</div>
<div class="move37-progress" aria-label="Replay progress">
<label class="form-label" for="move37-range">Move <span data-role="move-number">0</span></label>
<input id="move37-range" class="form-range" type="range" min="0" max="50" value="0" step="1">
</div>
</div>
<style>
#alphago-move-37 { color: var(--foreground); width: 100%; }
#alphago-move-37 .move37-stage { display: grid; grid-template-columns: minmax(0, 1fr) minmax(210px, 0.42fr); gap: 16px; align-items: center; margin-top: 12px; }
#alphago-move-37 .move37-board { width: 100%; max-width: 640px; height: auto; display: block; }
#alphago-move-37 .board-field { fill: color-mix(in srgb, var(--viz-series-1) 26%, var(--card)); stroke: var(--border); stroke-width: 2; }
#alphago-move-37 .board-line { stroke: color-mix(in srgb, var(--foreground) 64%, transparent); stroke-width: 1.35; }
#alphago-move-37 .star { fill: color-mix(in srgb, var(--foreground) 78%, transparent); }
#alphago-move-37 .coord { fill: var(--muted-foreground); font: 500 12px system-ui, sans-serif; text-anchor: middle; dominant-baseline: middle; }
#alphago-move-37 .stone { filter: url(#move37-shadow); }
#alphago-move-37 .stone-number { font: 500 10px system-ui, sans-serif; text-anchor: middle; dominant-baseline: central; pointer-events: none; }
#alphago-move-37 .move37-ring { fill: none; stroke: var(--viz-series-2); stroke-width: 5; }
#alphago-move-37 .move37-pulse { fill: color-mix(in srgb, var(--viz-series-2) 24%, transparent); stroke: var(--viz-series-2); stroke-width: 2; }
#alphago-move-37 .move37-label { fill: var(--foreground); font: 500 14px system-ui, sans-serif; text-anchor: middle; }
#alphago-move-37 .move37-detail { min-height: 150px; display: grid; align-content: center; gap: 8px; }
#alphago-move-37 .move37-kicker { color: var(--muted-foreground); }
#alphago-move-37 .move37-progress { margin-top: 12px; }
@media (max-width: 620px) {
#alphago-move-37 .move37-stage { grid-template-columns: 1fr; }
#alphago-move-37 .move37-detail { min-height: auto; }
}
@media (prefers-reduced-motion: no-preference) {
#alphago-move-37 .move37-pulse { animation: move37-pulse 1.4s ease-in-out infinite alternate; }
@keyframes move37-pulse { from { opacity: .4; transform: scale(.82); transform-origin: center; } to { opacity: 1; transform: scale(1.12); transform-origin: center; } }
}
</style>
<script>
(function () {
const root = document.getElementById("alphago-move-37");
const svg = root.querySelector(".move37-board");
const ns = "http://www.w3.org/2000/svg";
const moves = [
["B","pd"],["W","dp"],["B","cd"],["W","qp"],["B","op"],["W","oq"],["B","nq"],["W","pq"],["B","cn"],["W","fq"],
["B","mp"],["W","qn"],["B","ic"],["W","dj"],["B","po"],["W","qo"],["B","cp"],["W","cq"],["B","bq"],["W","co"],
["B","bp"],["W","bo"],["B","do"],["W","bn"],["B","dq"],["W","ep"],["B","dr"],["W","cm"],["B","jp"],["W","cg"],
["B","ed"],["W","qf"],["B","qe"],["W","pf"],["B","nd"],["W","pi"],["B","oj"],["W","oi"],["B","nj"],["W","mh"],
["B","gp"],["W","gq"],["B","dn"],["W","dm"],["B","fo"],["W","hp"],["B","ho"],["W","eo"],["B","en"],["W","fn"]
];
const letters = "ABCDEFGHJKLMNOPQRST";
const grid = svg.querySelector('[data-layer="grid"]');
const stars = svg.querySelector('[data-layer="stars"]');
const coords = svg.querySelector('[data-layer="coordinates"]');
const stones = svg.querySelector('[data-layer="stones"]');
const highlight = svg.querySelector('[data-layer="highlight"]');
const range = root.querySelector("#move37-range");
const moveNumber = root.querySelector('[data-role="move-number"]');
const kicker = root.querySelector('[data-role="kicker"]');
const headline = root.querySelector('[data-role="headline"]');
const detail = root.querySelector('[data-role="detail"]');
const margin = 50;
const step = 30;
let current = 0;
let timer = null;
function add(tag, attrs, parent) {
const element = document.createElementNS(ns, tag);
Object.entries(attrs).forEach(function (entry) { element.setAttribute(entry[0], entry[1]); });
parent.appendChild(element);
return element;
}
for (let i = 0; i < 19; i += 1) {
const p = margin + i * step;
add("line", { x1: margin, y1: p, x2: margin + 18 * step, y2: p, class: "board-line" }, grid);
add("line", { x1: p, y1: margin, x2: p, y2: margin + 18 * step, class: "board-line" }, grid);
const top = add("text", { x: p, y: 32, class: "coord" }, coords);
top.textContent = letters[i];
const left = add("text", { x: 30, y: p, class: "coord" }, coords);
left.textContent = String(19 - i);
}
[3, 9, 15].forEach(function (x) {
[3, 9, 15].forEach(function (y) {
add("circle", { cx: margin + x * step, cy: margin + y * step, r: 4, class: "star" }, stars);
});
});
function point(sgf) {
return { x: margin + (sgf.charCodeAt(0) - 97) * step, y: margin + (sgf.charCodeAt(1) - 97) * step };
}
function humanCoord(sgf) {
const col = letters[sgf.charCodeAt(0) - 97];
return col + String(19 - (sgf.charCodeAt(1) - 97));
}
function render() {
stones.replaceChildren();
highlight.replaceChildren();
const occupied = new Map();
moves.slice(0, current).forEach(function (move, index) {
occupied.set(move[1], { color: move[0], number: index + 1 });
});
occupied.forEach(function (stone, key) {
const p = point(key);
add("circle", { cx: p.x, cy: p.y, r: 13.2, fill: stone.color === "B" ? "url(#move37-black)" : "url(#move37-white)", class: "stone" }, stones);
const number = add("text", { x: p.x, y: p.y + 0.5, fill: stone.color === "B" ? "#ffffff" : "#111111", class: "stone-number" }, stones);
number.textContent = String(stone.number);
});
if (current > 0) {
const last = moves[current - 1];
const p = point(last[1]);
add("circle", { cx: p.x, cy: p.y, r: 16.8, class: current === 37 ? "move37-pulse" : "move37-ring" }, highlight);
kicker.textContent = last[0] === "B" ? "AlphaGo · Black" : "Lee Sedol · White";
headline.textContent = "Move " + current + " · " + humanCoord(last[1]);
if (current === 37) {
detail.textContent = "The famous fifth-line shoulder hit. It looked unnatural to professionals, yet connected influence across the right side and center.";
const label = add("text", { x: p.x, y: p.y - 28, class: "move37-label" }, highlight);
label.textContent = "MOVE 37";
} else if (current === 36) {
detail.textContent = "Pause here: the conventional-looking position just before AlphaGo's surprise.";
} else if (current === 38) {
detail.textContent = "Lee responds at O11 after taking unusually long to assess the unexpected shoulder hit.";
} else {
detail.textContent = current < 37 ? "The opening develops toward the position that made Move 37 so surprising." : "The game continues as AlphaGo converts its unconventional influence into a playable whole-board strategy.";
}
} else {
kicker.textContent = "Opening position";
headline.textContent = "Move 0 of 50";
detail.textContent = "AlphaGo plays Black. Lee Sedol plays White.";
}
moveNumber.textContent = String(current);
range.value = String(current);
}
function stop() {
if (timer) { clearInterval(timer); timer = null; }
const play = root.querySelector('[data-action="play"]');
play.innerHTML = '<i data-lucide="play" aria-hidden="true"></i> Play';
if (window.lucide) { lucide.createIcons({ attrs: { width: 16, height: 16 } }); }
}
function setMove(value) {
current = Math.max(0, Math.min(moves.length, value));
render();
}
root.querySelector('[data-action="start"]').addEventListener("click", function () { stop(); setMove(0); });
root.querySelector('[data-action="previous"]').addEventListener("click", function () { stop(); setMove(current - 1); });
root.querySelector('[data-action="next"]').addEventListener("click", function () { stop(); setMove(current + 1); });
root.querySelector('[data-action="move37"]').addEventListener("click", function () { stop(); setMove(37); });
root.querySelector('[data-action="play"]').addEventListener("click", function (event) {
if (timer) { stop(); return; }
if (current >= moves.length) { setMove(0); }
event.currentTarget.innerHTML = '<i data-lucide="pause" aria-hidden="true"></i> Pause';
if (window.lucide) { lucide.createIcons({ attrs: { width: 16, height: 16 } }); }
timer = setInterval(function () {
if (current >= moves.length) { stop(); return; }
setMove(current + 1);
if (current === 37) { stop(); }
}, 520);
});
range.addEventListener("input", function () { stop(); setMove(Number(range.value)); });
render();
}());
</script>
<script id="codex-visualization-floating-ui-core" src="https://unpkg.com/@floating-ui/core@1.7.3/dist/floating-ui.core.umd.min.js"></script>
<script id="codex-visualization-floating-ui-dom" src="https://unpkg.com/@floating-ui/dom@1.7.4/dist/floating-ui.dom.umd.min.js"></script>
<script id="codex-visualization-lucide" async src="https://unpkg.com/lucide@1.17.0/dist/umd/lucide.js"></script>
<script id="codex-visualization-tooltip">/* oxlint-disable typescript/no-unsafe-argument, typescript/no-unsafe-call, typescript/no-unsafe-return */
// Runs inside the visualization sandbox, not the host webview.
(() => {
if (window.FloatingUIDOM == null) {
return;
}
const { autoUpdate, computePosition, flip, offset, shift, size } =
window.FloatingUIDOM;
const tooltipPlacements = new Set(["top", "right", "bottom", "left"]);
const tooltipId = `codex-visualization-tooltip-${
window.crypto?.randomUUID?.() ?? Date.now().toString(36)
}`;
const tooltipGap = 5;
const hoverDelay = 700;
const skipDelay = 300;
let activeTrigger = null;
let pendingTrigger = null;
let tooltip = null;
let openTimeout = null;
let autoUpdateCleanup = null;
let positionRequest = 0;
let skipDelayUntil = 0;
const getTrigger = (target) =>
target?.nodeType === Node.ELEMENT_NODE
? target.closest("[data-tooltip]")
: null;
const containsTarget = (trigger, target) =>
target?.nodeType != null && trigger.contains(target);
const getContent = (trigger) =>
trigger.getAttribute("data-tooltip")?.trim() ?? "";
const getPlacement = (trigger) => {
const placement = trigger.getAttribute("data-tooltip-placement");
return tooltipPlacements.has(placement) ? placement : "top";
};
const getTooltip = () => {
if (tooltip == null) {
tooltip = document.createElement("div");
tooltip.id = tooltipId;
tooltip.className = "tooltip";
tooltip.setAttribute("role", "tooltip");
}
return tooltip;
};
const setDescribedBy = (trigger, described) => {
const ids = new Set(
(trigger.getAttribute("aria-describedby") ?? "")
.split(/\s+/)
.filter(Boolean),
);
if (described) {
ids.add(tooltipId);
} else {
ids.delete(tooltipId);
}
if (ids.size === 0) {
trigger.removeAttribute("aria-describedby");
} else {
trigger.setAttribute("aria-describedby", [...ids].join(" "));
}
};
const clearOpenTimeout = () => {
if (openTimeout != null) {
window.clearTimeout(openTimeout);
openTimeout = null;
}
pendingTrigger = null;
};
const closeTooltip = (activateSkipDelay = true) => {
clearOpenTimeout();
positionRequest += 1;
autoUpdateCleanup?.();
autoUpdateCleanup = null;
if (activeTrigger == null) {
return;
}
setDescribedBy(activeTrigger, false);
activeTrigger = null;
tooltip?.remove();
if (activateSkipDelay) {
skipDelayUntil = Date.now() + skipDelay;
}
};
const updatePosition = (trigger, floating) => {
const request = ++positionRequest;
void computePosition(trigger, floating, {
middleware: [
offset(tooltipGap),
flip({ padding: tooltipGap }),
shift({ padding: tooltipGap }),
size({
padding: tooltipGap,
apply({ availableHeight, availableWidth, elements }) {
elements.floating.style.setProperty(
"--tooltip-available-width",
`${Math.max(0, availableWidth)}px`,
);
elements.floating.style.setProperty(
"--tooltip-available-height",
`${Math.max(0, availableHeight)}px`,
);
},
}),
],
placement: getPlacement(trigger),
strategy: "fixed",
})
.then(({ x, y }) => {
if (activeTrigger !== trigger || request !== positionRequest) {
return;
}
const scale = window.devicePixelRatio || 1;
floating.style.transform = `translate(${Math.round(x * scale) / scale}px, ${Math.round(y * scale) / scale}px)`;
floating.style.visibility = "visible";
})
.catch(() => {
if (activeTrigger === trigger && request === positionRequest) {
closeTooltip(false);
}
});
};
const openTooltip = (trigger) => {
const content = getContent(trigger);
if (!trigger.isConnected || content.length === 0) {
return;
}
clearOpenTimeout();
if (activeTrigger === trigger) {
return;
}
closeTooltip();
const floating = getTooltip();
floating.textContent = content;
floating.style.visibility = "hidden";
floating.style.transform = "translate(0, 0)";
document.body.appendChild(floating);
activeTrigger = trigger;
setDescribedBy(trigger, true);
autoUpdateCleanup = autoUpdate(trigger, floating, () => {
updatePosition(trigger, floating);
});
};
const requestOpen = (trigger, immediate = false) => {
if (
getContent(trigger).length === 0 ||
activeTrigger === trigger ||
pendingTrigger === trigger
) {
return;
}
clearOpenTimeout();
pendingTrigger = trigger;
const delay =
immediate || activeTrigger != null || Date.now() < skipDelayUntil
? 0
: hoverDelay;
if (delay === 0) {
openTooltip(trigger);
return;
}
openTimeout = window.setTimeout(() => {
openTimeout = null;
pendingTrigger = null;
openTooltip(trigger);
}, delay);
};
const handlePointerOpen = (event) => {
if (event.pointerType === "touch") {
return;
}
const trigger = getTrigger(event.target);
if (trigger == null || containsTarget(trigger, event.relatedTarget)) {
return;
}
requestOpen(trigger);
};
const handleLeave = (event) => {
const trigger = getTrigger(event.target);
if (trigger == null || containsTarget(trigger, event.relatedTarget)) {
return;
}
if (activeTrigger === trigger || pendingTrigger === trigger) {
closeTooltip();
}
};
const handleFocusIn = (event) => {
const trigger = getTrigger(event.target);
if (trigger?.matches(":focus-visible")) {
requestOpen(trigger, true);
}
};
const handleKeyDown = (event) => {
if (event.key === "Escape") {
closeTooltip();
}
};
const handleContextMenu = (event) => {
const trigger = getTrigger(event.target);
if (activeTrigger === trigger || pendingTrigger === trigger) {
closeTooltip();
}
};
const destroy = () => {
closeTooltip(false);
document.removeEventListener("pointerover", handlePointerOpen);
document.removeEventListener("pointerout", handleLeave);
document.removeEventListener("focusin", handleFocusIn);
document.removeEventListener("focusout", handleLeave);
document.removeEventListener("keydown", handleKeyDown);
document.removeEventListener("contextmenu", handleContextMenu);
tooltip = null;
};
document.addEventListener("pointerover", handlePointerOpen);
document.addEventListener("pointerout", handleLeave);
document.addEventListener("focusin", handleFocusIn);
document.addEventListener("focusout", handleLeave);
document.addEventListener("keydown", handleKeyDown);
document.addEventListener("contextmenu", handleContextMenu);
window.addEventListener("pagehide", destroy, { once: true });
})();
</script>
<script id="codex-visualization-lucide-initialization">(() => {
const initialize = () => {
globalThis.lucide?.createIcons({ attrs: { width: 16, height: 16 } });
};
if (globalThis.lucide != null) {
initialize();
return;
}
document
.getElementById("codex-visualization-lucide")
?.addEventListener("load", initialize, { once: true });
})();
</script>
</body>
</html>