`;
coach.querySelector("#playanyway").onclick = () => {
const p = this.pendingBlunder;
this.pendingBlunder = null;
coach.textContent = "";
this.commitHuman(p.from, p.to, p.promo);
};
coach.querySelector("#takeback").onclick = () => {
this.pendingBlunder = null;
coach.textContent = "";
};
}
setCoach(text) {
const coach = app.querySelector("#coach");
if (coach)
coach.textContent = text;
}
setClock(c, ms) {
const el = document.getElementById("clk-" + c);
if (!el)
return;
el.textContent = formatTime(ms);
const box = el.parentElement;
box.classList.toggle("running", this.game.turn === c && this.canRun());
box.classList.toggle("low", ms < 10000 && ms > 0);
box.classList.toggle("flag", this.timeLoss === c);
}
canRun() {
return !this.timeLoss && !this.game.outcome().over;
}
showThinking() {
const status = app.querySelector("#status");
if (status)
status.innerHTML = `${this.personality.face} is thinking…`;
}
update() {
this.renderStatus();
this.renderMoves();
const undo = app.querySelector("#undo");
if (undo && !this.clock.enabled)
undo.disabled = this.game.history().length === 0;
}
renderStatus() {
const status = app.querySelector("#status");
if (!status)
return;
if (this.timeLoss) {
status.textContent =
this.timeLoss === this.human ? "You lost on time." : "Your opponent lost on time. You win.";
return;
}
const outcome = this.game.outcome();
if (outcome.over) {
status.textContent = describeOutcome(outcome, this.human);
return;
}
const dot = ``;
status.innerHTML = dot + (this.game.turn === this.human ? "Your move" : "Waiting…");
}
renderMoves() {
const list = app.querySelector("#movelist");
if (!list)
return;
const history = this.game.history();
let html = "";
for (let i = 0; i < history.length; i += 2) {
const no = i / 2 + 1;
const white = history[i]?.san ?? "";
const black = history[i + 1]?.san ?? "";
const wHere = i === history.length - 1 ? " here" : "";
const bHere = i + 1 === history.length - 1 ? " here" : "";
html += `
${no}.${white}${black}
`;
}
list.innerHTML = html;
list.parentElement.scrollTop = list.parentElement.scrollHeight;
}
persist() {
if (this.game.outcome().over || this.timeLoss) {
clearGame();
return;
}
saveGame({
pgn: this.game.pgn(),
side: this.human,
personality: this.personality.id,
savedAt: Date.now(),
});
}
}
function flash(btn, on, off) {
btn.textContent = on;
setTimeout(() => (btn.textContent = off), 1200);
}
function describeOutcome(outcome, human) {
if (outcome.reason === "checkmate") {
return outcome.winner === human ? "Checkmate. You win." : "Checkmate. You lose.";
}
const draws = {
stalemate: "Stalemate. It's a draw.",
insufficient: "Draw — not enough material to mate.",
repetition: "Draw by repetition.",
fifty: "Draw by the fifty-move rule.",
draw: "The game is a draw.",
};
return draws[outcome.reason] ?? "The game is over.";
}