core/piece.ts

import { is, not } from "./flags.ts";
import { Color, NONE } from "./color.ts";
import { Sym } from "./sym.ts";

/**
 * Base class for all board pieces (Terrain, Item, Agent, Effect).
 * All pieces are immutable — their state is set at construction and never changes.
 * The Registry ensures that identical pieces are the same object instance.
 */
export class Piece {
  name: string;
  flags: number;
  color: Color;
  symbol: Sym;
  /**
   * @param {string} name - Display name shown to the player
   * @param {number} flags - Bitmask of Flags constants
   * @param {Color} color - use Color.NONE for no color
   * @param {Sym} symbol - Visual representation
   */
  constructor(name: string, flags: number, color: Color, symbol: Sym);
  /** Overload without color — color defaults to Color.NONE. */
  constructor(name: string, flags: number, symbol: Sym);
  constructor(name: string, flags: number, colorOrSymbol: Color | Sym, symbol?: Sym) {
    const has4Args = symbol !== undefined;
    if (!name) {
      throw new Error("Piece must have a name");
    }
    const resolvedSymbol = has4Args ? symbol! : (colorOrSymbol as Sym);
    if (!(resolvedSymbol instanceof Sym)) {
      throw new Error(`Piece must have a symbol (name=${name})`);
    }
    const resolvedColor = has4Args ? (colorOrSymbol as Color) : NONE;
    if (!(resolvedColor instanceof Color)) {
      throw new Error(`Piece must have a color (name=${name}) — use Color.NONE, not null`);
    }
    this.name = name;
    this.flags = flags;
    this.color = resolvedColor;
    this.symbol = resolvedSymbol;
  }

  /** True if this piece has the given flag set. */
  is(flag: number): boolean {
    return is(flag, this.flags);
  }

  /** True if this piece does NOT have the given flag set. */
  not(flag: number): boolean {
    return not(flag, this.flags);
  }

}