You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

225 lines
7.6 KiB

import { describe, it, expect } from 'vitest';
import { chess } from '../server/games/chess.mjs';
import { checkers } from '../server/games/checkers.mjs';
import { getGame } from '../server/games/registry.mjs';
import { parchis } from '../server/games/parchis.mjs';
describe('ajedrez: reglas en el servidor', () => {
it('rechaza movimientos ilegales y jugar fuera de turno', () => {
expect(() =>
chess.apply(chess.create(), { type: 'move', from: 'e2', to: 'e5' }, 0),
).toThrow();
expect(chess.legal(chess.create(), 1)).toEqual([]);
expect(() =>
chess.apply(chess.create(), { type: 'move', from: 'e7', to: 'e5' }, 1),
).toThrow();
});
it('detecta el mate y preserva el historial para reconstruir la partida', () => {
let state = chess.create();
let result;
for (const [from, to, seat] of [
['f2', 'f3', 0],
['e7', 'e5', 1],
['g2', 'g4', 0],
['d8', 'h4', 1],
] as const) {
const move = chess.apply(state, { type: 'move', from, to }, seat);
state = move.state;
result = move.result;
}
expect(result).toEqual({ winner: 1, reason: 'jaque mate' });
expect(chess.view(state).pgn).toContain('Qh4#');
});
it('conserva la repetición triple al reconstruir desde movimientos', () => {
let state = chess.create();
let result;
for (let round = 0; round < 2; round++)
for (const [from, to, seat] of [
['g1', 'f3', 0],
['g8', 'f6', 1],
['f3', 'g1', 0],
['f6', 'g8', 1],
] as const) {
const applied = chess.apply(state, { type: 'move', from, to }, seat);
state = applied.state;
result = applied.result;
}
expect(result?.winner).toBeNull();
});
});
describe('damas con recorrido largo', () => {
it('aplica las correcciones también a las partidas ya creadas', () => {
expect(getGame('damas', '1.0.0')).toBe(checkers);
expect(getGame('damas', '2.0.0')).toBe(checkers);
});
const position = () => ({
board: Array(64).fill(0),
turn: 0,
chain: null,
quietMoves: 0,
});
it('fuerza capturas y encadena saltos con la misma pieza', () => {
const state = position();
state.board[42] = 1;
state.board[33] = 2;
state.board[17] = 2;
state.board[46] = 1;
expect(checkers.legal(state, 0)).toEqual([
{ type: 'move', from: 42, to: 24, capture: 33 },
]);
const first = checkers.apply(state, { type: 'move', from: 42, to: 24 }, 0);
expect(first.state.turn).toBe(0);
expect(first.state.chain).toBe(24);
expect(() =>
checkers.apply(first.state, { type: 'move', from: 46, to: 37 }, 0),
).toThrow();
const next = checkers.apply(
first.state,
{ type: 'move', from: 24, to: 10 },
0,
);
expect(next.state.board[17]).toBe(0);
expect(next.result?.winner).toBe(0);
});
it('la dama recorre diagonales completas y una ficha propia bloquea el paso', () => {
const state = position();
state.board[42] = -1;
expect(checkers.legal(state, 0).map((m) => m.to)).toEqual(
expect.arrayContaining([33, 24, 35, 28, 21, 14, 7, 49, 56, 51, 60]),
);
state.board[35] = 1;
expect(
checkers
.legal(state, 0)
.filter((m) => m.from === 42)
.map((m) => m.to),
).not.toEqual(expect.arrayContaining([28]));
expect(
checkers.legal(state, 0).some((m) => m.from === 42 && m.to === 7),
).toBe(false);
});
it('la dama captura a distancia y conserva la pieza en una segunda captura', () => {
const state = position();
state.board[42] = -1;
state.board[35] = 2;
state.board[19] = 2;
state.board[5] = 2;
expect(
checkers
.legal(state, 0)
.filter((m) => m.capture === 35)
.map((m) => m.to),
).toEqual([28, 21, 14, 7]);
const first = checkers.apply(state, { type: 'move', from: 42, to: 28 }, 0);
expect(first.state.chain).toBe(28);
expect(first.state.turn).toBe(0);
expect(checkers.legal(first.state, 0)).toEqual([
{ type: 'move', from: 28, to: 10, capture: 19 },
{ type: 'move', from: 28, to: 1, capture: 19 },
]);
const second = checkers.apply(
first.state,
{ type: 'move', from: 28, to: 1 },
0,
);
expect(second.state.board[35]).toBe(0);
expect(second.state.board[19]).toBe(0);
expect(second.state.board[1]).toBe(-1);
expect(second.state.chain).toBeNull();
expect(second.state.turn).toBe(1);
});
it('no permite saltar dos adversarios en un solo salto', () => {
const state = position();
state.board[42] = -1;
state.board[35] = 2;
state.board[28] = 2;
expect(
checkers
.legal(state, 0)
.some((m) => m.to === 21 || m.capture !== undefined),
).toBe(false);
});
it('termina la captura al coronar y permite que las damas retrocedan', () => {
const state = position();
state.board[17] = 1;
state.board[10] = 2;
state.board[12] = 2;
const result = checkers.apply(state, { type: 'move', from: 17, to: 3 }, 0);
expect(result.state.board[3]).toBe(-1);
expect(result.state.chain).toBeNull();
expect(result.state.turn).toBe(1);
expect(
checkers.legal({ ...result.state, turn: 0 }, 0).some((m) => m.to > 3),
).toBe(true);
});
it('declara tablas por inactividad y rechaza entradas arbitrarias', () => {
const state = position();
state.board[42] = -1;
state.board[5] = -2;
state.quietMoves = 79;
expect(
checkers.apply(state, { type: 'move', from: 42, to: 33 }, 0).result
?.winner,
).toBeNull();
expect(() =>
checkers.apply(state, { type: 'move', from: -1, to: 100 }, 0),
).toThrow();
});
});
describe('parchís básico', () => {
it('el dado procede del servidor y salir con un cinco es obligatorio', () => {
const state = parchis.create(2);
const rolled = parchis.apply(state, { type: 'roll', die: 6 }, 0, () => 5);
expect(rolled.state.die).toBe(5);
rolled.state.pieces[0][1] = 10;
expect(parchis.legal(rolled.state, 0).every((a) => a.piece !== 1)).toBe(
true,
);
expect(
parchis.apply(rolled.state, { type: 'move', piece: 0 }, 0).state
.pieces[0][0],
).toBe(0);
expect(() => parchis.apply(state, { type: 'roll' }, 1, () => 5)).toThrow();
});
it('bloquea el paso a través de barreras', () => {
const state = parchis.create(2);
state.pieces[0] = [0, 2, 2, -1];
state.die = 4;
expect(parchis.legal(state, 0).some((a) => a.piece === 0)).toBe(false);
});
it('captura fuera de un seguro y concede 20 pasos', () => {
const state = parchis.create(2);
state.pieces[0][0] = 20;
state.pieces[1][0] = 5;
state.die = 2;
const result = parchis.apply(state, { type: 'move', piece: 0 }, 0);
expect(result.state.pieces[1][0]).toBe(-1);
expect(result.state.die).toBe(20);
expect(result.state.bonus).toBe(true);
});
it('protege los seguros y exige entrada exacta a meta', () => {
const state = parchis.create(2);
state.pieces[0][0] = 15;
state.pieces[1][0] = 0;
state.die = 2;
expect(parchis.legal(state, 0).some((a) => a.piece === 0)).toBe(false);
state.pieces[0][0] = 70;
state.die = 2;
expect(parchis.legal(state, 0)).toEqual([{ type: 'pass' }]);
});
it('acaba al llevar las cuatro fichas a meta y conserva el turno extra', () => {
const state = parchis.create(2);
state.pieces[0] = [71, 71, 71, 70];
state.die = 1;
expect(parchis.apply(state, { type: 'move', piece: 3 }, 0).result).toEqual({
winner: 0,
reason: 'cuatro fichas en meta',
});
const roll = parchis.apply(parchis.create(2), { type: 'roll' }, 0, () => 6);
expect(roll.state.turn).toBe(0);
expect(roll.state.die).toBeNull();
});
});

Powered by TurnKey Linux.