All files / web/src/arcade-games/music-matching variant-server.ts

63.44% Statements 59/93
100% Branches 1/1
14.28% Functions 1/7
63.44% Lines 59/93

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 941x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                                                   1x 1x 1x 1x           1x 1x         1x 1x 1x 1x 1x 1x 1x 1x 1x 1x  
/**
 * Music Matching Game — Server-Safe Variant Definition
 *
 * Contains only the data/logic fields needed by the validator.
 * No React component imports — safe to use in server.js / socket-server.
 */
 
import type { MatchingPairsVariant } from '@/lib/arcade/matching-pairs-framework'
import { generateMusicCards, getGridConfiguration } from './utils/cardGeneration'
import { validateMatch } from './utils/matchValidation'
import { MusicCardSchema } from './types'
import type { MusicCard, MusicConfig } from './types'
 
export const musicVariantServer: Pick<
  MatchingPairsVariant<MusicCard, MusicConfig>,
  | 'gameName'
  | 'defaultConfig'
  | 'cardSchema'
  | 'generateCards'
  | 'validateMatch'
  | 'validateConfigField'
  | 'getTotalPairs'
  | 'getOriginalConfig'
  | 'hasConfigChangedFrom'
  | 'practiceBreakDefaults'
  | 'getGridConfig'
> = {
  gameName: 'music-matching',
 
  defaultConfig: {
    gameType: 'staff-to-name',
    clef: 'treble',
    difficulty: 6,
    turnTimer: 30,
  },
 
  cardSchema: MusicCardSchema,
 
  generateCards: (config) => generateMusicCards(config),
 
  validateMatch: (card1, card2) => validateMatch(card1, card2),
 
  validateConfigField: (field, value) => {
    switch (field) {
      case 'gameType':
        if (!['staff-to-name', 'treble-to-bass'].includes(value)) {
          return 'Invalid game type'
        }
        return null
      case 'clef':
        if (!['treble', 'bass', 'both'].includes(value)) {
          return 'Invalid clef'
        }
        return null
      case 'difficulty':
        if (![6, 8, 12, 15].includes(value)) {
          return 'Invalid difficulty'
        }
        return null
      case 'turnTimer':
        if (typeof value !== 'number' || value < 5 || value > 300) {
          return 'Turn timer must be between 5 and 300'
        }
        return null
      default:
        return null
    }
  },
 
  getTotalPairs: (config) => config.difficulty,
 
  getOriginalConfig: (config) => ({
    gameType: config.gameType,
    clef: config.clef,
    difficulty: config.difficulty,
    turnTimer: config.turnTimer,
  }),
 
  hasConfigChangedFrom: (current, original) =>
    current.gameType !== original.gameType ||
    current.clef !== original.clef ||
    current.difficulty !== original.difficulty ||
    current.turnTimer !== original.turnTimer,
 
  getGridConfig: (config) => getGridConfiguration(config.difficulty),
 
  practiceBreakDefaults: {
    gameType: 'staff-to-name',
    clef: 'treble',
    difficulty: 6,
    turnTimer: 30,
  },
}