CI / Generate TypeScript types (push) Canceled after 7s
CI / Server unit tests (push) Canceled after 0s
CI / Build server (push) Canceled after 0s
CI / Build client (push) Canceled after 0s
CI / Playwright E2E tests (push) Canceled after 0s
CI / Build and push Docker image (push) Canceled after 0s
CI / Notify (push) Canceled after 0s
Minihry se otevírají trojklikem na logo a napsáním kódu hry
("help" vypíše seznam do konzole prohlížeče):
- reakce: reakční hra, 3 kola, pořadí podle součtu časů
- snake: had pro více hráčů, simulaci drží server
- tetris: battle Tetris s odpadními řádky a lokálním tréninkem
- pong: Pong 1v1, ovládání myší i klávesami, bot se 4 obtížnostmi
- kresleni: kreslení a hádání jídel z dnešních menu
Ikony her se řídí zvoleným vzhledem. Stav her je v paměti serveru,
předpokládá se jedna replika.
103 lines
4.0 KiB
TypeScript
103 lines
4.0 KiB
TypeScript
import { SnakeGame, SnakeState, TICK_MS, COUNTDOWN_SECONDS, WIDTH, HEIGHT } from '../snakeGame';
|
|
import { GameError } from '../minigames';
|
|
import { fakeClock } from './helpers/fakeClock';
|
|
|
|
/** Náhoda vracející zadané hodnoty a pak nuly (jídlo pak padá do rohu [0,0]). */
|
|
const sequence = (...values: number[]) => () => values.length ? values.shift()! : 0;
|
|
|
|
/** Hodnoty náhody, které umístí jídlo na danou buňku. */
|
|
const foodAt = (x: number, y: number) => [x / WIDTH, y / HEIGHT];
|
|
|
|
function setup(random?: () => number) {
|
|
const clock = fakeClock(random);
|
|
const states: SnakeState[] = [];
|
|
const game = new SnakeGame(s => states.push(s), s => states.push(s), 1, clock);
|
|
const last = () => states[states.length - 1];
|
|
const player = (login: string) => last().players.find(p => p.login === login)!;
|
|
const startGame = (...logins: string[]) => {
|
|
logins.forEach(l => game.join(l));
|
|
game.start(logins[0]);
|
|
clock.advance(COUNTDOWN_SECONDS * 1000);
|
|
};
|
|
const ticks = (n: number) => clock.advance(n * TICK_MS);
|
|
return { game, clock, last, player, startGame, ticks };
|
|
}
|
|
|
|
describe('had', () => {
|
|
test('po odpočtu začne hra a had se posouvá o políčko za tick', () => {
|
|
const ctx = setup();
|
|
ctx.startGame('alice');
|
|
expect(ctx.last().phase).toBe('playing');
|
|
expect(ctx.player('alice').body).toEqual([[5, 10], [4, 10], [3, 10]]);
|
|
ctx.ticks(2);
|
|
expect(ctx.player('alice').body).toEqual([[7, 10], [6, 10], [5, 10]]);
|
|
});
|
|
|
|
test('zatáčí podle směru a obrat o 180° ignoruje', () => {
|
|
const ctx = setup();
|
|
ctx.startGame('alice');
|
|
ctx.game.turn('alice', 'left'); // obrat — ignoruje se
|
|
ctx.ticks(1);
|
|
expect(ctx.player('alice').body[0]).toEqual([6, 10]);
|
|
ctx.game.turn('alice', 'up');
|
|
ctx.ticks(1);
|
|
expect(ctx.player('alice').body[0]).toEqual([6, 9]);
|
|
});
|
|
|
|
test('nesmyslný směr se ignoruje', () => {
|
|
const ctx = setup();
|
|
ctx.startGame('alice');
|
|
ctx.game.turn('alice', 'diagonal');
|
|
ctx.ticks(1);
|
|
expect(ctx.player('alice').body[0]).toEqual([6, 10]);
|
|
});
|
|
|
|
test('sněděné jídlo prodlouží hada a přidá bod', () => {
|
|
const ctx = setup(sequence(...foodAt(7, 10)));
|
|
ctx.startGame('alice');
|
|
expect(ctx.last().food).toEqual([[7, 10]]);
|
|
ctx.ticks(2);
|
|
expect(ctx.player('alice').score).toBe(1);
|
|
expect(ctx.player('alice').body).toHaveLength(4);
|
|
expect(ctx.last().food).not.toContainEqual([7, 10]);
|
|
});
|
|
|
|
test('náraz do zdi ukončí sólo hru', () => {
|
|
const ctx = setup();
|
|
ctx.startGame('alice');
|
|
ctx.ticks(WIDTH);
|
|
expect(ctx.last().phase).toBe('finished');
|
|
expect(ctx.player('alice')).toMatchObject({ alive: false, place: 1 });
|
|
});
|
|
|
|
test('ve hře více hráčů vyhrává poslední přeživší', () => {
|
|
const ctx = setup();
|
|
ctx.startGame('alice', 'bob');
|
|
// alice startuje v řádku 6, nahoru do zdi dorazí v 7. ticku
|
|
ctx.game.turn('alice', 'up');
|
|
ctx.ticks(7);
|
|
expect(ctx.last().phase).toBe('finished');
|
|
expect(ctx.player('bob')).toMatchObject({ alive: true, place: 1 });
|
|
expect(ctx.player('alice')).toMatchObject({ alive: false, place: 2 });
|
|
});
|
|
|
|
test('čelní srážka zabije oba hady', () => {
|
|
const ctx = setup();
|
|
ctx.startGame('alice', 'bob');
|
|
// alice (5,6)→ doprava, bob (26,13)← doleva; po 7 tickách alice zatočí dolů
|
|
// a po dalších 7 se obě hlavy potkají na (12,13)
|
|
ctx.ticks(7);
|
|
ctx.game.turn('alice', 'down');
|
|
ctx.ticks(7);
|
|
const state = ctx.last();
|
|
expect(state.phase).toBe('finished');
|
|
expect(state.players.every(p => !p.alive)).toBe(true);
|
|
});
|
|
|
|
test('do běžící hry se nelze připojit', () => {
|
|
const ctx = setup();
|
|
ctx.startGame('alice');
|
|
expect(() => ctx.game.join('bob')).toThrow(GameError);
|
|
});
|
|
});
|