Male

import React, { useMemo, useState } from "react";

/**
 * A self-contained chess game in React (no external chess libs).
 * Features:
 * - Legal move generation (incl. castling, en passant, promotion)
 * - Check / checkmate / stalemate detection
 * - Click-to-move UI + legal-move highlights
 *
 * Notes:
 * - This is a "rules-correct" implementation for standard chess.
 * - 0,0 is a8; 7,7 is h1.
 */

// --- Types ---

/** @typedef {'w'|'b'} Color */
/** @typedef {'p'|'n'|'b'|'r'|'q'|'k'} PieceType */
/** @typedef {{ c: Color, t: PieceType }} Piece */
/** @typedef {Piece | null} Square */
/** @typedef {Square[][]} Board */
/** @typedef {{ r: number, c: number }} Pos */
/** @typedef {{ from: Pos, to: Pos, promo?: PieceType, isCastle?: boolean, isEnPassant?: boolean }} Move */

const FILES = "abcdefgh";

// --- Helpers ---

function inBounds(r, c) {
  return r >= 0 && r < 8 && c >= 0 && c < 8;
}

function cloneBoard(board) {
  return board.map((row) => row.map((sq) => (sq ? { ...sq } : null)));
}

function posEq(a, b) {
  return a.r === b.r && a.c === b.c;
}

function algebraic(pos) {
  // r=0 is rank 8
  const file = FILES[pos.c];
  const rank = 8 - pos.r;
  return `${file}${rank}`;
}

function opposite(color) {
  return color === "w" ? "b" : "w";
}

function pieceChar(p) {
  // Unicode pieces
  if (!p) return "";
  const map = {
    w: { k: "♔", q: "♕", r: "♖", b: "♗", n: "♘", p: "♙" },
    b: { k: "♚", q: "♛", r: "♜", b: "♝", n: "♞", p: "♟" },
  };
  return map[p.c][p.t];
}

function makeInitialBoard() {
  /** @type {Board} */
  const b = Array.from({ length: 8 }, () => Array.from({ length: 8 }, () => null));

  const back = /** @type {PieceType[]} */ (["r", "n", "b", "q", "k", "b", "n", "r"]);
  for (let c = 0; c < 8; c++) {
    b[0][c] = { c: "b", t: back[c] };
    b[1][c] = { c: "b", t: "p" };
    b[6][c] = { c: "w", t: "p" };
    b[7][c] = { c: "w", t: back[c] };
  }
  return b;
}

// Game state needed for special moves
function makeInitialState() {
  return {
    board: makeInitialBoard(),
    turn: /** @type {Color} */ ("w"),
    // castling rights
    castle: {
      wK: true,
      wQ: true,
      bK: true,
      bQ: true,
    },
    // en passant target square (the square a pawn could capture onto)
    ep: /** @type {Pos | null} */ (null),
    // halfmove and fullmove counters (for display; not 50-move rule enforcement here)
    halfmove: 0,
    fullmove: 1,
    // move log
    log: /** @type {string[]} */ ([]),
    // game over status
    result: /** @type {null | { type: 'checkmate'|'stalemate'|'draw', winner?: Color }} */ (null),
  };
}

// --- Move generation ---

function findKing(board, color) {
  for (let r = 0; r < 8; r++) {
    for (let c = 0; c < 8; c++) {
      const p = board[r][c];
      if (p && p.c === color && p.t === "k") return { r, c };
    }
  }
  return null;
}

function isSquareAttacked(board, target, byColor) {
  // Generate pseudo-attacks from byColor and see if any hits target.
  for (let r = 0; r < 8; r++) {
    for (let c = 0; c < 8; c++) {
      const p = board[r][c];
      if (!p || p.c !== byColor) continue;
      const from = { r, c };
      const attacks = pseudoAttacksForPiece(board, from, p);
      if (attacks.some((pos) => posEq(pos, target))) return true;
    }
  }
  return false;
}

function pseudoAttacksForPiece(board, from, p) {
  /** @type {Pos[]} */
  const out = [];
  const { r, c } = from;

  if (p.t === "p") {
    const dir = p.c === "w" ? -1 : 1;
    for (const dc of [-1, 1]) {
      const rr = r + dir;
      const cc = c + dc;
      if (inBounds(rr, cc)) out.push({ r: rr, c: cc });
    }
    return out;
  }

  if (p.t === "n") {
    const deltas = [
      [-2, -1],
      [-2, 1],
      [-1, -2],
      [-1, 2],
      [1, -2],
      [1, 2],
      [2, -1],
      [2, 1],
    ];
    for (const [dr, dc] of deltas) {
      const rr = r + dr;
      const cc = c + dc;
      if (inBounds(rr, cc)) out.push({ r: rr, c: cc });
    }
    return out;
  }

  if (p.t === "k") {
    for (let dr = -1; dr <= 1; dr++) {
      for (let dc = -1; dc <= 1; dc++) {
        if (dr === 0 && dc === 0) continue;
        const rr = r + dr;
        const cc = c + dc;
        if (inBounds(rr, cc)) out.push({ r: rr, c: cc });
      }
    }
    return out;
  }

  const sliders = {
    b: [
      [-1, -1],
      [-1, 1],
      [1, -1],
      [1, 1],
    ],
    r: [
      [-1, 0],
      [1, 0],
      [0, -1],
      [0, 1],
    ],
    q: [
      [-1, -1],
      [-1, 1],
      [1, -1],
      [1, 1],
      [-1, 0],
      [1, 0],
      [0, -1],
      [0, 1],
    ],
  };

  const dirs = sliders[p.t];
  if (!dirs) return out;

  for (const [dr, dc] of dirs) {
    let rr = r + dr;
    let cc = c + dc;
    while (inBounds(rr, cc)) {
      out.push({ r: rr, c: cc });
      if (board[rr][cc]) break; // ray blocked
      rr += dr;
      cc += dc;
    }
  }

  return out;
}

function generatePseudoLegalMoves(state, from) {
  const { board, turn, castle, ep } = state;
  const p = board[from.r][from.c];
  if (!p || p.c !== turn) return [];

  /** @type {Move[]} */
  const moves = [];

  const addMove = (to, extra = {}) => {
    moves.push({ from, to, ...extra });
  };

  const r = from.r;
  const c = from.c;

  if (p.t === "p") {
    const dir = p.c === "w" ? -1 : 1;
    const startRank = p.c === "w" ? 6 : 1;
    const promoRank = p.c === "w" ? 0 : 7;

    // forward 1
    const r1 = r + dir;
    if (inBounds(r1, c) && !board[r1][c]) {
      if (r1 === promoRank) {
        for (const promo of /** @type {PieceType[]} */ (["q", "r", "b", "n"])) {
          addMove({ r: r1, c }, { promo });
        }
      } else {
        addMove({ r: r1, c });
      }

      // forward 2 from start
      const r2 = r + 2 * dir;
      if (r === startRank && inBounds(r2, c) && !board[r2][c]) {
        addMove({ r: r2, c });
      }
    }

    // captures
    for (const dc of [-1, 1]) {
      const rr = r + dir;
      const cc = c + dc;
      if (!inBounds(rr, cc)) continue;
      const target = board[rr][cc];
      if (target && target.c !== p.c) {
        if (rr === promoRank) {
          for (const promo of /** @type {PieceType[]} */ (["q", "r", "b", "n"])) {
            addMove({ r: rr, c: cc }, { promo });
          }
        } else {
          addMove({ r: rr, c: cc });
        }
      }

      // en passant
      if (ep && ep.r === rr && ep.c === cc) {
        addMove({ r: rr, c: cc }, { isEnPassant: true });
      }
    }

    return moves;
  }

  if (p.t === "n") {
    const deltas = [
      [-2, -1],
      [-2, 1],
      [-1, -2],
      [-1, 2],
      [1, -2],
      [1, 2],
      [2, -1],
      [2, 1],
    ];
    for (const [dr, dc] of deltas) {
      const rr = r + dr;
      const cc = c + dc;
      if (!inBounds(rr, cc)) continue;
      const target = board[rr][cc];
      if (!target || target.c !== p.c) addMove({ r: rr, c: cc });
    }
    return moves;
  }

  if (p.t === "k") {
    for (let dr = -1; dr <= 1; dr++) {
      for (let dc = -1; dc <= 1; dc++) {
        if (dr === 0 && dc === 0) continue;
        const rr = r + dr;
        const cc = c + dc;
        if (!inBounds(rr, cc)) continue;
        const target = board[rr][cc];
        if (!target || target.c !== p.c) addMove({ r: rr, c: cc });
      }
    }

    // castling (pseudo; we validate check conditions in legal filtering)
    if (p.c === "w" && r === 7 && c === 4) {
      // King-side: squares f1,g1 empty and rook h1 present
      if (castle.wK && !board[7][5] && !board[7][6]) {
        const rook = board[7][7];
        if (rook && rook.c === "w" && rook.t === "r") {
          addMove({ r: 7, c: 6 }, { isCastle: true });
        }
      }
      // Queen-side: squares d1,c1,b1 empty and rook a1 present
      if (castle.wQ && !board[7][3] && !board[7][2] && !board[7][1]) {
        const rook = board[7][0];
        if (rook && rook.c === "w" && rook.t === "r") {
          addMove({ r: 7, c: 2 }, { isCastle: true });
        }
      }
    }

    if (p.c === "b" && r === 0 && c === 4) {
      if (castle.bK && !board[0][5] && !board[0][6]) {
        const rook = board[0][7];
        if (rook && rook.c === "b" && rook.t === "r") {
          addMove({ r: 0, c: 6 }, { isCastle: true });
        }
      }
      if (castle.bQ && !board[0][3] && !board[0][2] && !board[0][1]) {
        const rook = board[0][0];
        if (rook && rook.c === "b" && rook.t === "r") {
          addMove({ r: 0, c: 2 }, { isCastle: true });
        }
      }
    }

    return moves;
  }

  const dirs =
    p.t === "b"
      ? [
          [-1, -1],
          [-1, 1],
          [1, -1],
          [1, 1],
        ]
      : p.t === "r"
        ? [
            [-1, 0],
            [1, 0],
            [0, -1],
            [0, 1],
          ]
        : [
            [-1, -1],
            [-1, 1],
            [1, -1],
            [1, 1],
            [-1, 0],
            [1, 0],
            [0, -1],
            [0, 1],
          ]; // queen

  for (const [dr, dc] of dirs) {
    let rr = r + dr;
    let cc = c + dc;
    while (inBounds(rr, cc)) {
      const target = board[rr][cc];
      if (!target) {
        addMove({ r: rr, c: cc });
      } else {
        if (target.c !== p.c) addMove({ r: rr, c: cc });
        break;
      }
      rr += dr;
      cc += dc;
    }
  }

  return moves;
}

function applyMove(state, move) {
  const { board, turn } = state;
  const b = cloneBoard(board);
  const fromPiece = b[move.from.r][move.from.c];
  if (!fromPiece) return state;

  const captured = b[move.to.r][move.to.c];

  // clear en-passant captured pawn
  if (move.isEnPassant && fromPiece.t === "p") {
    const dir = fromPiece.c === "w" ? 1 : -1; // pawn captured behind target square
    b[move.to.r + dir][move.to.c] = null;
  }

  // move piece
  b[move.from.r][move.from.c] = null;
  b[move.to.r][move.to.c] = {
    c: fromPiece.c,
    t: move.promo ? move.promo : fromPiece.t,
  };

  // castling rook move
  if (move.isCastle && fromPiece.t === "k") {
    // king ends on g-file (c=6) or c-file (c=2)
    if (move.to.c === 6) {
      // rook: h -> f
      b[move.to.r][5] = b[move.to.r][7];
      b[move.to.r][7] = null;
    } else if (move.to.c === 2) {
      // rook: a -> d
      b[move.to.r][3] = b[move.to.r][0];
      b[move.to.r][0] = null;
    }
  }

  // update castling rights
  const nextCastle = { ...state.castle };
  // moving king removes both rights
  if (fromPiece.t === "k") {
    if (fromPiece.c === "w") {
      nextCastle.wK = false;
      nextCastle.wQ = false;
    } else {
      nextCastle.bK = false;
      nextCastle.bQ = false;
    }
  }
  // moving rook removes that side
  if (fromPiece.t === "r") {
    if (fromPiece.c === "w") {
      if (move.from.r === 7 && move.from.c === 0) nextCastle.wQ = false;
      if (move.from.r === 7 && move.from.c === 7) nextCastle.wK = false;
    } else {
      if (move.from.r === 0 && move.from.c === 0) nextCastle.bQ = false;
      if (move.from.r === 0 && move.from.c === 7) nextCastle.bK = false;
    }
  }
  // capturing rook removes opponent right
  if (captured && captured.t === "r") {
    if (captured.c === "w") {
      if (move.to.r === 7 && move.to.c === 0) nextCastle.wQ = false;
      if (move.to.r === 7 && move.to.c === 7) nextCastle.wK = false;
    } else {
      if (move.to.r === 0 && move.to.c === 0) nextCastle.bQ = false;
      if (move.to.r === 0 && move.to.c === 7) nextCastle.bK = false;
    }
  }

  // update en passant target
  let nextEp = null;
  if (fromPiece.t === "p" && Math.abs(move.to.r - move.from.r) === 2) {
    // target is the square jumped over
    nextEp = { r: (move.to.r + move.from.r) / 2, c: move.from.c };
  }

  // halfmove clock
  const isPawnMove = fromPiece.t === "p";
  const isCapture = !!captured || !!move.isEnPassant;
  const nextHalfmove = isPawnMove || isCapture ? 0 : state.halfmove + 1;

  const nextTurn = opposite(turn);
  const nextFullmove = turn === "b" ? state.fullmove + 1 : state.fullmove;

  return {
    ...state,
    board: b,
    turn: nextTurn,
    castle: nextCastle,
    ep: nextEp,
    halfmove: nextHalfmove,
    fullmove: nextFullmove,
  };
}

function isMoveLegal(state, move) {
  const p = state.board[move.from.r][move.from.c];
  if (!p || p.c !== state.turn) return false;

  // castling legality: king can't castle out of, through, or into check
  if (move.isCastle && p.t === "k") {
    const kingFrom = move.from;
    const kingTo = move.to;
    const enemy = opposite(p.c);

    // if king currently in check: illegal
    if (isSquareAttacked(state.board, kingFrom, enemy)) return false;

    // squares passed through
    const pathSquares = [];
    if (kingTo.c === 6) {
      // e->f->g
      pathSquares.push({ r: kingFrom.r, c: 5 }, { r: kingFrom.r, c: 6 });
    } else if (kingTo.c === 2) {
      // e->d->c
      pathSquares.push({ r: kingFrom.r, c: 3 }, { r: kingFrom.r, c: 2 });
    }

    for (const sq of pathSquares) {
      // simulate king stepping (without rook movement) for attack test
      if (isSquareAttacked(state.board, sq, enemy)) return false;
    }
  }

  // general legality: can't leave own king in check
  const next = applyMove(state, move);
  const kingPos = findKing(next.board, p.c);
  if (!kingPos) return false;
  return !isSquareAttacked(next.board, kingPos, opposite(p.c));
}

function legalMovesFrom(state, from) {
  const pseudo = generatePseudoLegalMoves(state, from);
  return pseudo.filter((m) => isMoveLegal(state, m));
}

function allLegalMoves(state) {
  /** @type {Move[]} */
  const out = [];
  for (let r = 0; r < 8; r++) {
    for (let c = 0; c < 8; c++) {
      const p = state.board[r][c];
      if (!p || p.c !== state.turn) continue;
      out.push(...legalMovesFrom(state, { r, c }));
    }
  }
  return out;
}

function detectGameResult(state) {
  const moves = allLegalMoves(state);
  const kingPos = findKing(state.board, state.turn);
  const inCheck = kingPos ? isSquareAttacked(state.board, kingPos, opposite(state.turn)) : false;

  if (moves.length > 0) return null;

  if (inCheck) {
    return { type: "checkmate", winner: opposite(state.turn) };
  }
  return { type: "stalemate" };
}

// --- Notation (simple SAN-ish) ---

function moveToNotation(prevState, move) {
  const board = prevState.board;
  const p = board[move.from.r][move.from.c];
  if (!p) return "";

  // Castling
  if (move.isCastle && p.t === "k") {
    return move.to.c === 6 ? "O-O" : "O-O-O";
  }

  const target = board[move.to.r][move.to.c];
  const isCapture = !!target || !!move.isEnPassant;

  const pieceLetter = p.t === "p" ? "" : p.t.toUpperCase();
  const fromFile = FILES[move.from.c];
  const toSq = algebraic(move.to);

  let s = "";

  if (p.t === "p" && isCapture) {
    s += fromFile;
  } else {
    s += pieceLetter;
  }

  if (isCapture) s += "x";
  s += toSq;

  if (move.promo) {
    s += "=" + move.promo.toUpperCase();
  }

  // Add check/checkmate suffix
  const next = applyMove(prevState, move);
  const res = detectGameResult(next);
  const enemyKing = findKing(next.board, next.turn);
  const givesCheck = enemyKing
    ? isSquareAttacked(next.board, enemyKing, opposite(next.turn))
    : false;

  if (res?.type === "checkmate") s += "#";
  else if (givesCheck) s += "+";

  return s;
}

// --- UI ---

function SquareButton({
  piece,
  isLight,
  isSelected,
  isLegalTarget,
  isLastMove,
  onClick,
  label,
}) {
  const base = {
    width: 56,
    height: 56,
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    fontSize: 30,
    border: "1px solid rgba(0,0,0,0.12)",
    cursor: "pointer",
    userSelect: "none",
    position: "relative",
  };

  let background = isLight ? "#f0d9b5" : "#b58863";
  if (isLastMove) background = isLight ? "#f6f669" : "#d6d64b";
  if (isSelected) background = isLight ? "#a9d18e" : "#6aa84f";

  return (
    <button
      onClick={onClick}
      style={{
        ...base,
        background,
        outline: isSelected ? "2px solid rgba(0,0,0,0.4)" : "none",
      }}
      aria-label={label}
      title={label}
    >
      <span>{pieceChar(piece)}</span>
      {isLegalTarget && (
        <span
          style={{
            position: "absolute",
            width: 12,
            height: 12,
            borderRadius: 999,
            background: "rgba(0,0,0,0.35)",
          }}
        />
      )}
    </button>
  );
}

function PromotionPicker({ color, onPick }) {
  const options = /** @type {PieceType[]} */ (["q", "r", "b", "n"]);
  return (
    <div
      style={{
        position: "absolute",
        inset: 0,
        background: "rgba(0,0,0,0.35)",
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        padding: 16,
      }}
    >
      <div
        style={{
          background: "white",
          borderRadius: 12,
          padding: 16,
          width: 320,
          boxShadow: "0 10px 30px rgba(0,0,0,0.25)",
        }}
      >
        <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 10 }}>
          Choose promotion
        </div>
        <div style={{ display: "flex", gap: 10, justifyContent: "space-between" }}>
          {options.map((t) => (
            <button
              key={t}
              onClick={() => onPick(t)}
              style={{
                flex: 1,
                height: 56,
                borderRadius: 10,
                border: "1px solid rgba(0,0,0,0.15)",
                cursor: "pointer",
                background: "#fafafa",
                fontSize: 26,
              }}
              aria-label={`Promote to ${t}`}
              title={`Promote to ${t.toUpperCase()}`}
            >
              {pieceChar({ c: color, t })}
            </button>
          ))}
        </div>
      </div>
    </div>
  );
}

export default function ChessGame() {
  const [state, setState] = useState(makeInitialState);
  const [selected, setSelected] = useState(/** @type {Pos | null} */ (null));
  const [pendingPromo, setPendingPromo] = useState(
    /** @type {null | { baseMove: Move, color: Color }} */ (null)
  );
  const [lastMove, setLastMove] = useState(/** @type {Move | null} */ (null));

  const legalTargets = useMemo(() => {
    if (!selected) return [];
    return legalMovesFrom(state, selected);
  }, [state, selected]);

  const kingPos = useMemo(() => findKing(state.board, state.turn), [state.board, state.turn]);
  const inCheck = useMemo(() => {
    if (!kingPos) return false;
    return isSquareAttacked(state.board, kingPos, opposite(state.turn));
  }, [state.board, kingPos, state.turn]);

  const statusText = useMemo(() => {
    if (state.result?.type === "checkmate") {
      return `Checkmate — ${state.result.winner === "w" ? "White" : "Black"} wins`;
    }
    if (state.result?.type === "stalemate") return "Stalemate — draw";
    return `${state.turn === "w" ? "White" : "Black"} to move${inCheck ? " (in check)" : ""}`;
  }, [state.result, state.turn, inCheck]);

  function reset() {
    setState(makeInitialState());
    setSelected(null);
    setPendingPromo(null);
    setLastMove(null);
  }

  function commitMove(move) {
    // Handle promotion choice UI
    const fromP = state.board[move.from.r][move.from.c];
    if (fromP && fromP.t === "p") {
      const promoRank = fromP.c === "w" ? 0 : 7;
      if (move.to.r === promoRank && !move.promo) {
        setPendingPromo({ baseMove: move, color: fromP.c });
        return;
      }
    }

    const notation = moveToNotation(state, move);
    const next = applyMove(state, move);
    const result = detectGameResult(next);

    setState({
      ...next,
      log: [...state.log, notation],
      result,
    });
    setLastMove(move);
    setSelected(null);
  }

  function onSquareClick(r, c) {
    if (state.result) return;
    if (pendingPromo) return;

    const p = state.board[r][c];
    const clicked = { r, c };

    if (!selected) {
      if (p && p.c === state.turn) setSelected(clicked);
      return;
    }

    // same square: deselect
    if (selected.r === r && selected.c === c) {
      setSelected(null);
      return;
    }

    // if selecting another own piece
    if (p && p.c === state.turn) {
      setSelected(clicked);
      return;
    }

    // try move to clicked square
    const m = legalTargets.find((mv) => mv.to.r === r && mv.to.c === c);
    if (m) commitMove(m);
    else setSelected(null);
  }

  function onPickPromotion(t) {
    if (!pendingPromo) return;
    commitMove({ ...pendingPromo.baseMove, promo: t });
    setPendingPromo(null);
  }

  // Display move list as pairs
  const movePairs = useMemo(() => {
    const pairs = [];
    for (let i = 0; i < state.log.length; i += 2) {
      pairs.push({
        n: 1 + i / 2,
        w: state.log[i] || "",
        b: state.log[i + 1] || "",
      });
    }
    return pairs;
  }, [state.log]);

  return (
    <div style={{ fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, sans-serif", padding: 16 }}>
      <div style={{ maxWidth: 920, margin: "0 auto" }}>
        <div
          style={{
            display: "flex",
            gap: 18,
            alignItems: "flex-start",
            flexWrap: "wrap",
          }}
        >
          <div style={{ position: "relative" }}>
            <div
              style={{
                display: "grid",
                gridTemplateColumns: "repeat(8, 56px)",
                gridTemplateRows: "repeat(8, 56px)",
                borderRadius: 12,
                overflow: "hidden",
                boxShadow: "0 10px 30px rgba(0,0,0,0.12)",
              }}
            >
              {state.board.map((row, r) =>
                row.map((sq, c) => {
                  const isLight = (r + c) % 2 === 0;
                  const isSelected = !!selected && selected.r === r && selected.c === c;
                  const isLegalTarget = legalTargets.some((m) => m.to.r === r && m.to.c === c);
                  const isLastMove =
                    !!lastMove &&
                    ((lastMove.from.r === r && lastMove.from.c === c) ||
                      (lastMove.to.r === r && lastMove.to.c === c));
                  const label = `${algebraic({ r, c })}${sq ? ` ${sq.c}${sq.t}` : ""}`;
                  return (
                    <SquareButton
                      key={`${r}-${c}`}
                      piece={sq}
                      isLight={isLight}
                      isSelected={isSelected}
                      isLegalTarget={isLegalTarget}
                      isLastMove={isLastMove}
                      onClick={() => onSquareClick(r, c)}
                      label={label}
                    />
                  );
                })
              )}
            </div>

            {pendingPromo && <PromotionPicker color={pendingPromo.color} onPick={onPickPromotion} />}
          </div>

          <div
            style={{
              width: 320,
              flex: "0 0 320px",
              border: "1px solid rgba(0,0,0,0.12)",
              borderRadius: 12,
              padding: 14,
              boxShadow: "0 10px 30px rgba(0,0,0,0.06)",
            }}
          >
            <div style={{ fontSize: 18, fontWeight: 800, marginBottom: 8 }}>Chess</div>
            <div style={{ fontSize: 14, marginBottom: 12, opacity: 0.85 }}>{statusText}</div>

            <div style={{ display: "flex", gap: 8, marginBottom: 12 }}>
              <button
                onClick={reset}
                style={{
                  padding: "10px 12px",
                  borderRadius: 10,
                  border: "1px solid rgba(0,0,0,0.15)",
                  background: "#fafafa",
                  cursor: "pointer",
                  fontWeight: 700,
                }}
              >
                New game
              </button>
              <button
                onClick={() => setSelected(null)}
                style={{
                  padding: "10px 12px",
                  borderRadius: 10,
                  border: "1px solid rgba(0,0,0,0.15)",
                  background: "#fff",
                  cursor: "pointer",
                  fontWeight: 700,
                }}
                disabled={!selected}
                title={selected ? "Clear selection" : ""}
              >
                Deselect
              </button>
            </div>

            <div style={{ fontSize: 13, fontWeight: 800, marginBottom: 6 }}>Moves</div>
            <div
              style={{
                maxHeight: 340,
                overflow: "auto",
                borderRadius: 10,
                border: "1px solid rgba(0,0,0,0.12)",
              }}
            >
              <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
                <thead>
                  <tr style={{ background: "rgba(0,0,0,0.03)" }}>
                    <th style={{ textAlign: "left", padding: 8, width: 40 }}>#</th>
                    <th style={{ textAlign: "left", padding: 8 }}>White</th>
                    <th style={{ textAlign: "left", padding: 8 }}>Black</th>
                  </tr>
                </thead>
                <tbody>
                  {movePairs.length === 0 ? (
                    <tr>
                      <td colSpan={3} style={{ padding: 10, opacity: 0.7 }}>
                        No moves yet.
                      </td>
                    </tr>
                  ) : (
                    movePairs.map((p) => (
                      <tr key={p.n}>
                        <td style={{ padding: 8, borderTop: "1px solid rgba(0,0,0,0.08)", opacity: 0.7 }}>
                          {p.n}.
                        </td>
                        <td style={{ padding: 8, borderTop: "1px solid rgba(0,0,0,0.08)" }}>{p.w}</td>
                        <td style={{ padding: 8, borderTop: "1px solid rgba(0,0,0,0.08)" }}>{p.b}</td>
                      </tr>
                    ))
                  )}
                </tbody>
              </table>
            </div>

            <div style={{ marginTop: 12, fontSize: 12, opacity: 0.75, lineHeight: 1.35 }}>
              Tip: click a piece to see legal moves. Click a highlighted square to move.
              Promotion pops up automatically.
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}
Scroll to Top
0

Subtotal