feat: add gameInfo export with structured game instructions
- Export gameInfo object with complete game documentation - Includes controls (navigation, game) - Includes rules (movement, game flow, maze elements) - Includes scoring (formula, penalties, size multiplier, examples) - Includes game-over conditions - Includes maze generation details - Includes tracked metrics - Structured for easy platform rendering - Fully typed with GameInfo type
This commit is contained in:
+141
@@ -0,0 +1,141 @@
|
||||
export const gameInfo = {
|
||||
name: 'VIM Maze',
|
||||
description: 'Navigate procedurally generated mazes with VIM-style controls',
|
||||
|
||||
controls: {
|
||||
navigation: [
|
||||
{ keys: 'hjkl', description: 'Move left/down/up/right' },
|
||||
{ keys: '5j, 10l', description: 'Move with count (5 down, 10 right)' },
|
||||
{ keys: '0 or ^', description: 'Jump to leftmost walkable cell in row' },
|
||||
{ keys: '$', description: 'Jump to rightmost walkable cell in row' },
|
||||
{ keys: 'gg', description: 'Jump to topmost walkable row' },
|
||||
{ keys: 'G', description: 'Jump to bottommost walkable row' },
|
||||
{ keys: '.', description: 'Repeat last motion' },
|
||||
],
|
||||
|
||||
game: [
|
||||
{ keys: 'Space', description: 'Start new game (when waiting)' },
|
||||
{ keys: 'q', description: 'Quit game' },
|
||||
{ keys: 'p', description: 'Pause/unpause game' },
|
||||
],
|
||||
},
|
||||
|
||||
rules: {
|
||||
movement: [
|
||||
{ rule: 'Wall Collision', description: 'Movement stops at walls, no wrapping' },
|
||||
{ rule: 'Counted Moves', description: 'Multi-step movements (e.g., 5j) stop at first wall encountered' },
|
||||
{ rule: 'Path Required', description: 'Can only move through open passages' },
|
||||
],
|
||||
|
||||
gameFlow: [
|
||||
{
|
||||
phase: 'Start',
|
||||
location: 'Entrance (top-left area)',
|
||||
objective: 'Navigate to find the key',
|
||||
status: 'started',
|
||||
},
|
||||
{
|
||||
phase: 'Key Found',
|
||||
location: 'Somewhere in maze',
|
||||
objective: 'Navigate to exit',
|
||||
status: 'has-key',
|
||||
},
|
||||
{
|
||||
phase: 'Victory',
|
||||
location: 'Exit (bottom-right area)',
|
||||
objective: 'Reached with key!',
|
||||
status: 'game-won',
|
||||
},
|
||||
],
|
||||
|
||||
mazeElements: [
|
||||
{ element: 'Entrance', appearance: 'Special cell in top-left area', description: 'Starting position' },
|
||||
{ element: 'Key', appearance: 'Special cell in maze', description: 'Must collect before exit' },
|
||||
{ element: 'Exit', appearance: 'Special cell in bottom-right area', description: 'Win condition (with key)' },
|
||||
{ element: 'Walls', appearance: 'Solid barriers', description: 'Cannot pass through' },
|
||||
{ element: 'Passages', appearance: 'Open paths', description: 'Walkable areas' },
|
||||
],
|
||||
},
|
||||
|
||||
scoring: {
|
||||
formula: 'Base Score = 1000 - (time penalty) - (keystroke penalty)',
|
||||
multiplier: 'Final Score = min(1000, Base Score × size multiplier)',
|
||||
range: '0 - 1000 points',
|
||||
|
||||
penalties: [
|
||||
{ factor: 'Time', formula: 'seconds / 10', example: '60 seconds = -6 points' },
|
||||
{ factor: 'Keystrokes', formula: 'total keys / 2', example: '100 keys = -50 points' },
|
||||
],
|
||||
|
||||
sizeMultiplier: {
|
||||
formula: 'max(1.0, (rows × cols) / 500)',
|
||||
description: 'Larger mazes earn higher potential scores',
|
||||
examples: [
|
||||
{ size: '16×24 (384 cells)', multiplier: '1.0x', description: 'Small maze, no bonus' },
|
||||
{ size: '24×32 (768 cells)', multiplier: '1.54x', description: 'Medium maze' },
|
||||
{ size: '32×48 (1536 cells)', multiplier: '3.07x', description: 'Large maze' },
|
||||
],
|
||||
},
|
||||
|
||||
examples: [
|
||||
{
|
||||
size: '16×24',
|
||||
time: '60 seconds',
|
||||
keystrokes: 80,
|
||||
calculation: '1000 - 6 - 40 = 954 × 1.0',
|
||||
score: '954 / 1000',
|
||||
},
|
||||
{
|
||||
size: '32×48',
|
||||
time: '180 seconds',
|
||||
keystrokes: 200,
|
||||
calculation: '1000 - 18 - 100 = 882 × 3.07',
|
||||
score: '1000 / 1000 (capped)',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
gameOver: {
|
||||
conditions: [
|
||||
{
|
||||
type: 'Time Limit',
|
||||
trigger: '10 minutes (600 seconds) by default',
|
||||
configurable: true,
|
||||
message: 'Time ran out!',
|
||||
description: 'Failed to reach exit with key in time',
|
||||
},
|
||||
],
|
||||
note: 'No other game-over conditions - take your time and find the optimal path!',
|
||||
},
|
||||
|
||||
mazeGeneration: {
|
||||
algorithm: 'Depth-first search with backtracking',
|
||||
guarantees: [
|
||||
'Path exists from entrance to key',
|
||||
'Path exists from key to exit',
|
||||
'No isolated areas',
|
||||
'Fully connected passages',
|
||||
],
|
||||
placement: [
|
||||
{ element: 'Entrance', location: 'Top-left area of maze' },
|
||||
{ element: 'Key', location: 'Random location requiring navigation' },
|
||||
{ element: 'Exit', location: 'Bottom-right area of maze' },
|
||||
],
|
||||
},
|
||||
|
||||
metrics: {
|
||||
tracked: [
|
||||
{ metric: 'Time', unit: 'seconds', description: 'Elapsed time since game start' },
|
||||
{ metric: 'Keystrokes', unit: 'count', description: 'Total keys pressed' },
|
||||
{ metric: 'Distance to Key', unit: 'cells', description: 'Current shortest path to key' },
|
||||
{ metric: 'Distance to Exit', unit: 'cells', description: 'Current shortest path to exit' },
|
||||
],
|
||||
displayed: 'Live during gameplay to help you track progress',
|
||||
},
|
||||
|
||||
objective: 'Navigate from entrance to key, then from key to exit, before time runs out.',
|
||||
|
||||
winCondition: 'Reach the exit cell while holding the key (key must be collected first).',
|
||||
} as const;
|
||||
|
||||
export type GameInfo = typeof gameInfo;
|
||||
@@ -1,3 +1,5 @@
|
||||
export * from './types';
|
||||
export * from './useGame';
|
||||
export * from './useGameStatus';
|
||||
export { gameInfo } from './gameInfo';
|
||||
export type { GameInfo } from './gameInfo';
|
||||
|
||||
Reference in New Issue
Block a user