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;
// }
// 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 ( ) ;
// console.log(moves);
for ( const i in moves ) {
let move = moves [ i ] ;
try {
let new_board = with_move ( board , move ) ;
let new_score = score ( new_board ) ;
// console.log({ move, best_score, new_score, best_move });
if (
new_score < best_score ||
( new_score == best_score && Math . random ( ) > 0.5 )
) {
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
var equalizer_state : {
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
}
} ;
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 ) ;
} ;
const no_i_insist : Player = board = > {
let 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 ) ;
} ;
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" ,
} ,
{
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];
const todays_player =
find_specified_in_hash ( ) ? ?
players [ Math . floor ( Math . random ( ) * players . length ) ] ;
console . log ( ` SPOILER: today's player is ${ todays_player . name } ` ) ;
// const todays_player = min_oppt_move;
const board : any = document . getElementById ( "board" ) ;
const game = new Chess ( ) ;
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" ) ;
}
function show_win() {
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 }
pyrope . net / ew
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" ;
copy_stats . addEventListener ( "click" , _ = >
navigator . clipboard . writeText ( stats_text )
) ;
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 ) ;
}
}
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 ( ) ) ;
append_players_to ( document . getElementById ( "players" ) ) ;
} ) ;