From ff8b8d9bb036c6b8468b0f14117b8c67eddd54be Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 18 Apr 2026 22:21:07 -0600 Subject: [PATCH] feat(engine): capture-flags modifier descriptor --- .../descriptors/capture-flags.test.ts | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 packages/chess/src/modifiers/descriptors/capture-flags.test.ts diff --git a/packages/chess/src/modifiers/descriptors/capture-flags.test.ts b/packages/chess/src/modifiers/descriptors/capture-flags.test.ts new file mode 100644 index 0000000..c2a883c --- /dev/null +++ b/packages/chess/src/modifiers/descriptors/capture-flags.test.ts @@ -0,0 +1,103 @@ +import { describe, it, expect } from "vitest"; +import { Session } from "@paratype/rete"; +import { MODIFIER_REGISTRY } from "../registry.js"; +import { CaptureFlag } from "../../schema.js"; +import "./capture-flags.js"; +import { CAPTURE_FLAGS_DESCRIPTOR, hasCaptureFlag } from "./capture-flags.js"; + +describe("capture-flags descriptor — registry", () => { + it("registers in MODIFIER_REGISTRY under key 'capture-flags'", () => { + expect(MODIFIER_REGISTRY.has("capture-flags")).toBe(true); + expect(MODIFIER_REGISTRY.get("capture-flags")).toBe(CAPTURE_FLAGS_DESCRIPTOR); + }); +}); + +describe("capture-flags descriptor — apply()", () => { + it("seeds CaptureFlags fact on piece entity in session", () => { + const session = new Session(); + const pieceId = session.nextId(); + session.insert(pieceId, "PieceType", "pawn"); + + CAPTURE_FLAGS_DESCRIPTOR.apply(session, pieceId, CaptureFlag.CANNOT_BE_CAPTURED); + + expect(session.contains(pieceId, "CaptureFlags")).toBe(true); + expect(session.get(pieceId, "CaptureFlags")).toBe(CaptureFlag.CANNOT_BE_CAPTURED); + }); +}); + +describe("capture-flags descriptor — describe()", () => { + it("returns 'no flags' for 0", () => { + expect(CAPTURE_FLAGS_DESCRIPTOR.describe(0)).toBe("no flags"); + }); + + it("describes combined CAN_CAPTURE_OWN | CANNOT_BE_CAPTURED flags", () => { + const combined = CaptureFlag.CAN_CAPTURE_OWN | CaptureFlag.CANNOT_BE_CAPTURED; + expect(CAPTURE_FLAGS_DESCRIPTOR.describe(combined)).toBe("can-capture-own, cannot-be-captured"); + }); + + it("describes all three flags", () => { + const allFlags = CaptureFlag.CAN_CAPTURE_OWN | CaptureFlag.CANNOT_BE_CAPTURED | CaptureFlag.EN_PASSANT; + expect(CAPTURE_FLAGS_DESCRIPTOR.describe(allFlags)).toBe( + "can-capture-own, cannot-be-captured, en-passant", + ); + }); + + it("describes a single flag", () => { + expect(CAPTURE_FLAGS_DESCRIPTOR.describe(CaptureFlag.EN_PASSANT)).toBe("en-passant"); + }); +}); + +describe("capture-flags descriptor — hasCaptureFlag()", () => { + it("returns true when the flag is set", () => { + const session = new Session(); + const pieceId = session.nextId(); + CAPTURE_FLAGS_DESCRIPTOR.apply( + session, + pieceId, + CaptureFlag.CAN_CAPTURE_OWN | CaptureFlag.CANNOT_BE_CAPTURED, + ); + + expect(hasCaptureFlag(session, pieceId, CaptureFlag.CANNOT_BE_CAPTURED)).toBe(true); + expect(hasCaptureFlag(session, pieceId, CaptureFlag.CAN_CAPTURE_OWN)).toBe(true); + }); + + it("returns false when the flag is not set", () => { + const session = new Session(); + const pieceId = session.nextId(); + CAPTURE_FLAGS_DESCRIPTOR.apply(session, pieceId, CaptureFlag.CAN_CAPTURE_OWN); + + expect(hasCaptureFlag(session, pieceId, CaptureFlag.EN_PASSANT)).toBe(false); + }); + + it("returns false when CaptureFlags fact is absent", () => { + const session = new Session(); + const pieceId = session.nextId(); + + expect(hasCaptureFlag(session, pieceId, CaptureFlag.CANNOT_BE_CAPTURED)).toBe(false); + }); +}); + +describe("capture-flags descriptor — valueSchema", () => { + const schema = CAPTURE_FLAGS_DESCRIPTOR.valueSchema; + + it("accepts 0 (no flags)", () => { + expect(() => schema.parse(0)).not.toThrow(); + }); + + it("accepts MAX_FLAGS (all flags OR'd together = 7)", () => { + const maxFlags = CaptureFlag.CAN_CAPTURE_OWN | CaptureFlag.CANNOT_BE_CAPTURED | CaptureFlag.EN_PASSANT; + expect(() => schema.parse(maxFlags)).not.toThrow(); + }); + + it("rejects -1 (below min)", () => { + expect(() => schema.parse(-1)).toThrow(); + }); + + it("rejects 8 (above MAX_FLAGS = 7)", () => { + expect(() => schema.parse(8)).toThrow(); + }); + + it("rejects non-integer values", () => { + expect(() => schema.parse(1.5)).toThrow(); + }); +});