diff --git a/src/game/Game.test.ts b/src/game/Game.test.ts index 1e2676e..8a0c7fb 100644 --- a/src/game/Game.test.ts +++ b/src/game/Game.test.ts @@ -9,28 +9,29 @@ describe("GameState", () => { ] const unrestrictedBuffer = 999 + const unlimitedTime = 999 * 1000 test("getting size works", () => { - expect((new Game(["00"], [["AA"]], unrestrictedBuffer)).size).toEqual(1) + expect((new Game(["00"], [["AA"]], unrestrictedBuffer, unlimitedTime)).size).toEqual(1) expect((new Game([ "00", "01", "10", "11", - ], [], unrestrictedBuffer)).size).toEqual(2) + ], [], unrestrictedBuffer, unlimitedTime)).size).toEqual(2) }); test("getting cell works", () => { - const game = new Game(threeByThreeMatrix, [["AA"]], unrestrictedBuffer); + const game = new Game(threeByThreeMatrix, [["AA"]], unrestrictedBuffer, unlimitedTime); expect(game.getCell(0, 2)).toEqual("02") }); describe("picking", () => { test("starts with free pick", () => { - const game = new Game([], [["AA"]], 1); + const game = new Game([], [["AA"]], 1, unlimitedTime); expect(game.state).toEqual({selectionMode: SelectionMode.FreePick}) }); test("picking cells works", () => { - const game = new Game(threeByThreeMatrix, [["AA"]], unrestrictedBuffer); + const game = new Game(threeByThreeMatrix, [["AA"]], unrestrictedBuffer, unlimitedTime); game.pick(0, 0); expect(game.state).toEqual({selectionMode: SelectionMode.RowPick, column: 0}); expect(() => game.pick(0, 2)).toThrow(); @@ -39,7 +40,7 @@ describe("GameState", () => { }); test("picking outside of range fails", () => { - const game = new Game(threeByThreeMatrix, [], unrestrictedBuffer); + const game = new Game(threeByThreeMatrix, [], unrestrictedBuffer, unlimitedTime); expect(() => game.pick(-1, 0)).toThrow(); expect(() => game.pick(0, -1)).toThrow(); expect(() => game.pick(3, 0)).toThrow(); @@ -48,7 +49,7 @@ describe("GameState", () => { test("picking fills buffer, fulfills sequence", () => { const simpleSequence = ["00", "10", "20"] - const game = new Game(threeByThreeMatrix, [simpleSequence], unrestrictedBuffer); + const game = new Game(threeByThreeMatrix, [simpleSequence], unrestrictedBuffer, unlimitedTime); expect(game.getSequences()).toEqual([{sequence: simpleSequence, numberOfFulfilled: 0}]) game.pick(0, 0); expect(game.buffer).toEqual(["00"]); @@ -61,7 +62,7 @@ describe("GameState", () => { "AA", "AA", "BB", "BB", "CC", "AA", "CC", "CC", "CC", - ], [sequence], unrestrictedBuffer); + ], [sequence], unrestrictedBuffer, unlimitedTime); expect(game.getSequences()).toEqual([{sequence: sequence, numberOfFulfilled: 0}]) game.pick(0, 0); expect(game.getSequences()).toEqual([{sequence: sequence, numberOfFulfilled: 1}]) @@ -82,7 +83,7 @@ describe("GameState", () => { "AA", "AA", "BB", "BB", "CC", "AA", "CC", "CC", "CC", - ], [["AA", "BB", "CC"]], 3) + ], [["AA", "BB", "CC"]], 3, unlimitedTime) game.pick(0, 0) game.pick(2, 0) game.pick(2, 2) @@ -95,7 +96,7 @@ describe("GameState", () => { "AA", "AA", "BB", "BB", "CC", "AA", "CC", "CC", "CC", - ], [["AA", "BB", "CC"]], 3) + ], [["AA", "BB", "CC"]], 3, unlimitedTime) game.pick(0, 0) game.pick(1, 0) game.pick(1, 2) @@ -103,4 +104,51 @@ describe("GameState", () => { expect(() => game.pick(2, 2)).toThrow(); }) }); + + describe("time management", () => { + let currentTimeProgress: number + + beforeEach(() => { + jest.useFakeTimers() + currentTimeProgress = 0 + Date.now = jest.fn(() => { + return currentTimeProgress + }) + }) + + function fakeTimeProgress(ms: number) { + currentTimeProgress += ms + jest.advanceTimersByTime(ms) + } + + test("loosing through timeout works", () => { + const game = new Game([ + "AA", "AA", "BB", + "BB", "CC", "AA", + "CC", "CC", "CC", + ], [["AA"]], 3, 10_000) + fakeTimeProgress(1_000) + expect(game.remainingMilliseconds).toEqual(9_000) + fakeTimeProgress(1_000) + expect(game.remainingMilliseconds).toEqual(8_000) + fakeTimeProgress(8_000) + expect(game.state).toEqual(EndState.Lost) + expect(game.remainingMilliseconds).toEqual(0) + fakeTimeProgress(1_000) + expect(game.remainingMilliseconds).toEqual(0) + }) + + test("clock stops when game is won", () => { + const game = new Game([ + "AA", "AA", "BB", + "BB", "CC", "AA", + "CC", "CC", "CC", + ], [["AA"]], 3, 10_000) + fakeTimeProgress(1_000) + game.pick(0, 0) + expect(game.remainingMilliseconds).toEqual(9_000) + fakeTimeProgress(1_000) + expect(game.remainingMilliseconds).toEqual(9_000) + }) + }) }); diff --git a/src/game/Game.ts b/src/game/Game.ts index f56d28c..dc00b60 100644 --- a/src/game/Game.ts +++ b/src/game/Game.ts @@ -80,9 +80,30 @@ export class Game { state: State = {selectionMode: SelectionMode.FreePick} public readonly size: number public readonly buffer: string[] = [] + private readonly timeoutInterval: ReturnType + private readonly startTimeTimeStamp: number + private endTimestamp: number | null = null - constructor(public readonly matrix: string[], private readonly sequences: string[][], public readonly maxBufferLength: number) { + constructor( + public readonly matrix: string[], + private readonly sequences: string[][], + public readonly maxBufferLength: number, + public readonly timeout: number, + ) { this.size = Math.sqrt(matrix.length) + this.timeoutInterval = setTimeout(() => { + this.state = EndState.Lost + this.stopClock() + }, + this.timeout) + this.startTimeTimeStamp = Date.now() + } + + get remainingMilliseconds(): number { + if (this.endTimestamp) { + return this.timeout - (this.endTimestamp - this.startTimeTimeStamp) + } + return this.timeout - (Date.now() - this.startTimeTimeStamp) } getCell(row: number, column: number) { @@ -112,11 +133,18 @@ export class Game { }) } + private stopClock(): void { + clearTimeout(this.timeoutInterval) + this.endTimestamp = Date.now() + } + private checkEndGame(): void { const isSequenceFulfilled = (sequence: Sequence) => sequence.sequence.length === sequence.numberOfFulfilled if (this.getSequences().every(isSequenceFulfilled)) { + this.stopClock() this.state = EndState.Won } else { + this.stopClock() if (this.buffer.length >= this.maxBufferLength) { this.state = EndState.Lost }