Business Logic Model - Nokia Snake Game

Game logic model

Business Logic Model - Nokia Snake Game

Overview

Game Type: Classic Nokia Snake with modern enhancements
Core Loop: Move snake → Collect food → Grow snake → Avoid collisions → Increase score
Difficulty: Gradual speed increase as snake grows
Scoring: Progressive scoring with consecutive food bonuses

Core Game Logic

Game State Transitions

stateDiagram-v2
    [*] --> Initialized
    Initialized --> MainMenu
    MainMenu --> GamePlaying : Start Game
    GamePlaying --> GamePaused : Pause
    GamePaused --> GamePlaying : Resume
    GamePlaying --> GameOver : Collision
    GameOver --> MainMenu : Return to Menu
    GameOver --> GamePlaying : Restart
    GamePlaying --> MainMenu : Quit to Menu

Game Loop Algorithm

1. Initialize game state
2. Start game loop (60 FPS target):
   a. Process input (keyboard events)
   b. Update game state:
      - Move snake based on current direction
      - Check for collisions
      - Check for food collection
      - Update score
      - Apply game rules
   c. Render game state
   d. Repeat until game over

Game Mechanics

Snake Movement Logic

// Snake movement algorithm
function moveSnake(snake, direction) {
  // 1. Create new head based on current direction
  const newHead = calculateNewHead(snake.head, direction);
  
  // 2. Add new head to snake body
  snake.body.unshift(newHead);
  
  // 3. If no food collected, remove tail
  if (!foodCollected) {
    snake.body.pop();
  }
  
  return snake;
}

// Direction calculation
function calculateNewHead(currentHead, direction) {
  switch(direction) {
    case 'UP': return { x: currentHead.x, y: currentHead.y - 1 };
    case 'DOWN': return { x: currentHead.x, y: currentHead.y + 1 };
    case 'LEFT': return { x: currentHead.x - 1, y: currentHead.y };
    case 'RIGHT': return { x: currentHead.x + 1, y: currentHead.y };
  }
}

Food Generation Logic

// Food generation algorithm
function generateFood(gameState) {
  // 1. Generate random position within game bounds
  const position = {
    x: Math.floor(Math.random() * gridWidth),
    y: Math.floor(Math.random() * gridHeight)
  };
  
  // 2. Ensure position doesn't overlap with snake
  while (positionOverlapsSnake(position, gameState.snake)) {
    position.x = Math.floor(Math.random() * gridWidth);
    position.y = Math.floor(Math.random() * gridHeight);
  }
  
  // 3. Determine food type (regular or power-up)
  const foodType = determineFoodType(gameState);
  
  return {
    position,
    type: foodType,
    value: calculateFoodValue(foodType, gameState)
  };
}

// Food type determination (10% chance for power-up)
function determineFoodType(gameState) {
  const powerUpChance = 0.1; // 10% chance
  return Math.random() < powerUpChance ? 'POWER_UP' : 'REGULAR';
}

Power-up Mechanics (Speed Boost)

// Power-up activation
function activatePowerUp(gameState, powerUpType) {
  if (powerUpType === 'SPEED_BOOST') {
    // Increase game speed by 25%
    gameState.gameSpeed *= 1.25;
    
    // Set power-up active flag
    gameState.activePowerUp = {
      type: 'SPEED_BOOST',
      duration: 'UNTIL_NEXT_FOOD', // Lasts until next food collection
      originalSpeed: gameState.gameSpeed / 1.25 // Store for reset
    };
  }
  
  return gameState;
}

// Power-up deactivation (when next food is collected)
function deactivatePowerUp(gameState) {
  if (gameState.activePowerUp?.type === 'SPEED_BOOST') {
    // Reset to original speed
    gameState.gameSpeed = gameState.activePowerUp.originalSpeed;
    gameState.activePowerUp = null;
  }
  
  return gameState;
}

Difficulty Progression

// Gradual speed increase as snake grows
function updateGameSpeed(gameState) {
  const baseSpeed = 10; // Initial speed (grid cells per second)
  const speedIncreasePerSegment = 0.5; // Speed increase per snake segment
  
  // Calculate new speed based on snake length
  const newSpeed = baseSpeed + (gameState.snake.length * speedIncreasePerSegment);
  
  // Apply power-up multiplier if active
  if (gameState.activePowerUp?.type === 'SPEED_BOOST') {
    gameState.gameSpeed = newSpeed * 1.25;
  } else {
    gameState.gameSpeed = newSpeed;
  }
  
  return gameState;
}

Scoring Algorithms

Progressive Scoring System

// Score calculation for food collection
function calculateFoodScore(gameState, foodType) {
  let baseScore = 10; // Base points per food
  
  // Apply consecutive food bonus
  if (gameState.consecutiveFoods > 0) {
    const bonusMultiplier = 1 + (gameState.consecutiveFoods * 0.1); // 10% increase per consecutive food
    baseScore = Math.floor(baseScore * bonusMultiplier);
  }
  
  // Apply food type multiplier
  switch(foodType) {
    case 'REGULAR':
      return baseScore;
    case 'POWER_UP':
      return baseScore * 2; // Double points for power-up food
    default:
      return baseScore;
  }
}

// Update consecutive food counter
function updateConsecutiveFoods(gameState, foodCollected) {
  if (foodCollected) {
    gameState.consecutiveFoods++;
  } else {
    gameState.consecutiveFoods = 0; // Reset on game over or missed food
  }
  
  return gameState;
}

High Score Tracking

// High score management
function updateHighScore(currentScore, highScores) {
  // Add new score
  const newScoreEntry = {
    score: currentScore,
    date: new Date().toISOString(),
    snakeLength: gameState.snake.length
  };
  
  // Add to scores array
  highScores.push(newScoreEntry);
  
  // Sort descending by score
  highScores.sort((a, b) => b.score - a.score);
  
  // Keep only top 10 scores
  return highScores.slice(0, 10);
}

State Management Logic

Game State Structure

const initialGameState = {
  // Game status
  status: 'MAIN_MENU', // MAIN_MENU, PLAYING, PAUSED, GAME_OVER
  gameSpeed: 10, // Grid cells per second
  
  // Snake data
  snake: {
    body: [{x: 10, y: 10}], // Array of positions
    direction: 'RIGHT', // Current direction
    length: 1
  },
  
  // Game objects
  food: {x: 5, y: 5, type: 'REGULAR', value: 10},
  powerUps: [], // Active power-ups
  
  // Game metrics
  score: 0,
  consecutiveFoods: 0, // For progressive scoring
  gameTime: 0, // Time in seconds
  
  // Settings
  gridSize: {width: 20, height: 20},
  controls: {up: 'ArrowUp', down: 'ArrowDown', left: 'ArrowLeft', right: 'ArrowRight'},
  
  // Power-up state
  activePowerUp: null
};

State Persistence Logic

// Save game state
function saveGameState(gameState) {
  const saveData = {
    // Basic game state (snake, food, score)
    snake: gameState.snake,
    food: gameState.food,
    score: gameState.score,
    consecutiveFoods: gameState.consecutiveFoods,
    gameTime: gameState.gameTime,
    
    // Game settings
    gameSpeed: gameState.gameSpeed,
    gridSize: gameState.gridSize,
    
    // Timestamp for reference
    savedAt: new Date().toISOString()
  };
  
  // Serialize and save to localStorage
  localStorage.setItem('nokiaSnake_saveGame', JSON.stringify(saveData));
}

// Load game state
function loadGameState() {
  const savedData = localStorage.getItem('nokiaSnake_saveGame');
  if (!savedData) return null;
  
  try {
    const parsedData = JSON.parse(savedData);
    
    // Merge with initial state
    return {
      ...initialGameState,
      ...parsedData,
      status: 'PLAYING' // Set to playing when loaded
    };
  } catch (error) {
    console.error('Failed to load game state:', error);
    return null;
  }
}

Data Flow Design

Game Loop Data Flow

Input Processing:
1. Keyboard event → Input Handler → Validated Input → Game State Update

Game State Update:
1. Current State + Validated Input → Game Engine → Updated State
2. Updated State → Collision Detection → Collision Results
3. Collision Results → Game Rules → Final State
4. Final State → Score Calculation → Updated Score

Rendering:
1. Final State → Rendering Engine → Visual Representation
2. Visual Representation → WebGL Context → Screen Update

Score Update:
1. Game Events → Score Service → Score Calculation
2. Score Calculation → High Score Check → High Score Update
3. High Score Update → Persistence Service → Local Storage

State Persistence Flow

Save Game:
1. User requests save → Game State Serialization → JSON Data
2. JSON Data → Local Storage API → Browser Storage
3. Success/Failure → User Feedback

Load Game:
1. User requests load → Local Storage API → JSON Data
2. JSON Data → Game State Deserialization → Game State
3. Game State → Validation → Validated State
4. Validated State → Game Initialization → Continue Game

High Score Persistence:
1. Game Over → Score Calculation → Final Score
2. Final Score → High Score Array Update → Sorted Array
3. Sorted Array → Local Storage API → Persistent Storage

Error Handling Logic

Silent Fail Strategy

// Global error handler
function handleGameError(error, context) {
  // Log error for debugging
  console.error(`Game error in ${context}:`, error);
  
  // Attempt to continue game if possible
  try {
    // Fallback to safe state
    const safeState = getSafeState();
    
    // Continue with safe state
    return safeState;
  } catch (fallbackError) {
    // If fallback fails, restart game
    console.error('Fallback failed, restarting game:', fallbackError);
    return restartGame();
  }
}

// Safe state fallback
function getSafeState() {
  return {
    ...initialGameState,
    status: 'PLAYING',
    errorRecovery: true // Flag to indicate recovery state
  };
}

Input Validation Flow

1. Raw Input → Input Validator → Validation Rules
2. Validation Rules → State Check → Valid/Invalid
3. Valid Input → Game State Update
4. Invalid Input → Error Logging → Safe Fallback
5. Safe Fallback → Continue Game

Performance Considerations

Game Loop Optimization

  1. Fixed Time Step: Use fixed time step for consistent gameplay
  2. Frame Skipping: Allow frame skipping if behind schedule
  3. State Batching: Batch state updates for efficiency
  4. Rendering Optimization: Use WebGL best practices

Memory Management

  1. Object Pooling: Reuse game objects where possible
  2. Garbage Collection: Minimize object creation in game loop
  3. State Serialization: Efficient serialization for persistence

Security Considerations

Input Validation (SECURITY-05 Compliance)

// Comprehensive input validation
function validateInput(input, gameState) {
  // 1. Type checking
  if (typeof input !== 'object') return false;
  
  // 2. Property validation
  if (!input.hasOwnProperty('key') || !input.hasOwnProperty('action')) return false;
  
  // 3. Key validation (allowed keys only)
  const allowedKeys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ' ', 'Escape'];
  if (!allowedKeys.includes(input.key)) return false;
  
  // 4. Action validation
  const allowedActions = ['KEY_DOWN', 'KEY_UP'];
  if (!allowedActions.includes(input.action)) return false;
  
  // 5. Game state validation (prevent invalid moves)
  if (input.action === 'KEY_DOWN') {
    return validateGameMove(input.key, gameState);
  }
  
  return true;
}

// Game move validation
function validateGameMove(key, gameState) {
  const currentDirection = gameState.snake.direction;
  
  // Prevent 180-degree turns (can't go right when moving left)
  switch(key) {
    case 'ArrowUp': return currentDirection !== 'DOWN';
    case 'ArrowDown': return currentDirection !== 'UP';
    case 'ArrowLeft': return currentDirection !== 'RIGHT';
    case 'ArrowRight': return currentDirection !== 'LEFT';
    default: return true;
  }
}

Exception Handling (SECURITY-15 Compliance)

// Global exception handler
window.addEventListener('error', (event) => {
  // Log error
  console.error('Unhandled error:', event.error);
  
  // Attempt to recover
  try {
    // Save current state if possible
    if (window.gameState) {
      localStorage.setItem('nokiaSnake_errorRecovery', JSON.stringify(window.gameState));
    }
    
    // Show recovery message
    showRecoveryMessage();
    
    // Restart game
    setTimeout(() => {
      window.location.reload();
    }, 3000);
  } catch (recoveryError) {
    console.error('Recovery failed:', recoveryError);
  }
  
  // Prevent default error handling
  event.preventDefault();
});