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.
129 lines
4.9 KiB
129 lines
4.9 KiB
|
4 days ago
|
import { describe, it, expect } from 'vitest';
|
||
|
|
import { parchis } from '../server/games/parchis-classic.mjs';
|
||
|
|
describe('parchís individual de un dado', () => {
|
||
|
|
it('usa la salida elegida al calcular capturas y conserva partidas antiguas', () => {
|
||
|
|
const state = parchis.create(2, ['verde', 'rojo']);
|
||
|
|
expect(parchis.view(state).starts).toEqual([55, 38]);
|
||
|
|
state.pieces[0][0] = 0;
|
||
|
|
state.pieces[1][0] = 18;
|
||
|
|
state.die = 1;
|
||
|
|
const moved = parchis.apply(state, { type: 'move', piece: 0 }, 0).state;
|
||
|
|
expect(moved.pieces[1][0]).toBe(-1);
|
||
|
|
expect(moved.die).toBe(20);
|
||
|
|
const old = parchis.create(2);
|
||
|
|
delete old.starts;
|
||
|
|
expect(parchis.view(old).starts).toEqual([4, 21, 38, 55]);
|
||
|
|
expect(() => parchis.create(2, ['verde', 'verde'])).toThrow('distintos');
|
||
|
|
});
|
||
|
|
it('resuelve tiradas sin movimientos automáticamente y permite actuar al siguiente jugador', () => {
|
||
|
|
const next = parchis.apply(
|
||
|
|
parchis.create(4),
|
||
|
|
{ type: 'roll' },
|
||
|
|
0,
|
||
|
|
() => 3,
|
||
|
|
).state;
|
||
|
|
expect(next.turn).toBe(1);
|
||
|
|
expect(next.die).toBeNull();
|
||
|
|
expect(parchis.legal(next, 0)).toEqual([]);
|
||
|
|
expect(parchis.legal(next, 1)).toEqual([{ type: 'roll' }]);
|
||
|
|
const moved = parchis.apply(
|
||
|
|
parchis.apply(next, { type: 'roll' }, 1, () => 5).state,
|
||
|
|
{ type: 'move', piece: 0 },
|
||
|
|
1,
|
||
|
|
).state;
|
||
|
|
expect(moved.turn).toBe(2);
|
||
|
|
expect(moved.pieces[1][0]).toBe(0);
|
||
|
|
});
|
||
|
|
it('conserva el seis sin movimiento y penaliza el tercero consecutivo', () => {
|
||
|
|
let state = parchis.create(2);
|
||
|
|
for (let n = 0; n < 2; n++) {
|
||
|
|
state = parchis.apply(state, { type: 'roll' }, 0, () => 6).state;
|
||
|
|
expect(state.turn).toBe(0);
|
||
|
|
expect(state.die).toBeNull();
|
||
|
|
}
|
||
|
|
state = parchis.apply(state, { type: 'roll' }, 0, () => 6).state;
|
||
|
|
expect(state.turn).toBe(1);
|
||
|
|
state = parchis.create(2);
|
||
|
|
state.pieces[0][0] = 8;
|
||
|
|
for (let n = 0; n < 2; n++) {
|
||
|
|
state = parchis.apply(state, { type: 'roll' }, 0, () => 6).state;
|
||
|
|
state = parchis.apply(state, { type: 'move', piece: 0 }, 0).state;
|
||
|
|
}
|
||
|
|
state = parchis.apply(state, { type: 'roll' }, 0, () => 6).state;
|
||
|
|
expect(state.pieces[0][0]).toBe(-1);
|
||
|
|
expect(state.turn).toBe(1);
|
||
|
|
});
|
||
|
|
it('no devuelve a casa la última ficha si está en el pasillo final', () => {
|
||
|
|
const state = parchis.create(2);
|
||
|
|
state.pieces[0][0] = 67;
|
||
|
|
state.sixes = 2;
|
||
|
|
state.lastMoved = 0;
|
||
|
|
expect(
|
||
|
|
parchis.apply(state, { type: 'roll' }, 0, () => 6).state.pieces[0][0],
|
||
|
|
).toBe(67);
|
||
|
|
});
|
||
|
|
it('cuenta siete sin fichas en casa y obliga a abrir barrera', () => {
|
||
|
|
const state = parchis.create(2);
|
||
|
|
state.pieces[0] = [0, 0, 20, 72];
|
||
|
|
const next = parchis.apply(state, { type: 'roll' }, 0, () => 6).state;
|
||
|
|
expect(next.die).toBe(7);
|
||
|
|
expect(parchis.legal(next, 0).map((a) => a.piece)).toEqual([0, 1]);
|
||
|
|
});
|
||
|
|
it('comparte un seguro con un rival y atraviesa dos colores distintos', () => {
|
||
|
|
const state = parchis.create(3);
|
||
|
|
state.pieces[0][0] = 15;
|
||
|
|
state.pieces[1][0] = 0;
|
||
|
|
state.die = 2;
|
||
|
|
const shared = parchis.apply(state, { type: 'move', piece: 0 }, 0).state;
|
||
|
|
expect(shared.pieces[1][0]).toBe(0);
|
||
|
|
expect(shared.pieces[0][0]).toBe(17);
|
||
|
|
state.pieces[0][0] = 16;
|
||
|
|
state.pieces[2][0] = 51;
|
||
|
|
state.die = 3;
|
||
|
|
expect(parchis.legal(state, 0).some((a) => a.piece === 0)).toBe(true);
|
||
|
|
});
|
||
|
|
it('captura al último rival en una salida llena y concede el premio', () => {
|
||
|
|
const state = parchis.create(3);
|
||
|
|
state.pieces[1][0] = 51;
|
||
|
|
state.pieces[2][0] = 34;
|
||
|
|
state.arrivals[1][0] = 1;
|
||
|
|
state.arrivals[2][0] = 2;
|
||
|
|
state.die = 5;
|
||
|
|
const next = parchis.apply(state, { type: 'move', piece: 0 }, 0).state;
|
||
|
|
expect(next.pieces[2][0]).toBe(-1);
|
||
|
|
expect(next.pieces[1][0]).toBe(51);
|
||
|
|
expect(next.die).toBe(20);
|
||
|
|
expect(next.turn).toBe(0);
|
||
|
|
});
|
||
|
|
it('descarta premios imposibles sin pedir pasar turno y conserva una repetición por seis', () => {
|
||
|
|
const state = parchis.create(2);
|
||
|
|
state.pieces[0] = [71, 72, 72, -1];
|
||
|
|
state.die = 1;
|
||
|
|
const next = parchis.apply(state, { type: 'move', piece: 0 }, 0).state;
|
||
|
|
expect(next.turn).toBe(1);
|
||
|
|
expect(next.die).toBeNull();
|
||
|
|
expect(next.bonus).toBe(false);
|
||
|
|
state.extraTurn = true;
|
||
|
|
expect(parchis.apply(state, { type: 'move', piece: 0 }, 0).state.turn).toBe(
|
||
|
|
0,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
it('termina solo con entrada exacta a meta y mantiene el último seguro antes del pasillo', () => {
|
||
|
|
const state = parchis.create(2);
|
||
|
|
state.pieces[0] = [72, 72, 72, 71];
|
||
|
|
state.die = 2;
|
||
|
|
expect(parchis.legal(state, 0)).toEqual([{ type: 'pass' }]);
|
||
|
|
state.die = 1;
|
||
|
|
expect(parchis.apply(state, { type: 'move', piece: 3 }, 0).result).toEqual({
|
||
|
|
winner: 0,
|
||
|
|
reason: 'cuatro fichas en meta',
|
||
|
|
});
|
||
|
|
state.pieces[0] = [63, 72, 72, 72];
|
||
|
|
state.die = 1;
|
||
|
|
const next = parchis.apply(state, { type: 'move', piece: 0 }, 0).state;
|
||
|
|
expect(next.pieces[0][0]).toBe(64);
|
||
|
|
expect(parchis.view(next).outerLength).toBe(65);
|
||
|
|
expect(parchis.view(next).safe).toHaveLength(16);
|
||
|
|
});
|
||
|
|
});
|