Compare commits

..

3 commits

Author SHA1 Message Date
05debd41f1 show ascii 2026-07-25 17:35:59 +07:00
7b396243ad detail display 2026-07-25 17:34:29 +07:00
43d1ed7932 board updating subscriber and listener 2026-07-25 17:10:41 +07:00
4 changed files with 76 additions and 12 deletions

View file

@ -1,3 +1,6 @@
import { Game } from "./utils";
export type GameListener = (board: Game) => void;
export enum Piece { export enum Piece {
EMPTY = 0, EMPTY = 0,
KING = 1, KING = 1,

View file

@ -1,8 +1,8 @@
import { CHAR_TO_PIECE, Piece } from "./types"; import { CHAR_TO_PIECE, Piece, PIECE_TO_CHAR, type GameListener } from "./types";
export const startFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0"; export const startFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0";
export class Board { export class Game {
public board = new Int8Array(8 ** 2); public board = new Int8Array(8 ** 2);
public details = { public details = {
whiteTurn: true, whiteTurn: true,
@ -15,10 +15,21 @@ export class Board {
fullMove: 0, fullMove: 0,
}; };
private listeners = new Set<GameListener>();
constructor() { constructor() {
this.loadFEN(); this.loadFEN();
} }
private notify() {
for (const listener of this.listeners) listener(this);
}
subscribe(listener: GameListener): () => void {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
loadFEN(fen = startFEN) { loadFEN(fen = startFEN) {
const [position, turn, castling, enPassant, halfMove, fullMove] = fen.split(" "); const [position, turn, castling, enPassant, halfMove, fullMove] = fen.split(" ");
@ -43,9 +54,20 @@ export class Board {
this.details.halfMove = parseInt(halfMove); this.details.halfMove = parseInt(halfMove);
this.details.fullMove = parseInt(fullMove); this.details.fullMove = parseInt(fullMove);
this.notify();
} }
} }
export function boardToAscii(board: Int8Array): string {
let ascii = '';
board.forEach((piece, i) => {
if (i % 8 === 0 && i !== 0) ascii += '\n';
ascii += PIECE_TO_CHAR[piece];
});
return ascii;
}
function algebraicToIndex(algebraic: string): number { function algebraicToIndex(algebraic: string): number {
const FILE_TO_INDEX: Record<string, number> = { const FILE_TO_INDEX: Record<string, number> = {
a: 0, b: 1, c: 2, d: 3, e: 4, f: 5, g: 6, h: 7, a: 0, b: 1, c: 2, d: 3, e: 4, f: 5, g: 6, h: 7,

View file

@ -1,20 +1,25 @@
<script lang="ts"> <script lang="ts">
import { PIECE_TO_CHAR } from "$lib/chess/types"; import { PIECE_TO_CHAR } from "$lib/chess/types";
import { Board, startFEN } from "$lib/chess/utils"; import { boardToAscii, Game, startFEN } from "$lib/chess/utils";
const board = new Board();
let boardView = $state(Array.from(board.board));
let showIndex = $state(false); let showIndex = $state(false);
let fen = $state(startFEN); let fen = $state(startFEN);
const game = new Game();
let board = $state(Array.from(game.board));
let ascii = $state(boardToAscii(game.board));
let details = $state(game.details)
game.subscribe(updateView);
function updateView() { function updateView() {
boardView = Array.from(board.board); board = Array.from(game.board);
ascii = boardToAscii(game.board);
details = game.details;
} }
</script> </script>
<div class="grid grid-cols-8 gap-0 border-2"> <div class="grid grid-cols-8 gap-0 border-2">
{#each boardView as square, index} {#each board as square, index}
<div class="h-20 aspect-square relative flex justify-center items-center <div class="h-20 aspect-square relative flex justify-center items-center
{index % 2 === Math.floor(index / 8) % 2 ? 'bg-orange-200 text-stone-800' : 'bg-amber-800 text-stone-200'} {index % 2 === Math.floor(index / 8) % 2 ? 'bg-orange-200 text-stone-800' : 'bg-amber-800 text-stone-200'}
"> ">
@ -24,6 +29,37 @@
{/each} {/each}
</div> </div>
<div> <div>
<pre>{ascii}</pre>
<ul>
{#each Object.entries(game.details).filter(([k]) => k !== "castling") as [key, val]}
<li>
<strong>{key}</strong>:
{val.toString()}
</li>
{/each}
<table>
<thead>
<tr>
<th>Castling</th>
<th>King</th>
<th>Queen</th>
</tr>
</thead>
<tbody>
<tr>
<th>White</th>
<td>{game.details.castling.white.king}</td>
<td>{game.details.castling.white.queen}</td>
</tr>
<tr>
<th>Black</th>
<td>{game.details.castling.black.king}</td>
<td>{game.details.castling.black.queen}</td>
</tr>
</tbody>
</table>
</ul>
<hr class="border border-stone-200 my-4">
<label class="block"> <label class="block">
show index show index
<input type="checkbox" bind:checked={showIndex} /> <input type="checkbox" bind:checked={showIndex} />
@ -31,10 +67,9 @@
<label class="block"> <label class="block">
fen (enter to set): fen (enter to set):
<input type="text" onkeypress={(e) => e.key === 'Enter' && (() => { <input type="text" onkeypress={(e) => e.key === 'Enter' && (() => {
board.loadFEN(fen) game.loadFEN(fen);
updateView()
})()} bind:value={fen} /> })()} bind:value={fen} />
</label> </label>
<button onclick={updateView}>update view</button> <button onclick={updateView}>update view</button>
<button onclick={() => console.log(board)}>dump game to console</button> <button onclick={() => console.log(game)}>dump game to console</button>
</div> </div>

View file

@ -4,3 +4,7 @@ html, body { @apply h-full; }
body { @apply bg-gray-900 text-gray-100; } body { @apply bg-gray-900 text-gray-100; }
input, button { @apply bg-stone-200 text-stone-800 px-2 py-1 m-1 rounded; } input, button { @apply bg-stone-200 text-stone-800 px-2 py-1 m-1 rounded; }
table { @apply w-full border-collapse; }
th, td { @apply border border-gray-700 px-2 py-1 text-center }
th { @apply font-bold; }