betterchess

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

commit aab172c73b463fd843910fe2bc5c5044e1438659
parent b5b8e9b5efe9a97a565a17c725cea3bd1b6d4d96
Author: thing1 <thing1@seacrossedlovers.xyz>
Date:   Wed, 21 Jan 2026 00:00:19 +0000

added move checks for every piece

Diffstat:
Mchess/chess.ha | 40+++++++++++++++++++---------------------
Mchess/moves.ha | 126+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mcmd/betterchess/main.ha | 12++++++++++--
3 files changed, 108 insertions(+), 70 deletions(-)

diff --git a/chess/chess.ha b/chess/chess.ha @@ -16,23 +16,24 @@ export type ptype = enum rune { }; export type color = enum {BLACK = 'b', WHITE = 'w'}; -export type movefn = *fn(_: i64, _: i64, _: i64, _: i64) bool; +export type movefn = *fn(_: piece, _: board, _: i64, _: i64) bool; export type piece = struct { - x: size, - y: size, + x: i64, + y: i64, ty: ptype, team: color, - valid: movefn + valid: movefn, + moved: bool }; export type board = struct { - w: size, - h: size, + w: i64, + h: i64, pieces: []piece, }; -export fn mkboard(w: size, h: size) board = { +export fn mkboard(w: i64, h: i64) board = { return board{w = w, h = h, pieces = []}; }; @@ -40,7 +41,7 @@ export fn finish(b: *board) void = { free(b.pieces); }; -export fn mkpiece(b: *board, x: size, y: size, ty: ptype, team: color) (*piece | !invalidplace) = { +export fn mkpiece(b: *board, x: i64, y: i64, ty: ptype, team: color) (*piece | !invalidplace) = { if (x < 0 || x >= b.w || y < 0 || y >= b.h) return invalidplace; let valid = switch(ty) { @@ -49,14 +50,14 @@ export fn mkpiece(b: *board, x: size, y: size, ty: ptype, team: color) (*piece | case ptype::BISHOP => yield &bishopmove; case ptype::KNIGHT => yield &knightmove; case ptype::ROOK => yield &rookmove; - case ptype::PAWN => yield &truemove; + case ptype::PAWN => yield &pawnmove; }; - append(b.pieces, piece{x = x, y = y, ty = ty, team = team, valid = valid})!; + append(b.pieces, piece{x = x, y = y, ty = ty, team = team, valid = valid, moved = false})!; return &b.pieces[len(b.pieces) - 1]; }; -export fn getpiece(b: *board, x: size, y: size) (*piece | !empty | !invalidplace) = { +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) @@ -64,20 +65,17 @@ export fn getpiece(b: *board, x: size, y: size) (*piece | !empty | !invalidplace return empty; }; -export fn movepiece(b: *board, x1: size, y1: size, x2: size, y2: size) (void | !empty | !invalidmove | !invalidplace) = { - let p = getpiece(b, x1, x2)?; - if (p.ty == ptype::PAWN) - if (!p.valid(x1: i64, y1: i64, x2: i64, y2: i64)) return invalidmove; +export fn movepiece(b: *board, x1: i64, y1: i64, x2: i64, y2: i64) (void | !empty | !invalidmove | !invalidplace) = { + let p = getpiece(b, x1, y1)?; + if (!p.valid(*p, *b, x2, y2)) return invalidmove; + p.x = x2; + p.y = y2; + p.moved = true; }; export fn print_board(b: board, f: io::handle) void = { - const w = 500z; - const h = 500z; - for (let p .. b.pieces) { - let x = (w / b.w) * p.x; - let y = (h / b.h) * p.y + 23; let text = if (p.team == color::WHITE) ascii::toupper(p.ty: rune) else p.ty: rune; - fmt::fprintf(f, "{} {} {}\n", text, x, y)!; + fmt::fprintf(f, "{} {} {}\n", text, p.x, p.y)!; }; }; diff --git a/chess/moves.ha b/chess/moves.ha @@ -1,70 +1,102 @@ use math; +use fmt; fn invalid(x2: i64, y2: i64) bool = if (x2 < 0 || y2 < 0 || x2 >= 8 || y2 >= 8) true else false; -export fn kingmove(x1: i64, y1: i64, x2: i64, y2: i64) bool = - if (invalid(x2, y2)) false - else if (math::absi64(x2 - x1) > 1 || math::absi64(y2 - y1) > 1) false - else true; +fn selftake(p: piece, b: board, x2: i64, y2: i64) bool = + match (getpiece(&b, x2, y2)) { + case let p2: *piece => return (p2.team == p.team); + case => return false; + }; + +fn checkdiag(p: piece, b: board, x2: i64, y2: i64) bool = { + if (math::absi64(x2 - p.x) == math::absi64(y2 - p.y)) { + let xdir = if ((x2 - p.x) > 0) 1 else -1; + let ydir = if ((y2 - p.y) > 0) 1 else -1; + let y = p.y + ydir; + for (let x = p.x + xdir; x != x2; x += xdir) { + let p2 = getpiece(&b, x, y); + if (!(p2 is empty)) return false; + y += ydir; + }; + return true; + }; + + return false; +}; + +fn checkstraight(p: piece, b: board, x2: i64, y2: i64) bool = { + if (p.x == x2) { + let xdir = if ((x2 - p.x) > 0) 1 else -1; + for (let x = p.x; x != x2; x += xdir) { + let p2 = getpiece(&b, x, p.y); + if (!(p2 is empty)) return false; + }; + return true; + } else if (p.y == y2) { + let ydir = if ((y2 - p.y) > 0) 1 else -1; + for (let y = p.y; y != y2; y += ydir) { + let p2 = getpiece(&b, p.x, y); + if (!(p2 is empty)) return false; + }; + return true; + }; + + return false; +}; + +fn kingmove(p: piece, b: board, x2: i64, y2: i64) bool = { + if (invalid(x2, y2)) return false + else if (selftake(p, b, x2, y2)) return false + else if (math::absi64(x2 - p.x) > 1 || math::absi64(y2 - p.y) > 1) return false; -export fn queenmove(x1: i64, y1: i64, x2: i64, y2: i64) bool = + let p2 = getpiece(&b, x2, y2); + if (p2 is empty || (p2 as *piece).team != p.team) return true; + + return false; +}; + +fn queenmove(p: piece, b: board, x2: i64, y2: i64) bool = if (invalid(x2, y2)) false - else if (math::absi64(x2 - x1) == math::absi64(y2 - y1) || x1 == x2 || y1 == y2) true + else if (selftake(p, b, x2, y2)) false + else if (checkdiag(p, b, x2, y2)) true + else if (checkstraight(p, b, x2, y2)) true else false; -export fn bishopmove(x1: i64, y1: i64, x2: i64, y2: i64) bool = +fn bishopmove(p: piece, b: board, x2: i64, y2: i64) bool = if (invalid(x2, y2)) false - else if (math::absi64(x2 - x1) == math::absi64(y2 - y1)) true + else if (selftake(p, b, x2, y2)) false + else if (checkdiag(p, b, x2, y2)) true else false; -export fn rookmove(x1: i64, y1: i64, x2: i64, y2: i64) bool = +fn rookmove(p: piece, b: board, x2: i64, y2: i64) bool = if (invalid(x2, y2)) false - else if (x1 == x2 || y1 == y2) true + else if (selftake(p, b, x2, y2)) false + else if (checkstraight(p, b, x2, y2)) true else false; -export fn knightmove(x1: i64, y1: i64, x2: i64, y2: i64) bool = +fn knightmove(p: piece, b: board, x2: i64, y2: i64) bool = if (invalid(x2, y2)) false - else if (math::absi64(x2 - x1) == 1 && math::absi64(y2 - y1) == 2) true - else if (math::absi64(x2 - x1) == 2 && math::absi64(y2 - y1) == 1) true + else if (selftake(p, b, x2, y2)) return false + else if (math::absi64(x2 - p.x) == 1 && math::absi64(y2 - p.y) == 2) true + else if (math::absi64(x2 - p.x) == 2 && math::absi64(y2 - p.y) == 1) true else false; -export fn pawnmove(x1: i64, y1: i64, x2: i64, y2: i64, b: board) bool = { +fn pawnmove(p: piece, b: board, x2: i64, y2: i64) bool = { if (invalid(x2, y2)) return false + else if (selftake(p, b, x2, y2)) return false + else if (p.moved && math::absi64(y2 - p.y) > 1) return false + else if (!p.moved && math::absi64(y2 - p.y) > 2) return false; - let p = getpiece(b, x1, y1)!; - // white is on the 0 row - if (p.team == color::WHITE) { - if (y2 <= y1) return false; + 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 => return false; + }; }; - else { - if (y2 >= y1) return false; - }; - // TODO here - // -}; - -export fn truemove(x1: i64, y1: i64, x2: i64, y2: i64) bool = !invalid(x2, y2); - -@test fn kingmove() void = assert(kingmove(4, 0, 4, 1) == true, "(4, 0) -> (4, 1) should be valid"); -@test fn kingmove() void = assert(kingmove(4, 0, 4, 2) == false, "(4, 0) -> (4, 2) should be invalid"); -@test fn kingmove() void = assert(kingmove(4, 0, 4, -1) == false, "(4, 0) -> (4, -1) should be invalid"); -@test fn kingmove() void = assert(kingmove(4, 0, 3, 0) == true, "(4, 0) -> (3, 0) should be valid"); - -@test fn queenmove() void = assert(queenmove(3, 2, 3, 7) == true, "(3, 2) -> (3, 7) should be valid"); -@test fn queenmove() void = assert(queenmove(2, 3, 7, 2) == false, "(2, 3) -> (7, 2) should be invalid"); -@test fn queenmove() void = assert(queenmove(3, 3, 6, 6) == true, "(3, 3) -> (6, 6) should be valid"); -@test fn queenmove() void = assert(queenmove(3, 3, 6, 5) == false, "(3, 3) -> (6, 5) should be invalid"); - -@test fn bishopmove() void = assert(bishopmove(3, 3, 6, 6) == true, "(3, 3) -> (6, 6) should be valid"); -@test fn bishopmove() void = assert(bishopmove(3, 3, 6, 2) == false, "(3, 3) -> (6, 2) should be invalid"); - -@test fn rookmove() void = assert(rookmove(3, 3, 6, 6) == false, "(3, 3) -> (6, 6) should be invalid"); -@test fn rookmove() void = assert(rookmove(3, 3, 3, 7) == true, "(3, 3) -> (3, 7) should be valid"); - -@test fn knightmove() void = assert(knightmove(0, 1, 2, 2) == true, "(0, 1) -> (2, 2) should be valid"); -@test fn knightmove() void = assert(knightmove(2, 2, 0, 1) == true, "(2, 2) -> (0, 1) should be valid"); -@test fn knightmove() void = assert(knightmove(2, 3, 0, 1) == false, "(2, 3) -> (0, 1) should be invalid"); -@test fn knightmove() void = assert(knightmove(2, 0, 0, 1) == true, "(2, 0) -> (0, 1) should be valid"); + return false; +}; diff --git a/cmd/betterchess/main.ha b/cmd/betterchess/main.ha @@ -7,14 +7,22 @@ use chess; export fn main() void = { let b = chess::mkboard(8, 8); - let p = match (chess::mkpiece(&b, 0, 0, chess::ptype::ROOK, chess::color::WHITE)) { + let p = match (chess::mkpiece(&b, 3, 6, chess::ptype::PAWN, chess::color::BLACK)) { case let p: *chess::piece => yield p; case => fmt::fatal("invalid placement"); }; - let p = match (chess::mkpiece(&b, 1, 0, chess::ptype::BISHOP, chess::color::WHITE)) { + let p = match (chess::mkpiece(&b, 2, 5, chess::ptype::PAWN, chess::color::WHITE)) { case let p: *chess::piece => yield p; case => fmt::fatal("invalid placement"); }; + + match (chess::movepiece(&b, 3, 6, 3, 4)) { + case chess::empty => fmt::println("empty space")!; + case chess::invalidmove => fmt::println("invalid move")!; + case chess::invalidplace => fmt::println("invalid place")!; + case => fmt::println("valid move")!; + }; + chess::print_board(b, os::stdout); };