betterchess

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit ae3afc57e59b11e1e7f015115445ae7e089003d0
parent 516b6ba7da53d270c1f293af97ff81369df1ae0c
Author: thing1 <thing1@seacrossedlovers.xyz>
Date:   Fri, 23 Jan 2026 23:14:11 +0000

switched over to a hash table for storing the board, this is to allow
for fast lookup, which will be used heavily when making the check
checker

Diffstat:
MMakefile | 2+-
Mchess/chess.ha | 27++++++++++++---------------
Achess/map.ha | 36++++++++++++++++++++++++++++++++++++
Mchess/moves.ha | 3+--
Mcmd/betterchess/main.ha | 10++++++++--
5 files changed, 58 insertions(+), 20 deletions(-)

diff --git a/Makefile b/Makefile @@ -2,7 +2,7 @@ .SUFFIXES: HARE=hare -HAREFLAGS=-lc +HAREFLAGS= DESTDIR= PREFIX=/usr/local diff --git a/chess/chess.ha b/chess/chess.ha @@ -32,15 +32,13 @@ export type piece = struct { export type board = struct { w: i64, h: i64, - pieces: []piece, + m: *map }; -export fn mkboard(w: i64, h: i64) board = { - return board{w = w, h = h, pieces = []}; -}; +export fn mkboard(w: i64, h: i64) board = board{w = w, h = h, m = mkmap((((w * h) / 4) * 3) : size)}; export fn finish(b: *board) void = { - free(b.pieces); + finishmap(b.m); }; export fn mkpiece(b: *board, x: i64, y: i64, ty: ptype, team: color) (*piece | !invalidplace) = { @@ -57,16 +55,13 @@ export fn mkpiece(b: *board, x: i64, y: i64, ty: ptype, team: color) (*piece | ! case ptype::PAWN => yield &pawnmove; }; - append(b.pieces, piece{x = x, y = y, ty = ty, team = team, valid = valid, moved = false, dead = false})!; - return &b.pieces[len(b.pieces) - 1]; + addmap(b.m, x, y, alloc(piece{x = x, y = y, ty = ty, team = team, valid = valid, moved = false, dead = false})!: *piece); + return getmap(b.m, x, y)!; }; export fn getpiece(b: *board, x: i64, y: i64) (*piece | !empty | !invalidplace) = { if (x < 0 || x >= b.w || y < 0 || y >= b.h) return invalidplace; - for (let p &.. b.pieces) - if (p.x == x && p.y == y && !p.dead) - return p; - return empty; + return getmap(b.m, x, y); }; export fn movepiece(b: *board, x1: i64, y1: i64, x2: i64, y2: i64) (void | !empty | !invalidmove | !invalidplace) = { @@ -84,9 +79,11 @@ export fn movepiece(b: *board, x1: i64, y1: i64, x2: i64, y2: i64) (void | !empt }; export fn print_board(b: board, f: io::handle) void = { - for (let p .. b.pieces) { - if (p.dead) continue; - let text = if (p.team == color::WHITE) ascii::toupper(p.ty: rune) else p.ty: rune; - fmt::fprintf(f, "{} {} {}\n", text, p.x, p.y)!; + for (let ps .. b.m.1) { + for (let p .. ps) { + if (p.dead) continue; + let text = if (p.team == color::WHITE) ascii::toupper(p.ty: rune) else p.ty: rune; + fmt::fprintf(f, "{} {} {}\n", text, p.x, p.y)!; + }; }; }; diff --git a/chess/map.ha b/chess/map.ha @@ -0,0 +1,36 @@ +use fmt; + +export type map = (size, [][]*piece); + +const fakepiece = piece{x = 0, y = 0, ty = ptype::KING, team = color::WHITE, valid = &kingmove, moved = false, dead = false}; + +// this function is a bit messy, but it provides a pretty good spread of values +fn hash(x: i64, y: i64, buckets: size) size = (((x ^ y) * (x | y)) | x * y): size % buckets; + +fn addmap(m: *map, x: i64, y: i64, p: *piece) void = { + let i = hash(x, y, m.0); + append(m.1[i], p)!; +}; + +fn getmap(m: *map, x: i64, y: i64) (*piece | !empty) = { + let i = hash(x, y, m.0); + for (let p .. m.1[i]) { + if (p.x == x && p.y == y) + return p; + }; + return empty; +}; + +fn mkmap(buckets: size) *map = alloc((buckets, alloc([[]: []*piece...], buckets)!))!; + +fn finishmap(m: *map) void = { + free(m.1); +}; + +@test fn getmap() void = { + let m = mkmap(5); + defer finishmap(m); + + addmap(m, 0, 0, &fakepiece); + assert(getmap(m, 0, 0) is *piece); +}; diff --git a/chess/moves.ha b/chess/moves.ha @@ -1,5 +1,4 @@ use math; -use fmt; fn invalid(x2: i64, y2: i64) bool = if (x2 < 0 || y2 < 0 || x2 >= 8 || y2 >= 8) true @@ -92,8 +91,8 @@ fn pawnmove(p: piece, b: board, x2: i64, y2: i64) bool = { if (x2 == p.x + 1 || x2 == p.x - 1 || x2 == p.x) { match (getpiece(&b, x2, y2)) { - case empty => return (x2 == p.x); case let p2: *piece => return (x2 != p.x && p2.team != p.team); + case !empty => return (x2 == p.x); case => return false; }; }; diff --git a/cmd/betterchess/main.ha b/cmd/betterchess/main.ha @@ -64,12 +64,18 @@ export fn main() void = { let iswhite = true; for (true) { + chess::print_board(b, os::stdout); + fmt::printf("{}'s move:\n", if (iswhite) "white" else "black")!; let m = parseinput(); - chess::movepiece(&b, m.x1, m.y1, m.x2, m.y2)!; + match (chess::movepiece(&b, m.x1, m.y1, m.x2, m.y2)) { + case void => yield; + case !chess::empty => fmt::fatal("space is empty"); + case !chess::invalidmove => fmt::fatal("invalid move"); + case !chess::invalidplace => fmt::fatal("invalid place"); + }; iswhite = !iswhite; - chess::print_board(b, os::stdout); }; };