elo-worldle/main.ts

970 lines
28 KiB
TypeScript
Raw Normal View History

2023-03-26 23:33:41 -04:00
import {
Chess,
Move,
Square,
Ox88,
DEFAULT_POSITION,
SQUARES,
rank,
file,
Piece,
WHITE,
BLACK,
KING,
PieceSymbol,
Color,
PIECE_SYMBOLS,
} from "./chess.js";
type Player = (_: Chess) => string;
function with_move(board: Chess, move: string): Chess {
let new_board = new Chess(board.fen());
new_board.move(move);
return new_board;
}
// function max_move_by(board: Chess, score: (_: Chess) => number): string {
// let best_score = 0;
// let best_move = "";
// for (const move in board.moves()) {
// let new_board = with_move(board, move);
// let new_score = score(new_board);
// if (
// new_score > best_score ||
// (new_score == new_score && Math.random() > 0.5)
// ) {
// best_score = new_score;
// best_move = move;
// }
// }
// return best_move;
// }
2023-03-29 17:02:53 -04:00
// classic: https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
function shuffle(array) {
let currentIndex = array.length,
randomIndex;
// While there remain elements to shuffle.
while (currentIndex != 0) {
// Pick a remaining element.
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [
array[randomIndex],
array[currentIndex],
];
}
return array;
}
2023-03-26 23:33:41 -04:00
// could return early if zero but meh
function min_move_by(board: Chess, score: (_: Chess) => number): string {
let best_score = Infinity;
let best_move = "";
let moves = board.moves();
2023-03-29 17:02:53 -04:00
shuffle(moves);
2023-03-26 23:33:41 -04:00
// console.log(moves);
2023-03-29 17:02:53 -04:00
for (const move of moves) {
2023-03-26 23:33:41 -04:00
try {
let new_board = with_move(board, move);
let new_score = score(new_board);
// console.log({ move, best_score, new_score, best_move });
if (
2023-03-29 17:02:53 -04:00
new_score < best_score
// array is already shuffled
// (new_score == best_score && Math.random() > 0.5)
2023-03-26 23:33:41 -04:00
) {
console.log("New best (or equal): ", new_score, move);
best_score = new_score;
best_move = move;
}
} catch (e) {
console.error(e);
continue;
}
}
return best_move;
}
// D --> movelist users (bad name whatever)
const random_move: Player = board => {
const moves = board.moves();
const move = moves[Math.floor(Math.random() * moves.length)];
return move;
};
function make_lexo(m: Move): [number, number, number, number, PieceSymbol] {
let { rank: src_row, file: src_col } = rank_and_file(m.from);
let { rank: dst_row, file: dst_col } = rank_and_file(m.to);
let promote_to = m.promotion;
return [src_row, src_col, dst_row, dst_col, promote_to];
}
function lexo_sort_moves(a: Move, b: Move): -1 | 0 | 1 {
let al = make_lexo(a);
let bl = make_lexo(a);
for (const i in al) {
if (al[i] < bl[i]) {
return -1;
} else if (al[i] > bl[i]) {
return 1;
}
}
return 0;
}
const first_move: Player = board => {
const moves = board.moves({ verbose: true }).sort(lexo_sort_moves);
return moves[0].san;
};
const alphabetical: Player = board => {
const moves = board.moves();
moves.sort();
return moves[0];
};
// i believe the actual one tracks things better (pawns are treated as different)
// yeah this version sucks butttttt uh
// yeah
// prettier-ignore
const equalizer_state_default: {
2023-03-26 23:33:41 -04:00
piece_moves: Record<PieceSymbol, number>;
square_visits: Record<Square, number>;
} = {
piece_moves: {
p: 0,
b: 0,
n: 0,
r: 0,
q: 0,
k: 0,
},
// bad
square_visits: {
a8: 0, b8: 0, c8: 0, d8: 0, e8: 0, f8: 0, g8: 0, h8: 0,
a7: 0, b7: 0, c7: 0, d7: 0, e7: 0, f7: 0, g7: 0, h7: 0,
a6: 0, b6: 0, c6: 0, d6: 0, e6: 0, f6: 0, g6: 0, h6: 0,
a5: 0, b5: 0, c5: 0, d5: 0, e5: 0, f5: 0, g5: 0, h5: 0,
a4: 0, b4: 0, c4: 0, d4: 0, e4: 0, f4: 0, g4: 0, h4: 0,
a3: 0, b3: 0, c3: 0, d3: 0, e3: 0, f3: 0, g3: 0, h3: 0,
a2: 0, b2: 0, c2: 0, d2: 0, e2: 0, f2: 0, g2: 0, h2: 0,
a1: 0, b1: 0, c1: 0, d1: 0, e1: 0, f1: 0, g1: 0, h1: 0
}
};
var equalizer_state = equalizer_state_default;
2023-03-26 23:33:41 -04:00
const equalizer: Player = board => {
let best_move: Move;
let least_moved_found = Infinity;
let least_visited_found = Infinity;
let piece_moving: PieceSymbol = "k";
let square_visiting: Square = "a1";
// let least_moved = PIECE_SYMBOLS.sort(
// (a, b) =>
// equalizer_state.piece_moves[a] - equalizer_state.piece_moves[b]
// )[0];
// let least_visited = SQUARES.sort(
// (a, b) =>
// equalizer_state.square_visits[a] - equalizer_state.square_visits[b]
// )[0];
// cbb to do the proper randomness (23:41)
for (const move of board.moves({ verbose: true })) {
if (
equalizer_state.piece_moves[move.piece] < least_moved_found ||
(equalizer_state.piece_moves[move.piece] == least_moved_found &&
equalizer_state.square_visits[move.to] < least_visited_found)
) {
best_move = move;
least_moved_found = equalizer_state.piece_moves[move.piece];
least_visited_found = equalizer_state.square_visits[move.to];
piece_moving = move.piece;
square_visiting = move.to;
}
}
equalizer_state.piece_moves[piece_moving] += 1;
equalizer_state.square_visits[square_visiting] += 1;
return best_move.san;
};
// D --> min-maxxing some score of the board after moving
const min_oppt_move: Player = board => {
let num_moves = b => b.moves().length;
return min_move_by(board, num_moves);
};
function num_pieces_on_own_color(b: Chess): number {
let count = 0;
for (const square of SQUARES) {
let piece = b.get(square);
if (typeof piece == "boolean") {
continue;
}
if (piece.color == b.squareColor(square)) {
count += 1;
}
}
return count;
}
const same_color: Player = board => {
return min_move_by(board, b => 64 - num_pieces_on_own_color(b));
};
const opposite_color: Player = board => {
return min_move_by(board, num_pieces_on_own_color);
};
function rank_and_file(s: Square): { rank: number; file: number } {
return { rank: rank(Ox88[s]), file: file(Ox88[s]) };
}
function chebyshev_distance(a: Square, b: Square): number {
let { rank: r1, file: f1 } = rank_and_file(a);
let { rank: r2, file: f2 } = rank_and_file(b);
// forgot the abs UGH
return Math.max(Math.abs(r2 - r1), Math.abs(f2 - f1));
}
const START_BOARD: Chess = new Chess(DEFAULT_POSITION);
// good enough
function move_distance(piece: { square: Square; type: PieceSymbol }): number {
let rank = rank_and_file(piece.square).rank;
let file = rank_and_file(piece.square).file;
switch (piece.type) {
case "k":
return chebyshev_distance(
piece.square,
find({ color: HUMAN_COLOR, type: "k" }, START_BOARD)
);
case "p":
return Math.abs(rank - 6);
case "r":
return (
Math.abs(rank - 7) +
Math.min(Math.abs(file - 7), Math.abs(file - 0))
);
case "n":
// inauthentic
return pieces_of_color(START_BOARD, HUMAN_COLOR)
.filter(p => p.type == "k")
.map(p => chebyshev_distance(piece.square, p.square))
.reduce((a, b) => Math.max(a, b));
case "b":
let is_black = START_BOARD.squareColor(piece.square) == "b";
let dest_col = is_black ? 2 : 5;
let dr = Math.abs(rank - 7);
let dc = Math.abs(file - dest_col);
return Math.max(dr, dc);
case "q":
return Math.max(Math.abs(rank - 7), Math.abs(file - 3));
}
}
// oh gosh i can do symmetry by making the "distance" just a score fn which
// would... work but dang so many things have done something similar in hindsight
// actually hm symmetry is weird
function minimize_distances(
distance_fn: (piece: { square: Square; type: PieceSymbol }) => number
): Player {
const player_score = (board: Chess) => {
let total_distance = 0;
for (const piece of pieces_of_color(board, ROBOT_COLOR)) {
total_distance += distance_fn(piece);
}
return total_distance;
};
return b => min_move_by(b, player_score);
}
const reverse_starting: Player = minimize_distances(move_distance);
// or lens :]
function mirror_player(mirror_fn: (_: Square) => Square): Player {
let player_score = (b: Chess) => {
// it's worrying how the strategies have shifted, before it was more
// common for them to weigh your pieces equally, meaning they might take
// them to make the score better; silver lining: a bit easier to tell
// apart potentially
// the penalties are taken from the og code (the last four are the ones
// where i finally figured out where to find the players (curse_you
// UpperCamelCase))
// the behavior is likely still slightly different
let total = 0;
for (const square of SQUARES) {
let reflected = mirror_fn(square);
console.log(square, reflected);
let piece = b.get(square);
let reflection = b.get(reflected);
if (typeof piece == "boolean" && typeof reflection == "boolean") {
continue;
}
if (typeof piece == "boolean") {
total += 10;
continue;
}
if (typeof reflection == "boolean") {
total += 10;
continue;
}
if (piece.color == reflection.color) {
total += 5;
continue;
}
if (piece.type != reflection.type) {
total += 1;
continue;
}
}
return total;
};
return b => min_move_by(b, player_score);
}
function rank_and_file_to_algebraic(rf: {
rank: number;
file: number;
}): Square {
let r: "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" = "8";
let f: "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" = "h";
// vim's <C-a> was very helpful here :]
// I WAS MIRRORING AND THEN MIRRORING AGAIN OH MY GOOOOOSH
// prettier-ignore
switch (rf.rank) {
case 0: r = "8"; break;
case 1: r = "7"; break;
case 2: r = "6"; break;
case 3: r = "5"; break;
case 4: r = "4"; break;
case 5: r = "3"; break;
case 6: r = "7"; break;
case 7: r = "1"; break;
}
// prettier-ignore
switch (rf.file) {
case 0: f = "a"; break;
case 1: f = "b"; break;
case 2: f = "c"; break;
case 3: f = "d"; break;
case 4: f = "e"; break;
case 5: f = "f"; break;
case 6: f = "g"; break;
case 7: f = "h"; break;
}
return `${f}${r}`;
}
function mirror_y(s: Square): Square {
let rf = rank_and_file(s);
rf.rank = 7 - rf.rank;
return rank_and_file_to_algebraic(rf);
}
const sym_mirror_y: Player = mirror_player(mirror_y);
function mirror_x(s: Square): Square {
let rf = rank_and_file(s);
rf.file = 7 - rf.file;
return rank_and_file_to_algebraic(rf);
}
const sym_mirror_x: Player = mirror_player(mirror_x);
function flip(s: Square): Square {
let rf = rank_and_file(s);
rf.rank = 7 - rf.rank;
rf.file = 7 - rf.file;
return rank_and_file_to_algebraic(rf);
}
const sym_180: Player = mirror_player(flip);
function find(p: Piece, b: Chess): Square | null {
for (const square of SQUARES) {
let got = b.get(square);
if (typeof got == "boolean") {
continue;
}
if (got.color == p.color && got.type == p.type) {
return square;
}
}
console.log(`didn't find this :O ${p}`);
return null;
}
// maybe a find function would be useful Piece -> Board -> Square | null
const suicide_king: Player = board => {
let distance_between_kings = (b: Chess) =>
chebyshev_distance(
find({ color: WHITE, type: KING }, b) ?? "a1",
find({ color: BLACK, type: KING }, b) ?? "a1"
);
return min_move_by(board, distance_between_kings);
};
// cccp and pacifist are basically opposites
// ugh not really
function pieces_of_color(
b: Chess,
color: Color
): { square: Square; type: PieceSymbol; color: Color }[] {
let flat_board = b.board().flat();
let non_empties: { square: Square; type: PieceSymbol; color: Color }[] = [];
for (const s of flat_board) {
if (typeof s == "boolean") {
continue;
}
non_empties.push(s);
}
return non_empties.filter(p => p.color == color);
}
function num_pieces_of_color(b: Chess, color: Color): number {
return pieces_of_color(b, color).length;
}
// see generous in http://tom7.org/chess/weak.pdf
function value_piece(type: PieceSymbol): number {
switch (type) {
case "p":
return 1;
case "b":
case "n":
return 3;
case "r":
return 5;
case "q":
return 9;
case "k":
// impossible (mostly)
// idk why i said this it's really not impossible lol
return 413;
}
}
function value_of_pieces_of_color(b: Chess, color: Color): number {
return pieces_of_color(b, color)
.map(p => value_piece(p.type))
.reduce((a, b) => a + b);
}
// hm
const HUMAN_COLOR: Color = WHITE;
const ROBOT_COLOR: Color = BLACK;
// DONE: I THINK: UGH piece value
const cccp: Player = board => {
let cccp_score = (b: Chess) => {
if (b.isCheckmate()) {
console.log("Found checkmate! :D");
return 0;
}
if (b.isCheck()) {
console.log("Found check! :T");
return 1;
}
// maximize the difference in value
let prev_value = value_of_pieces_of_color(board, HUMAN_COLOR);
let new_value = value_of_pieces_of_color(b, HUMAN_COLOR);
if (new_value < prev_value) {
console.log("There's less human-colored pieces now! :)");
return 10 * (6 - (prev_value - new_value));
}
// sum the ranks of the robot pieces (brittle with piece colors changing which uhh just don't do that :))
// they are zero based oopsie
return (
100 *
pieces_of_color(b, ROBOT_COLOR)
.map(b => 8 - rank_and_file(b.square).rank)
.reduce((a, b) => a + b)
);
};
return min_move_by(board, cccp_score);
};
// FIXME: function name is TOO short FIX
function total_chebyshev_distances_of_pieces_of_color_to_square(
board: Chess,
color: Color,
square: Square
): number {
return pieces_of_color(board, color)
.map(p => chebyshev_distance(p.square, square))
.reduce((a, b) => a + b);
}
const huddle: Player = board => {
return min_move_by(board, b =>
total_chebyshev_distances_of_pieces_of_color_to_square(
b,
ROBOT_COLOR,
find({ color: ROBOT_COLOR, type: KING }, b) ?? "a1"
)
);
};
const swarm: Player = board => {
return min_move_by(board, b =>
total_chebyshev_distances_of_pieces_of_color_to_square(
b,
ROBOT_COLOR,
find({ color: HUMAN_COLOR, type: KING }, b) ?? "a1"
)
);
};
function sum_of_possible_capture_value(b: Chess): number {
let prev_value = value_of_pieces_of_color(b, ROBOT_COLOR);
let sum = 0;
for (const move of b.moves()) {
let new_board = with_move(b, move);
let new_value = value_of_pieces_of_color(new_board, ROBOT_COLOR);
// value only goes down..... pretty much
// but the negation is now done by the guys so
sum += prev_value - new_value;
}
return sum;
}
// surprisingly not too difficult
const generous: Player = board => {
// sum of the difference in ROBOT_COLOR piece value of all moves
// but like uhh negative
let generous_score = (b: Chess) => {
return -sum_of_possible_capture_value(b);
};
return min_move_by(board, generous_score);
};
2023-03-29 16:43:14 -04:00
const coward: Player = board =>
min_move_by(board, sum_of_possible_capture_value);
2023-03-26 23:33:41 -04:00
2023-03-29 16:43:14 -04:00
function no_i_insist_score(b: Chess) {
if (b.isCheckmate()) {
return 612;
}
if (b.isCheck()) {
return 413;
}
let prev_value = value_of_pieces_of_color(b, ROBOT_COLOR);
return -(sum_of_possible_capture_value(b) / b.moves().length);
}
const no_i_insist: Player = board => {
2023-03-26 23:33:41 -04:00
return min_move_by(board, no_i_insist_score);
};
const pacifist: Player = board => {
let pacifist_score = b => {
if (b.isCheckmate()) {
return 612;
}
if (b.isCheck()) {
return 413;
}
let prev_value = value_of_pieces_of_color(board, HUMAN_COLOR);
let new_value = value_of_pieces_of_color(b, HUMAN_COLOR);
if (new_value < prev_value) {
return prev_value - new_value;
}
return 0;
};
return min_move_by(board, pacifist_score);
};
const players = [
{
f: random_move,
name: "random_move",
description: "Moves randomly, believe it or not.",
},
{
f: same_color,
name: "same_color",
description:
"Makes the move that maximizes the number of pieces on their own color",
},
{
f: opposite_color,
name: "opposite_color",
description: "Behavior left as an exercise to the reader",
},
{
f: pacifist,
name: "pacifist",
description:
"Avoids checkmate, then avoids checks, then avoids captures, if forced, captures the lowest value piece it can",
},
{
f: first_move,
name: "first_move",
description:
"Makes the lexicographically first move based on [source_row, source_col, dest_row, dest_col, promotion]",
},
{
f: alphabetical,
name: "alphabetical",
description:
"Makes the first move asciilphabetically (based on algebraic notation)",
},
{
f: huddle,
name: "huddle",
description:
"Makes the move that minimizes the total Chebyshev distances from their pieces to their king",
},
{
f: swarm,
name: "swarm",
description:
"Makes the move that minimizes the total Chebyshev distances from their pieces to YOUR king",
},
{
f: generous,
name: "generous",
description:
"Maximizes the number of YOUR moves that take their pieces, weighting by value",
},
{
f: no_i_insist,
name: "no_i_insist",
description:
"Maximizes the proportion of potential capture value of YOUR moves to the number of YOUR moves; very weird the way i've done it",
},
2023-03-29 16:43:14 -04:00
{
f: coward,
name: "coward",
description:
"Surprisingly uncowardly, tries to avoid losing pieces, weighted by value",
},
2023-03-26 23:33:41 -04:00
{
f: reverse_starting,
name: "reverse_starting",
description:
"Makes the move that minimizes the number of moves it takes to move their pieces to the opposite end",
},
{
f: cccp,
name: "cccp",
description:
"First checkmates, then checks, then captures (the best piece it can), then pushes",
},
{
f: suicide_king,
name: "suicide_king",
description:
"Makes the move that minimizes the Chebyshev distance between the two kings",
},
{
f: sym_mirror_y,
name: "sym_mirror_y",
description:
"Tries to make the board as vertically symmetric as possible (basically copies your moves)",
},
{
f: sym_mirror_x,
name: "sym_mirror_x",
description:
"Tries to make the board as horizontally symmetric as possible (basically doesn't copy your moves or do anything useful)",
},
{
f: sym_180,
name: "sym_180",
description:
"Tries to make the board as symmetric as possible when flipped around",
},
{
f: min_oppt_move,
name: "min_oppt_move",
description: "Does the move that leaves the least moves for YOU",
},
{
f: equalizer,
name: "equalizer",
description:
"Moves the least moved piece (actually piece type, e.g. moving a pawn counts for all pawns; it sucks, i know), breaking ties by moving to the least visited square",
},
// alphabetical is fine i guess, since the checklist order has little to do with the og paper
// ehhh nvm
]; //.sort((a, b) => a.name.localeCompare(b.name));
// https://stackoverflow.com/questions/12739171/javascript-epoch-time-in-days
function days_since_epoch() {
var now = new Date().getMilliseconds();
return Math.floor(now / 8.64e7);
}
function find_specified_in_hash(): null | {
f: Player;
name: string;
description: string;
} {
return players.find(p => `#${p.name}` == window.location.hash);
}
// bad
// const todays_player = players[days_since_epoch() % players.length];
2023-03-29 19:20:55 -04:00
function get_todays_player() {
let player =
find_specified_in_hash() ??
players[Math.floor(Math.random() * players.length)];
console.log(`SPOILER: "today"'s player is ${player.name}`);
return player;
}
var todays_player = get_todays_player();
2023-03-26 23:33:41 -04:00
// const todays_player = min_oppt_move;
var board: any = document.getElementById("board");
2023-03-26 23:33:41 -04:00
var game = new Chess();
2023-03-26 23:33:41 -04:00
function computer_move() {
num_moves++;
2023-03-28 23:15:10 -04:00
history += "M";
2023-03-26 23:33:41 -04:00
return todays_player.f(game);
}
// might want to change this when it's more wordly
function show_game_over() {
let game_over = document.getElementById("game-over");
game_over.style.display = "block";
game_over.innerText = `GAME OVER; player was ${todays_player.name}`;
console.log("GAME OVER");
}
2023-03-29 19:42:39 -04:00
function copy_stats_to_clipboard() {
navigator.clipboard.writeText(document.getElementById("stats").innerText);
}
2023-03-26 23:33:41 -04:00
function show_win() {
2023-03-29 19:20:55 -04:00
wins++;
2023-03-29 19:42:39 -04:00
in_game_over = true;
2023-03-29 19:35:50 -04:00
let win_count = document.getElementById("win-count");
win_count.style.display = "block";
win_count.innerText = `You have won ${wins} time${
wins == 1 ? "" : "s"
} without refreshing${wins_exclamation_marks()}`;
2023-03-29 19:20:55 -04:00
2023-03-26 23:33:41 -04:00
let game_over = document.getElementById("game-over");
game_over.style.display = "block";
game_over.innerText = "YOU WIN YOU WIN YOU WIN YOU WIN HOLY CRAP";
let stats = document.getElementById("stats");
stats.style.display = "block";
2023-03-28 23:15:10 -04:00
let stats_text = `#elo_worldle
#ew_${todays_player.name}
2023-03-29 19:35:50 -04:00
Win #${wins}
2023-03-28 23:15:10 -04:00
Guesses: ${num_guesses}
Moves: ${num_moves}
${history}`;
2023-03-26 23:33:41 -04:00
stats.innerText = stats_text;
let copy_stats = document.getElementById("copy-stats");
copy_stats.style.display = "block";
2023-03-29 19:42:39 -04:00
copy_stats.addEventListener("click", _ => copy_stats_to_clipboard());
2023-03-26 23:33:41 -04:00
let play_again = document.getElementById("play-again");
play_again.style.display = "block";
play_again.addEventListener("click", _ => reset());
2023-03-26 23:33:41 -04:00
console.log("PLAYER IS WIN");
}
var num_guesses = 0;
var num_moves = 0;
2023-03-28 23:15:10 -04:00
var history = "";
2023-03-26 23:33:41 -04:00
function append_players_to(n: Node) {
for (const player of players) {
let child = document.createElement("div");
child.classList.add("player");
let child_name = document.createElement("h1");
child_name.classList.add("player-name");
child_name.innerText = player.name;
let child_desc = document.createElement("p");
child_desc.classList.add("player-desc");
child_desc.innerText = player.description;
let guess_button = document.createElement("button");
guess_button.innerText = "^ guess ^";
guess_button.onclick = _ => {
num_guesses++;
2023-03-28 23:15:10 -04:00
history += "G";
2023-03-26 23:33:41 -04:00
if (todays_player.name == player.name) {
show_win();
child.classList.add("correct");
} else {
child.classList.add("wrong");
}
guess_button.onclick = () => {};
};
child.appendChild(child_name);
child.appendChild(child_desc);
child.appendChild(guess_button);
child_name.addEventListener("click", _ =>
child.classList.toggle("strikethrough")
);
n.appendChild(child);
}
}
2023-03-29 19:20:55 -04:00
var wins = 0;
// extraordinarily necessary
function wins_exclamation_marks() {
2023-03-29 22:15:25 -04:00
let base_10_log_of_wins = Math.floor(Math.log10(wins));
return base_10_log_of_wins > 0 ? "!".repeat(base_10_log_of_wins) : ".";
2023-03-29 19:20:55 -04:00
}
2023-03-29 19:35:50 -04:00
2023-03-29 19:42:39 -04:00
var in_game_over = false;
// ERROR: !
// INELEGANT
// "HACK"!
function reset() {
2023-03-29 19:20:55 -04:00
let play_again = document.getElementById("play-again");
play_again.style.display = "none";
play_again.onclick = _ =>
console.log("you can't play again again sorry :]");
2023-03-29 19:42:39 -04:00
in_game_over = false;
2023-03-29 19:20:55 -04:00
todays_player = get_todays_player();
game = new Chess();
board.fen = game.fen();
board.turn = "white";
board.interactive = true;
num_guesses = 0;
num_moves = 0;
history = "";
equalizer_state = equalizer_state_default;
let players = document.getElementById("players");
players.innerHTML = "";
append_players_to(players);
let stats = document.getElementById("stats");
stats.innerHTML = "";
stats.style.display = "none";
document.getElementById("copy-stats").style.display = "none";
document.getElementById("game-over").style.display = "none";
}
2023-03-26 23:33:41 -04:00
window.addEventListener("DOMContentLoaded", () => {
board.addEventListener("movestart", (e: any) => {
console.log(
`Move started: ${e.detail.from}, ${e.detail.piece.color} ${e.detail.piece.pieceType}`
);
e.detail.setTargets(
// This produces a list like ["e3", "e5"]
game.moves({ square: e.detail.from, verbose: true }).map(m => m.to)
);
if (game.isGameOver()) {
show_game_over();
}
});
board.addEventListener("moveend", (e: any) => {
console.log(
`Move ending: ${e.detail.from} -> ${e.detail.to}, ${e.detail.piece.color} ${e.detail.piece.pieceType}`
);
const move = game.move({
from: e.detail.from,
to: e.detail.to,
promotion: "q",
});
if (move === null) {
e.preventDefault();
}
if (game.isGameOver()) {
show_game_over();
}
});
board.addEventListener("movefinished", (e: any) => {
board.fen = game.fen();
board.turn = game.turn() === "w" ? "white" : "black";
board.interactive = game.turn() === "w";
if (!board.interactive) {
let move = computer_move();
if (!move) {
console.log(
"Didn't get a move from the computer (this is fine if the game is over)"
);
return;
}
console.log(`Computer move: ${move}`);
game.move(move);
board.fen = game.fen();
board.turn = "white";
board.interactive = true;
}
});
document
.getElementById("finish")
.addEventListener("click", _ => show_game_over());
2023-03-29 19:42:39 -04:00
document.addEventListener("keypress", e => {
if (e.key == "r" && in_game_over) {
reset();
} else if (e.key == "c" && in_game_over) {
copy_stats_to_clipboard();
}
});
2023-03-26 23:33:41 -04:00
append_players_to(document.getElementById("players"));
});