From 1a5505fe3fd48f5b60beb19c8d6b262dd3446ea8 Mon Sep 17 00:00:00 2001 From: martin Date: Sun, 28 May 2023 18:22:44 +0200 Subject: [PATCH] Each possible position now contains the path the character takes to reach it --- .../ClientApp/src/game/direction.ts | 7 +- .../src/game/possibleMovesAlgorithm.ts | 55 +++++++---- .../tests/game/possibleMovesAlgorithm.test.ts | 95 +++++++++++++------ 3 files changed, 108 insertions(+), 49 deletions(-) diff --git a/pac-man-board-game/ClientApp/src/game/direction.ts b/pac-man-board-game/ClientApp/src/game/direction.ts index 60ad123..92e9962 100644 --- a/pac-man-board-game/ClientApp/src/game/direction.ts +++ b/pac-man-board-game/ClientApp/src/game/direction.ts @@ -1,6 +1,11 @@ +import {EnumType} from "typescript"; + export enum Direction { left, up, right, down -} \ No newline at end of file +} + +export const getDirections = () => Object.values(Direction) + .filter(d => !isNaN(Number(d))) as Direction[]; diff --git a/pac-man-board-game/ClientApp/src/game/possibleMovesAlgorithm.ts b/pac-man-board-game/ClientApp/src/game/possibleMovesAlgorithm.ts index 5fe922f..2d8fe57 100644 --- a/pac-man-board-game/ClientApp/src/game/possibleMovesAlgorithm.ts +++ b/pac-man-board-game/ClientApp/src/game/possibleMovesAlgorithm.ts @@ -1,13 +1,13 @@ import {TileType} from "./tileType"; import {Character, PacMan} from "./character"; -import {Direction} from "./direction"; +import {Direction, getDirections} from "./direction"; /** * Finds all the possible positions for the character to move to * @param board The board the character is on * @param character The current position of the character * @param steps The number of steps the character can move - * @returns {Path[]} An array of paths the character can move to + * @returns An array of paths the character can move to */ export default function findPossiblePositions(board: GameMap, character: Character, steps: number): Path[] { return findPossibleRecursive(board, character.position, steps, character instanceof PacMan); @@ -16,41 +16,55 @@ export default function findPossiblePositions(board: GameMap, character: Charact /** * Uses recursion to move through the board and find all the possible positions * @param board The board the character is on - * @param path The current path the character is on + * @param currentPos The current path the character is on * @param steps The number of steps the character can move * @param isPacMan True if the character is Pac-Man, false if it is a ghost - * @returns {Path[]} An array of paths the character can move to + * @returns An array of paths the character can move to */ -function findPossibleRecursive(board: GameMap, path: Path, steps: number, isPacMan: boolean): Path[] { +function findPossibleRecursive(board: GameMap, currentPos: Path, steps: number, isPacMan: boolean): Path[] { const paths: Path[] = []; - if (isOutsideBoard(path, board.length)) { + if (isOutsideBoard(currentPos, board.length)) { if (isPacMan) { - return addTeleportationTiles(board, path, steps, isPacMan); + return addTeleportationTiles(board, currentPos, steps, isPacMan); } - } else if (!isWall(board, path)) { + } else if (!isWall(board, currentPos)) { if (steps === 0) { - paths.push(path); + paths.push(currentPos); } else { + addToPath(currentPos); + steps--; - paths.push(...tryMove(board, path, Direction.up, steps, isPacMan)); - paths.push(...tryMove(board, path, Direction.right, steps, isPacMan)); - paths.push(...tryMove(board, path, Direction.down, steps, isPacMan)); - paths.push(...tryMove(board, path, Direction.left, steps, isPacMan)); + for (const direction of getDirections()) { + paths.push(...tryMove(board, currentPos, direction, steps, isPacMan)); + } } } return paths; } /** - * Tries to move the character in the given direction + * Adds the current position to the path, if it's not on the spawn and it's not already in the path + * @param currentPos The current path the character is on + */ +function addToPath(currentPos: Path): void { + if (!currentPos.path) { + currentPos.path = []; + } else if(!currentPos.path.includes(currentPos.end)) { + currentPos.path = [...currentPos.path, currentPos.end]; + } +} + +/** + * Tries to move the character in the given direction. The character can move in all directions except the one it came from. + * Only Pac-Man characters can move through the teleportation tiles. * @param board The board the character is on * @param path The current path the character is on * @param direction The direction to move in * @param steps The number of steps the character can move * @param isPacMan True if the character is Pac-Man, false if it is a ghost - * @returns {Path[]} An array of paths the character can move to + * @returns An array of paths the character can move to */ function tryMove(board: GameMap, path: Path, direction: Direction, steps: number, isPacMan: boolean): Path[] { @@ -81,7 +95,7 @@ function tryMove(board: GameMap, path: Path, direction: Direction, steps: number if (path.direction !== (direction + 2) % 4) { return findPossibleRecursive(board, { - end: getNewPosition(), direction: direction + end: getNewPosition(), direction: direction, path: path.path }, steps, isPacMan); } return []; @@ -90,15 +104,16 @@ function tryMove(board: GameMap, path: Path, direction: Direction, steps: number /** * Finds all the possible paths when using teleportation tiles * @param board The board the character is on - * @param path The current path the character is on + * @param currentPath The current path the character is on * @param steps The number of steps the character can move * @param isPacMan True if the character is Pac-Man, false if it is a ghost */ -function addTeleportationTiles(board: GameMap, path: Path, steps: number, isPacMan: boolean): Path[] { +function addTeleportationTiles(board: GameMap, currentPath: Path, steps: number, isPacMan: boolean): Path[] { const possiblePositions = findTeleportationTiles(board); const paths: Path[] = []; for (const pos of possiblePositions) { - if (pos.end.x !== Math.max(path.end.x, 0) || pos.end.y !== Math.max(path.end.y, 0)) { + if (pos.end.x !== Math.max(currentPath.end.x, 0) || pos.end.y !== Math.max(currentPath.end.y, 0)) { + pos.path = currentPath.path; paths.push(...findPossibleRecursive(board, pos, steps, isPacMan)); } } @@ -108,7 +123,7 @@ function addTeleportationTiles(board: GameMap, path: Path, steps: number, isPacM /** * Finds all the teleportation tiles on the board * @param board The board the character is on - * @returns {Path[]} An array of paths containing the teleportation tiles + * @returns An array of paths containing the teleportation tiles */ function findTeleportationTiles(board: GameMap): Path[] { const possiblePositions: Path[] = []; diff --git a/pac-man-board-game/ClientApp/tests/game/possibleMovesAlgorithm.test.ts b/pac-man-board-game/ClientApp/tests/game/possibleMovesAlgorithm.test.ts index 3dfc3ea..1f560ed 100644 --- a/pac-man-board-game/ClientApp/tests/game/possibleMovesAlgorithm.test.ts +++ b/pac-man-board-game/ClientApp/tests/game/possibleMovesAlgorithm.test.ts @@ -13,48 +13,91 @@ beforeEach(() => { test("Pac-Man rolls one from start, should return one position", () => { const result = possibleMovesAlgorithm(testMap, pacMan, 1); expect(result.length).toBe(1); - expect(result).toEqual(getPath({at: {x: 3, y: 2}, direction: Direction.up})); + expect(result[0].path?.length).toBe(0); + expect(result).toEqual([{end: {x: 3, y: 2}, direction: Direction.up, path: []}]); }); test("Pac-Man rolls two from start, should return one position", () => { const result = possibleMovesAlgorithm(testMap, pacMan, 2); expect(result.length).toBe(1); - expect(result).toEqual(getPath({at: {x: 3, y: 1}, direction: Direction.up})); + expect(result[0].path?.length).toBe(1); + expect(result).toEqual([{end: {x: 3, y: 1}, direction: Direction.up, path: [{x: 3, y: 2}]}]); }); test("Pac-Man rolls three from start, should return two positions", () => { const result = possibleMovesAlgorithm(testMap, pacMan, 3); expect(result.length).toBe(2); - arrayEquals(result, getPath({at: {x: 2, y: 1}, direction: Direction.left}, {at: {x: 4, y: 1}, direction: Direction.right})); + arrayEquals(result, [{end: {x: 2, y: 1}, direction: Direction.left, path: [{x: 3, y: 2}, {x: 3, y: 1}]}, + {end: {x: 4, y: 1}, direction: Direction.right, path: [{x: 3, y: 2}, {x: 3, y: 1}]}]); }); test("Pac-Man rolls four from start, should return two positions", () => { const result = possibleMovesAlgorithm(testMap, pacMan, 4); expect(result.length).toBe(2); - arrayEquals(result, getPath({at: {x: 1, y: 1}, direction: Direction.left}, {at: {x: 5, y: 1}, direction: Direction.right})); + arrayEquals(result, [{ + end: {x: 1, y: 1}, + direction: Direction.left, + path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 2, y: 1}] + }, { + end: {x: 5, y: 1}, + direction: Direction.right, + path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}] + }]); }); test("Pac-Man rolls five from start, should return four positions", () => { const result = possibleMovesAlgorithm(testMap, pacMan, 5); expect(result.length).toBe(4); - arrayEquals(result, getPath( - {at: {x: 5, y: 0}, direction: Direction.up}, - {at: {x: 6, y: 1}, direction: Direction.right}, - {at: {x: 1, y: 2}, direction: Direction.down}, - {at: {x: 5, y: 2}, direction: Direction.down} - )); + arrayEquals(result, [{ + end: {x: 5, y: 0}, + direction: Direction.up, + path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}, {x: 5, y: 1}] + }, { + end: {x: 6, y: 1}, + direction: Direction.right, + path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}, {x: 5, y: 1}] + }, { + end: {x: 1, y: 2}, + direction: Direction.down, + path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 2, y: 1}, {x: 1, y: 1}] + }, { + end: {x: 5, y: 2}, + direction: Direction.down, + path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}, {x: 5, y: 1}] + } + ]); }); test("Pac-Man rolls six from start, should return six positions", () => { const result = possibleMovesAlgorithm(testMap, pacMan, 6); expect(result.length).toBe(6); - arrayEquals(result, getPath( - {at: {x: 1, y: 3}, direction: Direction.down}, - {at: {x: 0, y: 5}, direction:Direction.right}, - {at: {x: 5, y: 3}, direction: Direction.down}, - {at: {x: 7, y: 1}, direction: Direction.right}, - {at: {x: 10, y: 5}, direction: Direction.left}, - {at: {x: 5, y: 10}, direction: Direction.up})); + arrayEquals(result, [ + { + end: {x: 1, y: 3}, + direction: Direction.down, + path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 2, y: 1}, {x: 1, y: 1}, {x: 1, y: 2}] + }, { + end: {x: 0, y: 5}, + direction: Direction.right, + path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}, {x: 5, y: 1}, {x: 5, y: 0}] + }, { + end: {x: 5, y: 3}, + direction: Direction.down, + path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}, {x: 5, y: 1}, {x: 5, y: 2}] + }, { + end: {x: 7, y: 1}, + direction: Direction.right, + path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}, {x: 5, y: 1}, {x: 6, y: 1}] + }, { + end: {x: 10, y: 5}, + direction: Direction.left, + path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}, {x: 5, y: 1}, {x: 5, y: 0}] + }, { + end: {x: 5, y: 10}, + direction: Direction.up, + path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}, {x: 5, y: 1}, {x: 5, y: 0}] + } + ]); }); test("Pac-Man rolls four from position [5,1] (right), should return 11", () => { @@ -66,13 +109,13 @@ test("Pac-Man rolls four from position [5,1] (right), should return 11", () => { test("Pac-Man rolls three from position [1,5] (left), should return 5", () => { pacMan.follow({end: {x: 1, y: 5}, direction: Direction.left}); const result = possibleMovesAlgorithm(testMap, pacMan, 3); - arrayEquals(result, getPath( - {at: {x: 1, y: 2}, direction: Direction.up}, - {at: {x: 1, y: 8}, direction: Direction.down}, - {at: {x: 5, y: 1}, direction: Direction.down}, - {at: {x: 9, y: 5}, direction: Direction.left}, - {at: {x: 5, y: 9}, direction: Direction.up}, - )); + arrayEquals(result, [ + {end: {x: 1, y: 2}, direction: Direction.up, path: [{x: 1, y: 4}, {x: 1, y: 3}]}, + {end: {x: 1, y: 8}, direction: Direction.down, path: [{x: 1, y: 6}, {x: 1, y: 7}]}, + {end: {x: 5, y: 1}, direction: Direction.down, path: [{x: 0, y: 5}, {x: 5, y: 0}]}, + {end: {x: 9, y: 5}, direction: Direction.left, path: [{x: 0, y: 5}, {x: 10, y: 5}]}, + {end: {x: 5, y: 9}, direction: Direction.up, path: [{x: 0, y: 5}, {x: 5, y: 10}]}, + ]); expect(result.length).toBe(5); }); @@ -82,10 +125,6 @@ test("Pac-Man rolls six from position [1,5] (down), should return 17", () => { expect(result.length).toBe(17); }); -function getPath(...positions: DirectionalPosition[]): Path[] { - return positions.map(pos => ({end: {x: pos.at.x, y: pos.at.y}, direction: pos.direction})); -} - function arrayEquals(result: T, expected: T, message?: string): void { for (const item of expected) { expect(result, message).toContainEqual(item);