From ee58ab25fbc363d06e18c3b657dd5836ec275c22 Mon Sep 17 00:00:00 2001 From: Andre Padez Date: Fri, 17 Oct 2025 03:07:07 +0100 Subject: [PATCH] working on refactor --- src/index.ts | 6 +- src/types.ts | 62 ++-- src/useBoard/MazeGenerator.ts | 347 ++++++++++++++++++++ src/useBoard/MazeRenderer.ts | 116 +++++++ src/useBoard/index.ts | 1 + src/useBoard/useBoard.ts | 46 +++ src/useCursor/index.ts | 1 + src/useCursor/useCursor.ts | 37 +++ src/useGame.ts | 45 +++ {src => src_old}/MazeGenerator.ts | 0 {src => src_old}/MazeRenderer.ts | 0 {src => src_old}/game.css | 0 {src => src_old}/hooks/useTimer.ts | 0 src_old/index.ts | 6 + src_old/types.ts | 54 +++ {src => src_old}/useGame/index.ts | 0 {src => src_old}/useGame/useGame.ts | 22 +- {src => src_old}/useGame/useGameStatus.ts | 0 {src => src_old}/useGame/useHeroRender.ts | 0 {src => src_old}/useGame/useKeyBindings.ts | 0 {src => src_old}/useGame/useMaze.ts | 0 {src => src_old}/useGame/usePlayer.ts | 0 {src => src_old}/useScore/hooks/useTimer.ts | 0 {src => src_old}/useScore/index.ts | 0 {src => src_old}/useScore/useScore.ts | 0 {src => src_old}/useScore/useScorePaths.ts | 0 {src => src_old}/useScore/useScoreTime.ts | 0 27 files changed, 688 insertions(+), 55 deletions(-) create mode 100644 src/useBoard/MazeGenerator.ts create mode 100644 src/useBoard/MazeRenderer.ts create mode 100644 src/useBoard/index.ts create mode 100644 src/useBoard/useBoard.ts create mode 100644 src/useCursor/index.ts create mode 100644 src/useCursor/useCursor.ts create mode 100644 src/useGame.ts rename {src => src_old}/MazeGenerator.ts (100%) rename {src => src_old}/MazeRenderer.ts (100%) rename {src => src_old}/game.css (100%) rename {src => src_old}/hooks/useTimer.ts (100%) create mode 100644 src_old/index.ts create mode 100644 src_old/types.ts rename {src => src_old}/useGame/index.ts (100%) rename {src => src_old}/useGame/useGame.ts (82%) rename {src => src_old}/useGame/useGameStatus.ts (100%) rename {src => src_old}/useGame/useHeroRender.ts (100%) rename {src => src_old}/useGame/useKeyBindings.ts (100%) rename {src => src_old}/useGame/useMaze.ts (100%) rename {src => src_old}/useGame/usePlayer.ts (100%) rename {src => src_old}/useScore/hooks/useTimer.ts (100%) rename {src => src_old}/useScore/index.ts (100%) rename {src => src_old}/useScore/useScore.ts (100%) rename {src => src_old}/useScore/useScorePaths.ts (100%) rename {src => src_old}/useScore/useScoreTime.ts (100%) diff --git a/src/index.ts b/src/index.ts index 0c2a959..5dbda14 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,2 @@ -import './game.css'; -export { MazeGenerator } from "./MazeGenerator"; -export { MazeRenderer } from "./MazeRenderer"; -export * from './useGame'; export * from './types'; - +export * from './useGame'; diff --git a/src/types.ts b/src/types.ts index 2e6c5d4..eec6c29 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,43 +1,38 @@ -import { useScore } from "./useScore"; -import { useGame } from "./useGame"; -export type UseGameType = ReturnType; -export type UseScoreType = ReturnType; +import type { RefObject } from 'react'; +export * from './useGame'; +export * from './useBoard'; +export * from './useCursor'; -export type GamePhase = "idle" | "playing"; -export type PlayStatus = "started" | "hasKey" | "game-over" | "game-won"; -export type GameStatus = "waiting" | "started" | "hasKey" | "game-over" | "game-won"; +export type GameStatus = 'waiting' | 'started' | 'hasKey' | 'paused' | 'game-over' | 'game-won' -export const isPlaying = (status: GameStatus): status is PlayStatus => - status === "started" || status === "hasKey" || status === "game-over" || status === "game-won"; - -export const getGamePhase = (status: GameStatus): GamePhase => - status === "waiting" ? "idle" : "playing"; - -export type KeyLogEntry = { key: string; timestamp: number }; -export type KeyLogProvider = { keyLog: KeyLogEntry[] }; - -export type GameScoreContext = { +export type GameManager = { //return type of useGame + containerRef: RefObject; gameStatus: GameStatus; - setGameStatus: (status: GameStatus) => void; - stopGame: () => void; + renderBoard: () => void; + startGame: () => void; + togglePause: (pause?: boolean) => void; + quitGame: () => void; - // Optional — only Maze-type games provide these - mazeManager?: { - mazeInstanceRef?: { current?: any }; - }; - playerManager?: { - playerPos?: { r: number; c: number } | null; - }; + cursor: Cursor; }; -export type UseScoreParams = { - gameContext: GameScoreContext; - keyManager?: KeyLogProvider; -}; +export type Cursor = { //return type of useCursor (currently useHero) + position: () => Coord; + mode: () => CursorMode; + move: (dCols: number, dRows: number, count: number) => void; + moveLeft: (count?: number) => void; + moveRight: (count?: number) => void; + moveUp: (count?: number) => void; + moveDown: (count?: number) => void; +} + +export type CursorMode = 'normal' | 'insert' | 'replace' | 'visual' | 'visual-line'; + + export type PositionTag = "entrance" | "exit" | "key" | "hero"; -export type Coord = [number, number]; +export type Coord = { row: number, col: number }; export type CellTag = "wall" | "door" | PositionTag; @@ -52,3 +47,8 @@ export interface MazeData { totalSteps: number; } + +export type GameOptions = { + rows: number; + cols: number; +}; diff --git a/src/useBoard/MazeGenerator.ts b/src/useBoard/MazeGenerator.ts new file mode 100644 index 0000000..616e69f --- /dev/null +++ b/src/useBoard/MazeGenerator.ts @@ -0,0 +1,347 @@ +import type { PositionTag, Coord, CellTag, MazeCell, MazeData } from "../types"; + +export class MazeGenerator { + width: number; + height: number; + cols: number; + rows: number; + maze: MazeCell[][]; + totalSteps: number = 0; + + constructor(userCols: number, userRows: number) { + const cellsWide = Math.max(1, Math.floor(userCols / 2)); + const cellsHigh = Math.max(1, Math.floor(userRows / 2)); + + this.width = cellsWide; + this.height = cellsHigh; + + this.cols = 2 * this.width + 1; + this.rows = 2 * this.height + 1; + + this.maze = this.initArray([]); + + this.maze.forEach((row, r) => { + row.forEach((_cell, c) => { + switch (r) { + case 0: + case this.rows - 1: + this.maze[r][c] = ["wall"]; + break; + + default: + if ((r % 2) === 1) { + if ((c === 0) || (c === this.cols - 1)) { + this.maze[r][c] = ["wall"]; + } + } else if (c % 2 === 0) { + this.maze[r][c] = ["wall"]; + } + } + }); + + if (r === 0) { + const doorPos = this.posToSpace(this.rand(1, this.width)); + this.maze[r][doorPos] = ["door", "exit"]; + } + + if (r === this.rows - 1) { + const doorPos = this.posToSpace(this.rand(1, this.width)); + this.maze[r][doorPos] = ["door", "entrance"]; + } + }); + + this.partition(1, this.height - 1, 1, this.width - 1); + } + + private initArray(value?: T): T[][] { + return new Array(this.rows) + .fill(null) + .map(() => new Array(this.cols).fill(value as T)); + } + + private rand(min: number, max: number): number { + return min + Math.floor(Math.random() * (1 + max - min)); + } + + private posToSpace(x: number): number { + return 2 * (x - 1) + 1; + } + + private posToWall(x: number): number { + return 2 * x; + } + + private inBounds(r: number, c: number): boolean { + if ( + typeof this.maze[r] === "undefined" || + typeof this.maze[r]?.[c] === "undefined" + ) { + return false; + } + return true; + } + + private shuffle(array: T[]): T[] { + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } + return array; + } + + private partition(r1: number, r2: number, c1: number, c2: number): void { + let horiz: number, vert: number; + let x: number, y: number, start: number, end: number; + + if (r2 < r1 || c2 < c1) { + return; + } + + if (r1 === r2) { + horiz = r1; + } else { + x = r1 + 1; + y = r2 - 1; + start = Math.round(x + (y - x) / 4); + end = Math.round(x + (y - x) * 3 / 4); + horiz = this.rand(start, end); + } + + if (c1 === c2) { + vert = c1; + } else { + x = c1 + 1; + y = c2 - 1; + start = Math.round(x + (y - x) / 3); + end = Math.round(x + (y - x) * 2 / 3); + vert = this.rand(start, end); + } + + for (let i = this.posToWall(r1) - 1; i <= this.posToWall(r2) + 1; i++) { + for (let j = this.posToWall(c1) - 1; j <= this.posToWall(c2) + 1; j++) { + if (i === this.posToWall(horiz) || j === this.posToWall(vert)) { + this.maze[i][j] = ["wall"]; + } + } + } + + const gaps = this.shuffle([true, true, true, false]); + + if (gaps[0]) { + const gapPosition = this.rand(c1, vert); + this.maze[this.posToWall(horiz)][this.posToSpace(gapPosition)] = []; + } + + if (gaps[1]) { + const gapPosition = this.rand(vert + 1, c2 + 1); + this.maze[this.posToWall(horiz)][this.posToSpace(gapPosition)] = []; + } + + if (gaps[2]) { + const gapPosition = this.rand(r1, horiz); + this.maze[this.posToSpace(gapPosition)][this.posToWall(vert)] = []; + } + + if (gaps[3]) { + const gapPosition = this.rand(horiz + 1, r2 + 1); + this.maze[this.posToSpace(gapPosition)][this.posToWall(vert)] = []; + } + + this.partition(r1, horiz - 1, c1, vert - 1); + this.partition(horiz + 1, r2, c1, vert - 1); + this.partition(r1, horiz - 1, vert + 1, c2); + this.partition(horiz + 1, r2, vert + 1, c2); + } + + private isGap(...cells: Array<[number, number]>): boolean { + return cells.every(([row, col]) => { + const cell = this.maze[row][col]; + if (cell.length > 0) { + if (!cell.includes("door")) return false; + } + return true; + }); + } + + private countSteps( + array: (number | undefined)[][], + r: number, + c: number, + val: number, + stop: CellTag + ): boolean { + if (!this.inBounds(r, c)) { + return false; + } + + if (array[r][c] !== undefined && (array[r][c] as number) <= val) { + return false; + } + + if (!this.isGap([r, c])) { + return false; + } + + array[r][c] = val; + + if (this.maze[r][c].includes(stop)) { + return true; + } + + this.countSteps(array, r - 1, c, val + 1, stop); + this.countSteps(array, r, c + 1, val + 1, stop); + this.countSteps(array, r + 1, c, val + 1, stop); + this.countSteps(array, r, c - 1, val + 1, stop); + + return false; + } + + private getKeyLocation(): Coord { + const fromEntrance = this.initArray(); + const fromExit = this.initArray(); + + this.totalSteps = -1; + + for (let j = 1; j < this.cols - 1; j++) { + if (this.maze[this.rows - 1][j].includes("entrance")) { + this.countSteps(fromEntrance, this.rows - 1, j, 0, "exit"); + } + if (this.maze[0][j].includes("exit")) { + this.countSteps(fromExit, 0, j, 0, "entrance"); + } + } + + let fc = -1, + fr = -1; + + this.maze.forEach((_row, r) => { + _row.forEach((_cell, c) => { + if (typeof fromEntrance[r]?.[c] === "undefined") { + return; + } + const stepCount = + (fromEntrance[r][c] ?? Infinity) + (fromExit[r][c] ?? Infinity); + if (stepCount > this.totalSteps) { + fr = r; + fc = c; + this.totalSteps = stepCount; + } + }); + }); + + return [fr, fc]; + } + + placeKey(): void { + const [fr, fc] = this.getKeyLocation(); + this.maze[fr][fc] = ["key"]; + } + + shortestPath( + fromTag: PositionTag, + toTag: PositionTag, + currentPosition?: Coord + ): Coord[] { + let start: Coord | undefined; + let end: Coord | undefined; + + if (fromTag === "hero" && currentPosition) { + start = currentPosition; + } else { + for (let r = 0; r < this.rows; r++) { + for (let c = 0; c < this.cols; c++) { + if (this.maze[r][c].includes(fromTag)) start = [r, c]; + } + } + } + + for (let r = 0; r < this.rows; r++) { + for (let c = 0; c < this.cols; c++) { + if (this.maze[r][c].includes(toTag)) end = [r, c]; + } + } + + if (!start || !end) return []; + + const visited = this.initArray(false); + const prev = this.initArray(null); + const queue: Coord[] = [[...start]]; + visited[start[0]][start[1]] = true; + + const dirs: Coord[] = [ + [-1, 0], + [1, 0], + [0, -1], + [0, 1], + ]; + + while (queue.length > 0) { + const [r, c] = queue.shift() as Coord; + + if (r === end[0] && c === end[1]) { + const path: Coord[] = []; + let curr: Coord | null = end; + while (curr) { + path.unshift(curr); + curr = prev[curr[0]][curr[1]]; + } + return path; + } + + for (const [dr, dc] of dirs) { + const nr = r + dr, + nc = c + dc; + if (!this.inBounds(nr, nc)) continue; + if (visited[nr][nc]) continue; + if (this.maze[nr][nc].includes("wall")) continue; + + visited[nr][nc] = true; + prev[nr][nc] = [r, c]; + queue.push([nr, nc]); + } + } + + return []; + } + + shortestPathToKey(currentPosition?: Coord): Coord[] { + return this.shortestPath( + currentPosition ? "hero" : "entrance", + "key", + currentPosition + ); + } + + shortestPathToExit(currentPosition?: Coord): Coord[] { + return this.shortestPath( + currentPosition ? "hero" : "entrance", + "exit", + currentPosition + ); + } + + getDistance( + fromTag: PositionTag, + toTag: PositionTag, + currentPosition?: Coord + ): number { + const path = this.shortestPath(fromTag, toTag, currentPosition); + return path.length ? path.length - 1 : Infinity; + } + + getData(): MazeData { + return { + maze: this.maze, + width: this.width, + height: this.height, + cols: this.cols, + rows: this.rows, + totalSteps: this.totalSteps, + }; + } + + toJSON(): MazeData { + return this.getData(); + } +} + diff --git a/src/useBoard/MazeRenderer.ts b/src/useBoard/MazeRenderer.ts new file mode 100644 index 0000000..b8fcc35 --- /dev/null +++ b/src/useBoard/MazeRenderer.ts @@ -0,0 +1,116 @@ +import type { MazeData } from "../types"; + +export class MazeRenderer { + private mazeData: MazeData; + private numbered: boolean; + private parentDiv?: HTMLElement; + + constructor(mazeData: MazeData, numbered: boolean = false) { + this.mazeData = mazeData; + this.numbered = numbered; + } + + display(parentDiv: HTMLElement | null): boolean { + this.parentDiv = parentDiv ?? undefined; + + if (!this.parentDiv) { + console.error("Cannot initialise maze - parent element not provided."); + return false; + } + + while (this.parentDiv.firstChild) { + this.parentDiv.removeChild(this.parentDiv.firstChild); + } + + const container = document.createElement("div"); + container.id = "maze"; + container.dataset.steps = String(this.mazeData.totalSteps); + + const makeCell = (cls = "", text = "", attrs?: Record) => { + const d = document.createElement("div"); + d.className = cls; + if (text) d.textContent = text; + if (attrs) for (const k in attrs) d.setAttribute(k, attrs[k]); + return d; + }; + + const rowLabel = (r: number) => (r > 0 && r < this.mazeData.rows ? String(r) : ""); + const colLabel = (c: number) => (c > 0 && c < this.mazeData.cols ? String(c) : ""); + + if (this.numbered) { + const topStrip = document.createElement("div"); + topStrip.className = "number-strip number-strip-top"; + topStrip.appendChild(makeCell("number corner", "")); + for (let c = 0; c < this.mazeData.cols; c++) { + topStrip.appendChild(makeCell("number col", colLabel(c), { "data-c": String(c) })); + } + topStrip.appendChild(makeCell("number corner", "")); + container.appendChild(topStrip); + } + + this.mazeData.maze.forEach((row, r) => { + const rowDiv = document.createElement("div"); + rowDiv.className = "maze-row"; + rowDiv.setAttribute("data-r", String(r)); + + if (this.numbered) { + rowDiv.appendChild(makeCell("number row", rowLabel(r), { "data-r": String(r) })); + } + + row.forEach((cell, c) => { + const cellDiv = document.createElement("div"); + if (cell) cellDiv.className = cell.join(" "); + cellDiv.setAttribute("data-r", String(r)); + cellDiv.setAttribute("data-c", String(c)); + rowDiv.appendChild(cellDiv); + }); + + if (this.numbered) { + rowDiv.appendChild(makeCell("number row", rowLabel(r), { "data-r": String(r) })); + } + + container.appendChild(rowDiv); + }); + + if (this.numbered) { + const bottomStrip = document.createElement("div"); + bottomStrip.className = "number-strip number-strip-bottom"; + bottomStrip.appendChild(makeCell("number corner", "")); + for (let c = 0; c < this.mazeData.cols; c++) { + bottomStrip.appendChild(makeCell("number col", colLabel(c), { "data-c": String(c) })); + } + bottomStrip.appendChild(makeCell("number corner", "")); + container.appendChild(bottomStrip); + } + + this.parentDiv.appendChild(container); + return true; + } + + getMazeEl(container: HTMLElement): HTMLElement | null { + return container.querySelector("#maze"); + } + + getCellSelector(r: number, c: number): string { + return `.maze-row > div[data-r="${r}"][data-c="${c}"]`; + } + + getCellEl(container: HTMLElement, r: number, c: number): HTMLElement | null { + const maze = this.getMazeEl(container); + if (!maze) return null; + return maze.querySelector(this.getCellSelector(r, c)) as HTMLElement | null; + } + + addCellClass(container: HTMLElement, r: number, c: number, cls: string): void { + this.getCellEl(container, r, c)?.classList.add(cls); + } + + removeCellClass(container: HTMLElement, r: number, c: number, cls: string): void { + this.getCellEl(container, r, c)?.classList.remove(cls); + } + + toggleCellClass(container: HTMLElement, r: number, c: number, cls: string): void { + this.getCellEl(container, r, c)?.classList.toggle(cls); + } +} + diff --git a/src/useBoard/index.ts b/src/useBoard/index.ts new file mode 100644 index 0000000..f381252 --- /dev/null +++ b/src/useBoard/index.ts @@ -0,0 +1 @@ +export * from './useBoard'; diff --git a/src/useBoard/useBoard.ts b/src/useBoard/useBoard.ts new file mode 100644 index 0000000..ae7d9b8 --- /dev/null +++ b/src/useBoard/useBoard.ts @@ -0,0 +1,46 @@ +import { useRef } from "react"; +import type { RefObject } from 'react'; +import { MazeGenerator } from "./MazeGenerator"; +import { MazeRenderer } from "./MazeRenderer"; +import type { MazeCell } from "../types"; + +export function useBoard(cols: number, rows: number) { + const containerRef = useRef(null); + const mazeRef = useRef([]); + const mazeInstanceRef = useRef(null); + const rendererRef = useRef(null); + + function renderBoard() { + const container = containerRef.current; + if (!container) return; + + container.innerHTML = ""; + + const generator = new MazeGenerator(cols, rows); + generator.placeKey(); + + const renderer = new MazeRenderer(generator.getData(), true); + renderer.display(container); + + mazeRef.current = generator.maze; + mazeInstanceRef.current = generator; + rendererRef.current = renderer; + } + + return { + containerRef, + mazeRef, + mazeInstanceRef, + rendererRef, + renderBoard, + }; +} + +export type BoardManager = { + containerRef: RefObject + mazeRef: RefObject + mazeInstanceRef: RefObject + rendererRef: RefObject + renderBoard: () => void +} + diff --git a/src/useCursor/index.ts b/src/useCursor/index.ts new file mode 100644 index 0000000..31ce475 --- /dev/null +++ b/src/useCursor/index.ts @@ -0,0 +1 @@ +export * from './useCursor'; diff --git a/src/useCursor/useCursor.ts b/src/useCursor/useCursor.ts new file mode 100644 index 0000000..e8161dd --- /dev/null +++ b/src/useCursor/useCursor.ts @@ -0,0 +1,37 @@ +import { useRef } from 'react' +import type { Cursor, CursorMode, Coord, BoardManager } from '../types' + +export function useCursor(board: BoardManager): Cursor { + const position = useRef({ row: 0, col: 0 }) + const mode = useRef('normal') + + function move(dCols: number, dRows: number, count: number = 1) { + + } + function moveLeft(count = 1) { + // TODO: implement move left + } + + function moveRight(count = 1) { + // TODO: implement move right + } + + function moveUp(count = 1) { + // TODO: implement move up + } + + function moveDown(count = 1) { + // TODO: implement move down + } + + return { + position: () => position.current, + mode: () => mode.current, + move, + moveLeft, + moveRight, + moveUp, + moveDown + } +} + diff --git a/src/useGame.ts b/src/useGame.ts new file mode 100644 index 0000000..da337de --- /dev/null +++ b/src/useGame.ts @@ -0,0 +1,45 @@ +import { useState } from 'react'; +import type { GameManager, GameOptions, GameStatus } from './types'; +import { useBoard } from './useBoard'; +import { useCursor } from './useCursor'; + +export function useGame(options: GameOptions, platformHook?: unknown): GameManager { + const { cols, rows } = options; + const [gameStatus, setGameStatus] = useState('waiting'); + const boardManager = useBoard(cols, rows); + const { containerRef, renderBoard } = boardManager; + const cursor = useCursor(boardManager); + + function startGame() { + setGameStatus('started'); + } + + function togglePause(pause?: boolean) { + if (typeof pause === 'boolean') { + setGameStatus(pause ? 'paused' : 'started'); + return; + } + setGameStatus(prevStatus => prevStatus === 'paused' ? 'started' : 'paused'); + } + + function quitGame() { + setGameStatus('waiting'); + } + + + const gameManager = { + containerRef, + gameStatus, + renderBoard, + startGame, + togglePause, + quitGame, + cursor, + } + + if (typeof platformHook === 'function') { + platformHook(gameManager); + } + + return gameManager; +} diff --git a/src/MazeGenerator.ts b/src_old/MazeGenerator.ts similarity index 100% rename from src/MazeGenerator.ts rename to src_old/MazeGenerator.ts diff --git a/src/MazeRenderer.ts b/src_old/MazeRenderer.ts similarity index 100% rename from src/MazeRenderer.ts rename to src_old/MazeRenderer.ts diff --git a/src/game.css b/src_old/game.css similarity index 100% rename from src/game.css rename to src_old/game.css diff --git a/src/hooks/useTimer.ts b/src_old/hooks/useTimer.ts similarity index 100% rename from src/hooks/useTimer.ts rename to src_old/hooks/useTimer.ts diff --git a/src_old/index.ts b/src_old/index.ts new file mode 100644 index 0000000..0c2a959 --- /dev/null +++ b/src_old/index.ts @@ -0,0 +1,6 @@ +import './game.css'; +export { MazeGenerator } from "./MazeGenerator"; +export { MazeRenderer } from "./MazeRenderer"; +export * from './useGame'; +export * from './types'; + diff --git a/src_old/types.ts b/src_old/types.ts new file mode 100644 index 0000000..2e6c5d4 --- /dev/null +++ b/src_old/types.ts @@ -0,0 +1,54 @@ +import { useScore } from "./useScore"; +import { useGame } from "./useGame"; +export type UseGameType = ReturnType; +export type UseScoreType = ReturnType; + +export type GamePhase = "idle" | "playing"; +export type PlayStatus = "started" | "hasKey" | "game-over" | "game-won"; +export type GameStatus = "waiting" | "started" | "hasKey" | "game-over" | "game-won"; + +export const isPlaying = (status: GameStatus): status is PlayStatus => + status === "started" || status === "hasKey" || status === "game-over" || status === "game-won"; + +export const getGamePhase = (status: GameStatus): GamePhase => + status === "waiting" ? "idle" : "playing"; + +export type KeyLogEntry = { key: string; timestamp: number }; +export type KeyLogProvider = { keyLog: KeyLogEntry[] }; + +export type GameScoreContext = { + gameStatus: GameStatus; + setGameStatus: (status: GameStatus) => void; + stopGame: () => void; + + // Optional — only Maze-type games provide these + mazeManager?: { + mazeInstanceRef?: { current?: any }; + }; + playerManager?: { + playerPos?: { r: number; c: number } | null; + }; +}; + +export type UseScoreParams = { + gameContext: GameScoreContext; + keyManager?: KeyLogProvider; +}; + +export type PositionTag = "entrance" | "exit" | "key" | "hero"; + +export type Coord = [number, number]; + +export type CellTag = "wall" | "door" | PositionTag; + +export type MazeCell = CellTag[]; + +export interface MazeData { + maze: MazeCell[][]; + width: number; + height: number; + cols: number; + rows: number; + totalSteps: number; +} + diff --git a/src/useGame/index.ts b/src_old/useGame/index.ts similarity index 100% rename from src/useGame/index.ts rename to src_old/useGame/index.ts diff --git a/src/useGame/useGame.ts b/src_old/useGame/useGame.ts similarity index 82% rename from src/useGame/useGame.ts rename to src_old/useGame/useGame.ts index 425d680..81758bf 100644 --- a/src/useGame/useGame.ts +++ b/src_old/useGame/useGame.ts @@ -1,4 +1,3 @@ -import { useEffect } from "react"; import { useMaze } from "./useMaze"; import { usePlayer } from "./usePlayer"; import { useGameStatus } from "./useGameStatus"; @@ -13,24 +12,11 @@ export function useGame(cols: number, rows: number, platformHook?: unknown) { const gameManager = useGameStatus(mazeManager, playerManager); useHeroRender({ mazeManager, playerManager }); - const { initMaze } = mazeManager; + const { render } = mazeManager; const { movePlayer } = playerManager; const { gameStatus, setGameStatus, startGame, stopGame } = gameManager; const { containerRef } = mazeManager; - // Initialize maze on mount - useEffect(() => { - initMaze(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const startNewGame = (autoStart = false) => { - if (autoStart) { - initMaze(true, startGame); - } else { - initMaze(); - } - }; // 🎮 key bindings integrated const keyBindings: UseKeyBindingsType = useKeyBindings({ @@ -56,18 +42,16 @@ export function useGame(cols: number, rows: number, platformHook?: unknown) { }); const fullGameManager = { - // core game containerRef, gameStatus, - setGameStatus, mazeManager, playerManager, + render, startGame, - initGame: startNewGame, + togglePause, movePlayer: (dr: number, dc: number, count: number = 1) => movePlayer(dr, dc, count, gameStatus, setGameStatus), stopGame, - reset: () => initMaze(), // key bindings ...keyBindings, diff --git a/src/useGame/useGameStatus.ts b/src_old/useGame/useGameStatus.ts similarity index 100% rename from src/useGame/useGameStatus.ts rename to src_old/useGame/useGameStatus.ts diff --git a/src/useGame/useHeroRender.ts b/src_old/useGame/useHeroRender.ts similarity index 100% rename from src/useGame/useHeroRender.ts rename to src_old/useGame/useHeroRender.ts diff --git a/src/useGame/useKeyBindings.ts b/src_old/useGame/useKeyBindings.ts similarity index 100% rename from src/useGame/useKeyBindings.ts rename to src_old/useGame/useKeyBindings.ts diff --git a/src/useGame/useMaze.ts b/src_old/useGame/useMaze.ts similarity index 100% rename from src/useGame/useMaze.ts rename to src_old/useGame/useMaze.ts diff --git a/src/useGame/usePlayer.ts b/src_old/useGame/usePlayer.ts similarity index 100% rename from src/useGame/usePlayer.ts rename to src_old/useGame/usePlayer.ts diff --git a/src/useScore/hooks/useTimer.ts b/src_old/useScore/hooks/useTimer.ts similarity index 100% rename from src/useScore/hooks/useTimer.ts rename to src_old/useScore/hooks/useTimer.ts diff --git a/src/useScore/index.ts b/src_old/useScore/index.ts similarity index 100% rename from src/useScore/index.ts rename to src_old/useScore/index.ts diff --git a/src/useScore/useScore.ts b/src_old/useScore/useScore.ts similarity index 100% rename from src/useScore/useScore.ts rename to src_old/useScore/useScore.ts diff --git a/src/useScore/useScorePaths.ts b/src_old/useScore/useScorePaths.ts similarity index 100% rename from src/useScore/useScorePaths.ts rename to src_old/useScore/useScorePaths.ts diff --git a/src/useScore/useScoreTime.ts b/src_old/useScore/useScoreTime.ts similarity index 100% rename from src/useScore/useScoreTime.ts rename to src_old/useScore/useScoreTime.ts