feat: add initialSnakeSize and initialFoodCount to GameOptions

- New option: initialSnakeSize (defaults to 3)
- New option: initialFoodCount (defaults to 1)
- Snake spawns with configurable size
- Initial food count is configurable
- Example: useGame({ initialSnakeSize: 5, initialFoodCount: 2 })
This commit is contained in:
2025-10-31 00:31:52 +00:00
parent b82bef56e5
commit 2fcfd26429
4 changed files with 19 additions and 10 deletions
+2
View File
@@ -35,6 +35,8 @@ export type GameOptions = {
startingLevel?: number;
foodsPerLevel?: number;
maxLevel?: number;
initialSnakeSize?: number;
initialFoodCount?: number;
};
// ============================================================================
+11 -6
View File
@@ -2,11 +2,17 @@ import { useRef, useState } from "react";
import type { Direction, SnakeBody, Position } from "../types";
import type { UseBoardType } from "../useBoard";
export type UseCursorParams = {
initialSnakeSize?: number;
};
export function useCursor(
cols: number,
rows: number,
boardManager: UseBoardType
boardManager: UseBoardType,
params?: UseCursorParams
) {
const initialSnakeSize = params?.initialSnakeSize ?? 3;
const { containerRef } = boardManager;
const [snakeBody, setSnakeBody] = useState<SnakeBody>([]);
const snakeBodyRef = useRef<SnakeBody>([]);
@@ -19,11 +25,10 @@ export function useCursor(
const centerR = Math.floor(rows / 2);
const centerC = Math.floor(cols / 2);
const initialBody: SnakeBody = [
{ r: centerR, c: centerC },
{ r: centerR + 1, c: centerC },
{ r: centerR + 2, c: centerC },
];
const initialBody: SnakeBody = [];
for (let i = 0; i < initialSnakeSize; i++) {
initialBody.push({ r: centerR + i, c: centerC });
}
setSnakeBody(initialBody);
snakeBodyRef.current = initialBody;
+3 -3
View File
@@ -8,17 +8,17 @@ import { useKeyBindings, type UseKeyBindingsType } from "./useKeyBindings";
import { useScore } from "../useScore";
export function useGame(options?: GameOptions, platformHook?: unknown): GameManager {
const { cols = 30, rows = 20, startingLevel, foodsPerLevel, maxLevel } = options ?? {};
const { cols = 30, rows = 20, startingLevel, foodsPerLevel, maxLevel, initialSnakeSize, initialFoodCount } = options ?? {};
const boardManager = useBoard();
const { containerRef, renderBoard } = boardManager;
const snakeManager = useCursor(cols, rows, boardManager);
const snakeManager = useCursor(cols, rows, boardManager, { initialSnakeSize });
const { changeDirection } = snakeManager;
const foodManager = useFood(cols, rows, boardManager);
const gameManager = useGameStatus(boardManager, snakeManager, foodManager, { startingLevel, foodsPerLevel, maxLevel });
const gameManager = useGameStatus(boardManager, snakeManager, foodManager, { startingLevel, foodsPerLevel, maxLevel, initialFoodCount });
const { gameStatus, setGameStatus, startGame, quitGame, togglePause, score, level } = gameManager;
useEffect(() => {
+3 -1
View File
@@ -8,6 +8,7 @@ export type UseGameStatusParams = {
startingLevel?: number;
foodsPerLevel?: number;
maxLevel?: number;
initialFoodCount?: number;
};
export function useGameStatus(
@@ -19,6 +20,7 @@ export function useGameStatus(
const startingLevel = params?.startingLevel ?? 1;
const foodsPerLevel = params?.foodsPerLevel ?? 10;
const maxLevel = params?.maxLevel ?? 25;
const initialFoodCount = params?.initialFoodCount ?? 1;
const { containerRef } = boardManager;
const { initSnake, clearSnake, snakeBodyRef } = snakeManager;
const { spawnFood, clearFood } = foodManager;
@@ -38,7 +40,7 @@ export function useGameStatus(
const startGame = () => {
initSnake();
spawnFood(snakeBodyRef.current, 3);
spawnFood(snakeBodyRef.current, initialFoodCount);
setScore(0);
setLevel(startingLevel);
levelRef.current = startingLevel;