diff --git a/src/features/engine/utils/get-next-board-final-state/__tests__/moving-dice-hits-same-dice.test.ts b/src/features/engine/utils/get-next-board-final-state/__tests__/moving-dice-hits-same-dice.test.ts new file mode 100644 index 0000000..240857a --- /dev/null +++ b/src/features/engine/utils/get-next-board-final-state/__tests__/moving-dice-hits-same-dice.test.ts @@ -0,0 +1,43 @@ +import { expect, test } from 'vitest'; + +import { VECTOR_RIGHT, VECTOR_ZERO } from '../../../constants'; +import { isDice } from '../../../types/entities'; +import { createEntity } from '../../create-entity'; +import { getNextBoardFinalState } from '..'; + +test('two dices moving same direction merge', () => { + const level = [ + createEntity('floor', { position: { x: 0, y: 0 } }), + createEntity('floor', { position: { x: 1, y: 0 } }), + createEntity('floor', { position: { x: 2, y: 0 } }), + createEntity('floor', { position: { x: 3, y: 0 } }), + createEntity('dice', { + position: { x: 0, y: 0 }, + value: 0, + velocity: VECTOR_RIGHT, + }), + createEntity('dice', { + position: { x: 1, y: 0 }, + value: 0, + velocity: VECTOR_ZERO, + }), + ]; + + const result = getNextBoardFinalState({ + entities: level, + velocity: VECTOR_RIGHT, + }); + + expect(result).toBeDefined(); + expect(result?.entities).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + position: { x: 3, y: 0 }, + type: 'dice', + value: 1, + velocity: VECTOR_ZERO, + }), + ]), + ); + expect(result?.entities.filter(isDice)).toHaveLength(1); +}); diff --git a/src/features/engine/utils/get-next-board-final-state/__tests__/two-dices-moving-same-direction-merge.test.ts b/src/features/engine/utils/get-next-board-final-state/__tests__/two-dices-moving-same-direction-merge.test.ts new file mode 100644 index 0000000..d5814e1 --- /dev/null +++ b/src/features/engine/utils/get-next-board-final-state/__tests__/two-dices-moving-same-direction-merge.test.ts @@ -0,0 +1,48 @@ +import { expect, test } from 'vitest'; + +import { DIRECTIONS, VECTOR_ZERO } from '../../../constants'; +import { isDice, Vector } from '../../../types/entities'; +import { createEntity } from '../../create-entity'; +import { getNextBoardFinalState } from '..'; + +test.each(DIRECTIONS)( + 'two dices moving %o merge and stop', + (velocity: Vector) => { + const level = [ + createEntity('floor', { position: { x: 0, y: 0 } }), + createEntity('floor', { position: velocity }), + createEntity('floor', { + position: { x: velocity.x * 2, y: velocity.y * 2 }, + }), + createEntity('dice', { + position: { x: 0, y: 0 }, + value: 0, + velocity, + }), + createEntity('dice', { + position: velocity, + value: 0, + velocity, + }), + ]; + + const result = getNextBoardFinalState({ + entities: level, + velocity, + }); + + expect(result).toBeDefined(); + expect(result?.entities).toHaveLength(4); + expect(result?.entities).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + position: { x: velocity.x * 2, y: velocity.y * 2 }, + type: 'dice', + value: 1, + velocity: VECTOR_ZERO, + }), + ]), + ); + expect(result?.entities.filter(isDice)).toHaveLength(1); + }, +); diff --git a/src/features/engine/utils/get-next-board-final-state/__tests__/two-same-dices-taking-one-floor.test.ts b/src/features/engine/utils/get-next-board-final-state/__tests__/two-same-dices-taking-one-floor.test.ts new file mode 100644 index 0000000..b36bd80 --- /dev/null +++ b/src/features/engine/utils/get-next-board-final-state/__tests__/two-same-dices-taking-one-floor.test.ts @@ -0,0 +1,39 @@ +import { expect, test } from 'vitest'; + +import { VECTOR_DOWN, VECTOR_LEFT, VECTOR_ZERO } from '../../../constants'; +import { createEntity } from '../../create-entity'; +import { getNextBoardFinalState } from '..'; + +test.only('two same dices taking one floor', () => { + const dice1 = createEntity('dice', { + id: 'dice-1', + position: { x: 0, y: 0 }, + velocity: VECTOR_DOWN, + }); + const dice2 = createEntity('dice', { + id: 'dice-2', + position: { x: 1, y: 1 }, + velocity: VECTOR_LEFT, + }); + const floors = [ + createEntity('floor', { position: { x: 0, y: 0 } }), + createEntity('floor', { position: { x: 1, y: 0 } }), + createEntity('floor', { position: { x: 0, y: 1 } }), + createEntity('floor', { position: { x: 1, y: 1 } }), + ]; + + const entities = [dice1, dice2, ...floors]; + + const result = getNextBoardFinalState({ entities }); + + expect(result?.entities).toBeDefined(); + expect(result?.entities).toHaveLength(5); + expect(result?.entities).toContainEqual( + expect.objectContaining({ + isFresh: true, + position: { x: 0, y: 1 }, + type: 'dice', + velocity: VECTOR_ZERO, + }), + ); +}); diff --git a/src/features/engine/utils/get-next-board-final-state/get-next-board-final-state.ts b/src/features/engine/utils/get-next-board-final-state/get-next-board-final-state.ts new file mode 100644 index 0000000..7ee19b5 --- /dev/null +++ b/src/features/engine/utils/get-next-board-final-state/get-next-board-final-state.ts @@ -0,0 +1,49 @@ +import { Entity, isMovable, Vector } from '../../types/entities'; +import { getNextBoardState } from '../get-next-board-state'; +import { getShouldUpdate } from '../get-should-update'; + +export const getNextBoardFinalState = ({ + entities: originalEntities, + velocity, +}: { + entities: Entity[]; + velocity?: Vector; +}): { entities: Entity[] } | undefined => { + // apply velocity to all movable entities + const entities = velocity + ? originalEntities.map((entity) => { + if (isMovable(entity)) { + return { + ...entity, + isForced: true, + velocity, + }; + } + + return { ...entity }; + }) + : originalEntities; + + for (const entity of entities) { + entity.isFresh = false; + } + + let nextEntities: Entity[] = entities; + let previousEntities: Entity[] = []; + + let iterations = 30; + do { + previousEntities = nextEntities; + nextEntities = getNextBoardState({ board: { entities: nextEntities } }).board + .entities; + } while ( + getShouldUpdate({ entities: nextEntities, previousEntities }) && + iterations-- + ); + + const result = previousEntities.map((entity) => ({ + ...entity, + })); + + return { entities: result }; +}; diff --git a/src/features/engine/utils/get-next-board-final-state/index.ts b/src/features/engine/utils/get-next-board-final-state/index.ts new file mode 100644 index 0000000..d7ee429 --- /dev/null +++ b/src/features/engine/utils/get-next-board-final-state/index.ts @@ -0,0 +1 @@ +export * from './get-next-board-final-state';