diff --git a/.gitignore b/.gitignore index 7fc71cc..50ab282 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /a.out +/.zig-cache/ +/zig-out/ diff --git a/README.md b/README.md index a3e0074..7cdbb71 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ # RatChess -A chess engine made for fun \ No newline at end of file +A chess engine made for fun + +## Branch +Port to zig for fun and to learn zig diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..2d033e1 --- /dev/null +++ b/build.zig @@ -0,0 +1,36 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const exe_mod = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + + const exe = b.addExecutable(.{ .name = "RatChess", .root_module = exe_mod }); + + exe.linkLibC(); + exe.addIncludePath(b.path("headers/")); + exe.addCSourceFile(.{ .file = b.path("src/eval.c") }); + exe.addCSourceFile(.{ .file = b.path("src/help.c") }); + exe.addCSourceFile(.{ .file = b.path("src/main.c") }); + exe.addCSourceFile(.{ .file = b.path("src/moves.c") }); + b.installArtifact(exe); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + + if (b.args) |args| run_cmd.addArgs(args); + + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + const exe_unit_tests = b.addTest(.{ .root_module = exe_mod }); + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&run_exe_unit_tests.step); +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..1d63e43 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,86 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = .RatChess, + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + + // Together with name, this represents a globally unique package + // identifier. This field is generated by the Zig toolchain when the + // package is first created, and then *never changes*. This allows + // unambiguous detection of one package being an updated version of + // another. + // + // When forking a Zig project, this id should be regenerated (delete the + // field and run `zig build`) if the upstream project is still maintained. + // Otherwise, the fork is *hostile*, attempting to take control over the + // original project's identity. Thus it is recommended to leave the comment + // on the following line intact, so that it shows up in code reviews that + // modify the field. + .fingerprint = 0xad90dd593d9885b0, // Changing this has security and trust implications. + + // Tracks the earliest Zig version that the package considers to be a + // supported use case. + .minimum_zig_version = "0.14.1", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. If the contents of a URL change this will result in a hash mismatch + // // which will prevent zig from using it. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + // + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/headers/main.h b/headers/main.h new file mode 100644 index 0000000..d4157b5 --- /dev/null +++ b/headers/main.h @@ -0,0 +1,4 @@ +#ifndef MAIN_H +#define MAIN_H +int cmain(); +#endif diff --git a/headers/types.h b/headers/types.h index 7aa69c9..4bc75aa 100644 --- a/headers/types.h +++ b/headers/types.h @@ -3,12 +3,12 @@ #include typedef struct { - long long pawns; - long long knights; - long long bishops; - long long rooks; - long long queen; - long long king; + unsigned long long pawns; + unsigned long long knights; + unsigned long long bishops; + unsigned long long rooks; + unsigned long long queen; + unsigned long long king; } sets; typedef struct { diff --git a/shell.nix b/shell.nix index ba4a69b..f1155e7 100644 --- a/shell.nix +++ b/shell.nix @@ -1,11 +1,9 @@ {pkgs ? import {}}: with pkgs; mkShell rec { - packages = [gdb clang-tools uchess cutechess]; + packages = [gdb zls uchess cutechess]; nativeBuildInputs = [ - pkg-config - gcc - gnumake + zig ]; buildInputs = [ ]; diff --git a/src/eval.c b/src/eval.c index 8ace056..74dfbb9 100644 --- a/src/eval.c +++ b/src/eval.c @@ -56,20 +56,20 @@ int evaluateBoard(game *game) { } void makeMove(game *g, move* m) { - long long from_bit = 1LL << m->From; - long long to_bit = 1LL << m->To; + unsigned long long from_bit = 1ULL << m->From; + unsigned long long to_bit = 1ULL << m->To; - long long *set = findSet(g, from_bit); + unsigned long long *set = findSet(g, from_bit); if (!set) return; *set &= ~from_bit; - long long *captured = findSet(g, to_bit); + unsigned long long *captured = findSet(g, to_bit); if (captured) *captured &= ~to_bit; if (m->Promo) { char promoChar = g->whiteToMove ? toupper(m->Promo) : m->Promo; - long long *newSet = charToSet(g, promoChar); + unsigned long long *newSet = charToSet(g, promoChar); if (newSet) *newSet |= to_bit; } else { diff --git a/src/main.c b/src/main.c index c607749..f41bce7 100644 --- a/src/main.c +++ b/src/main.c @@ -18,7 +18,7 @@ void playMoves(game *g, char *moves); long long *findSet(game *g, long long bit); long long *charToSet(game *g, char c); -int main() { +int cmain() { setbuf(stdin, NULL); setbuf(stdout, NULL); game *g = NULL; @@ -112,7 +112,7 @@ game *fenGame(char *str) { continue; } - long long bit = 1LL << (rank * 8 + file); + long long bit = 1ULL << (rank * 8 + file); long long *set = charToSet(g, *str); if (set) *set |= bit; diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..ff253a2 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,8 @@ +const std = @import("std"); +const c = @cImport({ + @cInclude("main.h"); +}); + +pub fn main() !void { + _ = c.cmain(); +} diff --git a/src/moves.c b/src/moves.c index dafb464..e1ba8d1 100644 --- a/src/moves.c +++ b/src/moves.c @@ -12,7 +12,7 @@ int pawnMove(game *g, move *moves) { long long pawns = g->whiteToMove ? g->white.pawns : g->black.pawns; long long enemy = g->whiteToMove ? fullSet(&g->black) : fullSet(&g->white); long long occupied = fullSetBoth(g); - long long promo = (0xFFLL) << (g->whiteToMove ? 56 : 0); + long long promo = (0xFFULL) << (g->whiteToMove ? 56 : 0); int movdir = g->whiteToMove ? 8 : -8; int capLeft = g->whiteToMove ? 7 : -9; @@ -21,29 +21,29 @@ int pawnMove(game *g, move *moves) { size_t index = 0; for (int i = 0; i < 64; ++i) { - long long bit = 1LL << i; + long long bit = 1ULL << i; char p = 0; if (!(pawns & bit)) continue; // forword int to = i + movdir; - p = (promo & (1LL << to)) ? 'q' : 0; - if (to >= 0 && to < 64 && !(occupied & (1LL << to))) { + p = (promo & (1ULL << to)) ? 'q' : 0; + if (to >= 0 && to < 64 && !(occupied & (1ULL << to))) { moves[index++] = (move){.From = i, .To = to, .Promo = p}; } // left to = i + capLeft; - p = (promo & (1LL << to)) ? 'q' : 0; - if (i % 8 > 0 && (to >= 0 && to < 64) && (enemy & (1LL << to))) { + p = (promo & (1ULL << to)) ? 'q' : 0; + if (i % 8 > 0 && (to >= 0 && to < 64) && (enemy & (1ULL << to))) { moves[index++] = (move){.From = i, .To = to, .Promo = p}; } // right to = i + capRight; - p = (promo & (1LL << to)) ? 'q' : 0; - if (i % 8 < 7 && (to >= 0 && to < 64) && (enemy & (1LL << to))) { + p = (promo & (1ULL << to)) ? 'q' : 0; + if (i % 8 < 7 && (to >= 0 && to < 64) && (enemy & (1ULL << to))) { moves[index++] = (move){.From = i, .To = to, .Promo = p}; } } @@ -58,7 +58,7 @@ int knightMove(game *g, move *moves) { int index = 0; for (int i = 0; i < 64; i++) { - long long bit = 1LL << i; + long long bit = 1ULL << i; if (!(knights & bit)) continue; @@ -78,7 +78,7 @@ int knightMove(game *g, move *moves) { (fileDiff == 2 && rankDiff == 1))) continue; - long long destBit = 1LL << to; + long long destBit = 1ULL << to; if (occupied & destBit) continue; @@ -100,7 +100,7 @@ int rookScan(game *g, move *moves, long long w, long long b) { int index = 0; for (int i = 0; i < 64; i++) { - long long bit = 1LL << i; + long long bit = 1ULL << i; int x = i % 8; int y = i / 8; if (!(rooks & bit)) @@ -109,7 +109,7 @@ int rookScan(game *g, move *moves, long long w, long long b) { // fwd for (int j = y + 1; j < 8; j++) { int to = (j * 8) + x; - long long lbit = 1LL << to; + long long lbit = 1ULL << to; if (occupied & lbit) break; moves[index++] = (move){.From = i, .To = to, .Promo=0}; @@ -120,7 +120,7 @@ int rookScan(game *g, move *moves, long long w, long long b) { // bck for (int j = y - 1; j >= 0; j--) { int to = (j * 8) + x; - long long lbit = 1LL << to; + long long lbit = 1ULL << to; if (occupied & lbit) break; moves[index++] = (move){.From = i, .To = to, .Promo=0}; @@ -131,7 +131,7 @@ int rookScan(game *g, move *moves, long long w, long long b) { // rht for (int j = x + 1; j < 8; j++) { int to = (y * 8) + j; - long long lbit = 1LL << to; + long long lbit = 1ULL << to; if (occupied & lbit) break; moves[index++] = (move){.From = i, .To = to, .Promo=0}; @@ -142,7 +142,7 @@ int rookScan(game *g, move *moves, long long w, long long b) { // lft for (int j = x - 1; j >= 0; j--) { int to = (y * 8) + j; - long long lbit = 1LL << to; + long long lbit = 1ULL << to; if (occupied & lbit) break; moves[index++] = (move){.From = i, .To = to, .Promo=0}; @@ -158,14 +158,14 @@ int bishopMove(game *g, move *moves) { } int bishopScan(game *g, move *moves, long long w, long long b) { - long long bishops = g->whiteToMove ? w : b; - long long occupied = g->whiteToMove ? fullSet(&g->white) : fullSet(&g->black); - long long occupiedE = + unsigned long long bishops = g->whiteToMove ? w : b; + unsigned long long occupied = g->whiteToMove ? fullSet(&g->white) : fullSet(&g->black); + unsigned long long occupiedE = !g->whiteToMove ? fullSet(&g->white) : fullSet(&g->black); int index = 0; for (int i = 0; i < 64; i++) { - long long bit = 1LL << i; + unsigned long long bit = 1ULL << i; int x = i % 8; int y = i / 8; @@ -180,7 +180,7 @@ int bishopScan(game *g, move *moves, long long w, long long b) { break; int to = (yTo * 8) + xTo; - long long lbit = 1LL << to; + unsigned long long lbit = 1ULL << to; if (occupied & lbit) break; @@ -197,7 +197,7 @@ int bishopScan(game *g, move *moves, long long w, long long b) { break; int to = (yTo * 8) + xTo; - long long lbit = 1LL << to; + unsigned long long lbit = 1ULL << to; if (occupied & lbit) break; @@ -214,7 +214,7 @@ int bishopScan(game *g, move *moves, long long w, long long b) { break; int to = (yTo * 8) + xTo; - long long lbit = 1LL << to; + unsigned long long lbit = 1ULL << to; if (occupied & lbit) break; @@ -231,7 +231,7 @@ int bishopScan(game *g, move *moves, long long w, long long b) { break; int to = (yTo * 8) + xTo; - long long lbit = 1LL << to; + unsigned long long lbit = 1ULL << to; if (occupied & lbit) break; @@ -245,7 +245,7 @@ int bishopScan(game *g, move *moves, long long w, long long b) { int queenMove(game *g, move *moves){ int size = 0; - long long w,b; + unsigned long long w,b; w = g->white.queen; b = g->black.queen; size += rookScan(g,moves,w,b); @@ -255,12 +255,12 @@ int queenMove(game *g, move *moves){ int kingMove(game *g, move *moves){ - long long king = g->whiteToMove ? g->white.king : g->black.king; - long long occupied = g->whiteToMove ? fullSet(&g->white) : fullSet(&g->black); + unsigned long long king = g->whiteToMove ? g->white.king : g->black.king; + unsigned long long occupied = g->whiteToMove ? fullSet(&g->white) : fullSet(&g->black); int index = 0; for (int i = 0; i < 64; i++){ - long long bit = 1LL << i; + unsigned long long bit = 1ULL << i; int x = i % 8; int y = i / 8; int movesX[] = {-1, 0, 1, -1, 1, -1, 0, 1}; @@ -273,7 +273,9 @@ int kingMove(game *g, move *moves){ int toX = x + movesX[j]; int toY = y + movesY[j]; int to = (toY * 8) + toX; - long long destBit = 1LL << to; + if(to > 63 || to < 1) + continue; + unsigned long long destBit = 1ULL << to; if (toX >= 0 && toX < 8 && toY >= 0 && toY < 8) { if (!(occupied & destBit)) {