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); }); });