Files
scopone/docs/CODE_STYLE.md
Giancarmine Salucci 3d1f3e5eb4 chore: initial commit
2026-03-31 18:38:34 +02:00

4.8 KiB

Code Style

Last Updated: 2026-03-31T00:00:00.000Z

Language & Version

  • TypeScript 5.x, strict mode enabled
  • Target: ES2020, module: ESNext, module resolution: bundler
  • noEmit: true — Vite handles transpilation; tsc is used for type-checking only

Naming Conventions

Kind Convention Examples
Types/Interfaces PascalCase Card, GameState, PlayerIndex, TeamScore, ScoreBreakdown
Type aliases PascalCase Suit, PlayerIndex
Classes PascalCase BootScene, MenuScene, GameScene
Functions camelCase buildDeck(), findCaptures(), chooseMove(), applyMove()
Constants UPPER_SNAKE PRIMIERA_VALUES, SUITS, AI_DELAY, SCOREBAR_H
Local variables camelCase bestMove, capturedCards, isScopa, afterTable
Private members camelCase this.state, this.cardImages, this.aiThinking
Parameters camelCase playerIdx, captureChoice, onComplete

Class & Scene Patterns

Scenes extend Phaser.Scene and follow the Phaser lifecycle:

export class BootScene extends Phaser.Scene {
  constructor() {
    super({ key: 'BootScene' });
  }
  preload(): void { /* asset loading */ }
  create(): void { /* scene setup */ }
}

Scene registration via Phaser.Types.Core.GameConfig.scene array in main.ts.

Indentation & Formatting

  • 2-space indentation
  • Single quotes for string literals
  • No semicolons omission — semicolons used consistently
  • Trailing commas in multi-line objects/arrays
  • const preferred; let when reassignment is needed; no var

Import Patterns

Named imports from local modules:

import { Card, GameState, PlayerIndex } from './types';
import { findCaptures, canCapture, calcPrimiera, teamOf } from './engine';
import { chooseMove } from '../game/ai';

Default import for Phaser:

import Phaser from 'phaser';

Type-only import for Capacitor config:

import type { CapacitorConfig } from '@capacitor/cli';

Relative paths only (./, ../). No path aliases configured.

Export Patterns

  • Named exports for all public symbols (export function, export class, export interface, export type, export const)
  • No default exports except vite.config.ts and capacitor.config.ts (framework convention)
  • Private/internal functions are not exported (getSubsets, calculateScores, scoreRound, deepClone)

Type Annotations

  • Explicit return types on exported functions: (): Card[], (): GameState, (): boolean
  • Union types for constrained values: PlayerIndex = 0 | 1 | 2 | 3
  • String literal unions: Suit = 'bastoni' | 'coppe' | 'denara' | 'spade'
  • Tuple types for fixed-length arrays: [Player, Player, Player, Player], [TeamScore, TeamScore]
  • Record<K, V> for maps: PRIMIERA_VALUES: Record<number, number>

Comments & Documentation

  • JSDoc comments on key exported functions (findCaptures, applyMove)
  • Section separators using dashed lines:
    // ---------------------------------------------------------------------------
    // Deck
    // ---------------------------------------------------------------------------
    
  • [trueref] annotations documenting Phaser API provenance
  • Inline comments for non-obvious logic (capture rules, AI heuristic weights)
  • No auto-generated docs or separate documentation tooling

Code Examples (from codebase)

Immutable state update pattern

export function applyMove(
  state: GameState,
  playerIdx: PlayerIndex,
  card: Card,
  captureChoice?: Card[]
): { nextState: GameState; capture: Capture | null; isScopa: boolean } {
  const state2 = deepClone(state);
  // ... mutations on state2 ...
  return { nextState: state2, capture: ..., isScopa };
}

AI heuristic weighted scoring

function scoreCapture(...): number {
  let score = 100;           // base for capturing anything
  if (isScopa) score += 500;
  if (settebello) score += 300;
  score += denariCount * 50;
  score += captured.length * 20;
  // ...
  return score;
}

Phaser particle effect

const e1 = this.add.particles(x, y, 'particle_glow', {
  lifespan: { min: 350, max: 700 },
  speed: { min: 80, max: 280 },
  scale: { start: 0.9, end: 0 },
  tint: color, gravityY: 100, emitting: false,
}).setDepth(25);
e1.explode(count);

Linting & Formatting

No ESLint or Prettier configuration detected. Code style is maintained manually. TypeScript strict mode provides type-level linting (strict: true in tsconfig.json).