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:
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);
};
};