brainrot-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/README.md +372 -0
  2. package/dist/AchievementNotification.d.ts +28 -0
  3. package/dist/AchievementNotification.d.ts.map +1 -0
  4. package/dist/AchievementNotification.js +74 -0
  5. package/dist/AchievementNotification.js.map +1 -0
  6. package/dist/GameSelector.d.ts +25 -0
  7. package/dist/GameSelector.d.ts.map +1 -0
  8. package/dist/GameSelector.js +105 -0
  9. package/dist/GameSelector.js.map +1 -0
  10. package/dist/HelpOverlay.d.ts +15 -0
  11. package/dist/HelpOverlay.d.ts.map +1 -0
  12. package/dist/HelpOverlay.js +134 -0
  13. package/dist/HelpOverlay.js.map +1 -0
  14. package/dist/Layout.d.ts +49 -0
  15. package/dist/Layout.d.ts.map +1 -0
  16. package/dist/Layout.js +83 -0
  17. package/dist/Layout.js.map +1 -0
  18. package/dist/Leaderboard.d.ts +46 -0
  19. package/dist/Leaderboard.d.ts.map +1 -0
  20. package/dist/Leaderboard.js +68 -0
  21. package/dist/Leaderboard.js.map +1 -0
  22. package/dist/LogViewer.d.ts +33 -0
  23. package/dist/LogViewer.d.ts.map +1 -0
  24. package/dist/LogViewer.js +179 -0
  25. package/dist/LogViewer.js.map +1 -0
  26. package/dist/LoopAlertOverlay.d.ts +15 -0
  27. package/dist/LoopAlertOverlay.d.ts.map +1 -0
  28. package/dist/LoopAlertOverlay.js +17 -0
  29. package/dist/LoopAlertOverlay.js.map +1 -0
  30. package/dist/LoopManagementPanel.d.ts +44 -0
  31. package/dist/LoopManagementPanel.d.ts.map +1 -0
  32. package/dist/LoopManagementPanel.js +220 -0
  33. package/dist/LoopManagementPanel.js.map +1 -0
  34. package/dist/SettingsMenu.d.ts +22 -0
  35. package/dist/SettingsMenu.d.ts.map +1 -0
  36. package/dist/SettingsMenu.js +367 -0
  37. package/dist/SettingsMenu.js.map +1 -0
  38. package/dist/SplitPane.d.ts +63 -0
  39. package/dist/SplitPane.d.ts.map +1 -0
  40. package/dist/SplitPane.js +104 -0
  41. package/dist/SplitPane.js.map +1 -0
  42. package/dist/StatsMenu.d.ts +15 -0
  43. package/dist/StatsMenu.d.ts.map +1 -0
  44. package/dist/StatsMenu.js +230 -0
  45. package/dist/StatsMenu.js.map +1 -0
  46. package/dist/StatusBar.d.ts +58 -0
  47. package/dist/StatusBar.d.ts.map +1 -0
  48. package/dist/StatusBar.js +106 -0
  49. package/dist/StatusBar.js.map +1 -0
  50. package/dist/__tests__/ralph-loop-parser.test.d.ts +2 -0
  51. package/dist/__tests__/ralph-loop-parser.test.d.ts.map +1 -0
  52. package/dist/__tests__/ralph-loop-parser.test.js +143 -0
  53. package/dist/__tests__/ralph-loop-parser.test.js.map +1 -0
  54. package/dist/claude-code-process.d.ts +76 -0
  55. package/dist/claude-code-process.d.ts.map +1 -0
  56. package/dist/claude-code-process.js +221 -0
  57. package/dist/claude-code-process.js.map +1 -0
  58. package/dist/cli.d.ts +42 -0
  59. package/dist/cli.d.ts.map +1 -0
  60. package/dist/cli.js +265 -0
  61. package/dist/cli.js.map +1 -0
  62. package/dist/config.d.ts +206 -0
  63. package/dist/config.d.ts.map +1 -0
  64. package/dist/config.js +270 -0
  65. package/dist/config.js.map +1 -0
  66. package/dist/game-types.d.ts +177 -0
  67. package/dist/game-types.d.ts.map +1 -0
  68. package/dist/game-types.js +55 -0
  69. package/dist/game-types.js.map +1 -0
  70. package/dist/games/MinesweeperGame.d.ts +15 -0
  71. package/dist/games/MinesweeperGame.d.ts.map +1 -0
  72. package/dist/games/MinesweeperGame.js +555 -0
  73. package/dist/games/MinesweeperGame.js.map +1 -0
  74. package/dist/games/PongGame.d.ts +15 -0
  75. package/dist/games/PongGame.d.ts.map +1 -0
  76. package/dist/games/PongGame.js +379 -0
  77. package/dist/games/PongGame.js.map +1 -0
  78. package/dist/games/SnakeGame.d.ts +15 -0
  79. package/dist/games/SnakeGame.d.ts.map +1 -0
  80. package/dist/games/SnakeGame.js +333 -0
  81. package/dist/games/SnakeGame.js.map +1 -0
  82. package/dist/games/TetrisGame.d.ts +15 -0
  83. package/dist/games/TetrisGame.d.ts.map +1 -0
  84. package/dist/games/TetrisGame.js +654 -0
  85. package/dist/games/TetrisGame.js.map +1 -0
  86. package/dist/games/index.d.ts +23 -0
  87. package/dist/games/index.d.ts.map +1 -0
  88. package/dist/games/index.js +47 -0
  89. package/dist/games/index.js.map +1 -0
  90. package/dist/high-scores.d.ts +57 -0
  91. package/dist/high-scores.d.ts.map +1 -0
  92. package/dist/high-scores.js +230 -0
  93. package/dist/high-scores.js.map +1 -0
  94. package/dist/index.d.ts +3 -0
  95. package/dist/index.d.ts.map +1 -0
  96. package/dist/index.js +264 -0
  97. package/dist/index.js.map +1 -0
  98. package/dist/ralph-loop-parser.d.ts +58 -0
  99. package/dist/ralph-loop-parser.d.ts.map +1 -0
  100. package/dist/ralph-loop-parser.js +315 -0
  101. package/dist/ralph-loop-parser.js.map +1 -0
  102. package/dist/stats.d.ts +142 -0
  103. package/dist/stats.d.ts.map +1 -0
  104. package/dist/stats.js +521 -0
  105. package/dist/stats.js.map +1 -0
  106. package/dist/styled-components.d.ts +231 -0
  107. package/dist/styled-components.d.ts.map +1 -0
  108. package/dist/styled-components.js +192 -0
  109. package/dist/styled-components.js.map +1 -0
  110. package/dist/theme.d.ts +301 -0
  111. package/dist/theme.d.ts.map +1 -0
  112. package/dist/theme.js +372 -0
  113. package/dist/theme.js.map +1 -0
  114. package/dist/themes.d.ts +117 -0
  115. package/dist/themes.d.ts.map +1 -0
  116. package/dist/themes.js +296 -0
  117. package/dist/themes.js.map +1 -0
  118. package/dist/ui/index.d.ts +13 -0
  119. package/dist/ui/index.d.ts.map +1 -0
  120. package/dist/ui/index.js +29 -0
  121. package/dist/ui/index.js.map +1 -0
  122. package/dist/use-claude-code.d.ts +30 -0
  123. package/dist/use-claude-code.d.ts.map +1 -0
  124. package/dist/use-claude-code.js +84 -0
  125. package/dist/use-claude-code.js.map +1 -0
  126. package/dist/use-config.d.ts +58 -0
  127. package/dist/use-config.d.ts.map +1 -0
  128. package/dist/use-config.js +113 -0
  129. package/dist/use-config.js.map +1 -0
  130. package/dist/use-game-loop.d.ts +47 -0
  131. package/dist/use-game-loop.d.ts.map +1 -0
  132. package/dist/use-game-loop.js +136 -0
  133. package/dist/use-game-loop.js.map +1 -0
  134. package/dist/use-high-scores.d.ts +41 -0
  135. package/dist/use-high-scores.d.ts.map +1 -0
  136. package/dist/use-high-scores.js +94 -0
  137. package/dist/use-high-scores.js.map +1 -0
  138. package/dist/use-layout-state.d.ts +77 -0
  139. package/dist/use-layout-state.d.ts.map +1 -0
  140. package/dist/use-layout-state.js +160 -0
  141. package/dist/use-layout-state.js.map +1 -0
  142. package/dist/use-ralph-loop.d.ts +41 -0
  143. package/dist/use-ralph-loop.d.ts.map +1 -0
  144. package/dist/use-ralph-loop.js +106 -0
  145. package/dist/use-ralph-loop.js.map +1 -0
  146. package/dist/use-spinner.d.ts +46 -0
  147. package/dist/use-spinner.d.ts.map +1 -0
  148. package/dist/use-spinner.js +71 -0
  149. package/dist/use-spinner.js.map +1 -0
  150. package/dist/use-stats.d.ts +59 -0
  151. package/dist/use-stats.d.ts.map +1 -0
  152. package/dist/use-stats.js +150 -0
  153. package/dist/use-stats.js.map +1 -0
  154. package/dist/use-terminal-size.d.ts +29 -0
  155. package/dist/use-terminal-size.d.ts.map +1 -0
  156. package/dist/use-terminal-size.js +48 -0
  157. package/dist/use-terminal-size.js.map +1 -0
  158. package/dist/useTheme.d.ts +76 -0
  159. package/dist/useTheme.d.ts.map +1 -0
  160. package/dist/useTheme.js +136 -0
  161. package/dist/useTheme.js.map +1 -0
  162. package/package.json +58 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ralph-loop-parser.js","sourceRoot":"","sources":["../src/ralph-loop-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Stats and Achievement System
3
+ *
4
+ * Tracks gameplay statistics and provides an achievement system with
5
+ * unlockable milestones. Data persists using XDG Base Directory conventions.
6
+ */
7
+ /** Stats for a single game */
8
+ export interface GameStats {
9
+ /** Total games played */
10
+ gamesPlayed: number;
11
+ /** Total time played in seconds */
12
+ timePlayed: number;
13
+ /** Highest score achieved */
14
+ highestScore: number;
15
+ /** Total score accumulated across all games */
16
+ totalScore: number;
17
+ /** Number of wins (for games with win conditions) */
18
+ wins: number;
19
+ /** Number of losses */
20
+ losses: number;
21
+ /** Last played timestamp */
22
+ lastPlayed: string | null;
23
+ /** Game-specific stats */
24
+ custom?: Record<string, number>;
25
+ }
26
+ /** Global stats across all games */
27
+ export interface GlobalStats {
28
+ /** Total games played across all games */
29
+ totalGamesPlayed: number;
30
+ /** Total time played in seconds */
31
+ totalTimePlayed: number;
32
+ /** Total achievements unlocked */
33
+ achievementsUnlocked: number;
34
+ /** First played timestamp */
35
+ firstPlayed: string | null;
36
+ /** Last played timestamp */
37
+ lastPlayed: string | null;
38
+ /** Session count (app opens) */
39
+ sessionCount: number;
40
+ /** Current streak (consecutive days played) */
41
+ currentStreak: number;
42
+ /** Best streak */
43
+ bestStreak: number;
44
+ /** Last streak date */
45
+ lastStreakDate: string | null;
46
+ }
47
+ /** Achievement definition */
48
+ export interface Achievement {
49
+ /** Unique identifier */
50
+ id: string;
51
+ /** Display name */
52
+ name: string;
53
+ /** Description of how to unlock */
54
+ description: string;
55
+ /** Category of achievement */
56
+ category: "general" | "snake" | "pong" | "tetris" | "minesweeper";
57
+ /** Icon to display */
58
+ icon: string;
59
+ /** Whether this achievement is hidden until unlocked */
60
+ hidden?: boolean;
61
+ /** Condition check function - receives stats and returns true if unlocked */
62
+ condition: (stats: StatsData) => boolean;
63
+ }
64
+ /** Unlocked achievement record */
65
+ export interface UnlockedAchievement {
66
+ /** Achievement ID */
67
+ id: string;
68
+ /** Timestamp when unlocked */
69
+ unlockedAt: string;
70
+ }
71
+ /** Complete stats data structure */
72
+ export interface StatsData {
73
+ /** Data format version */
74
+ version: number;
75
+ /** Global stats */
76
+ global: GlobalStats;
77
+ /** Per-game stats */
78
+ games: Record<string, GameStats>;
79
+ /** Unlocked achievements */
80
+ achievements: UnlockedAchievement[];
81
+ }
82
+ export declare const ACHIEVEMENTS: Achievement[];
83
+ /**
84
+ * Load stats from disk
85
+ */
86
+ export declare function loadStats(): Promise<StatsData>;
87
+ /**
88
+ * Save stats to disk
89
+ */
90
+ export declare function saveStats(data: StatsData): Promise<void>;
91
+ /**
92
+ * Get stats for a specific game
93
+ */
94
+ export declare function getGameStats(gameId: string): Promise<GameStats>;
95
+ /**
96
+ * Get global stats
97
+ */
98
+ export declare function getGlobalStats(): Promise<GlobalStats>;
99
+ /**
100
+ * Record a game session
101
+ */
102
+ export declare function recordGameSession(gameId: string, score: number, duration: number, won?: boolean, customStats?: Record<string, number>): Promise<string[]>;
103
+ /**
104
+ * Record a session start (increment session count)
105
+ */
106
+ export declare function recordSessionStart(): Promise<void>;
107
+ /**
108
+ * Check for newly unlocked achievements
109
+ * Returns array of newly unlocked achievement IDs
110
+ */
111
+ export declare function checkAchievements(data: StatsData): string[];
112
+ /**
113
+ * Get all achievements with unlock status
114
+ */
115
+ export declare function getAchievements(): Promise<Array<Achievement & {
116
+ unlocked: boolean;
117
+ unlockedAt: string | null;
118
+ }>>;
119
+ /**
120
+ * Get achievement by ID
121
+ */
122
+ export declare function getAchievementById(id: string): Achievement | undefined;
123
+ /**
124
+ * Get achievement count
125
+ */
126
+ export declare function getAchievementCount(): Promise<{
127
+ unlocked: number;
128
+ total: number;
129
+ }>;
130
+ /**
131
+ * Get all stats data
132
+ */
133
+ export declare function getAllStats(): Promise<StatsData>;
134
+ /**
135
+ * Format duration in seconds to human-readable string
136
+ */
137
+ export declare function formatDuration(seconds: number): string;
138
+ /**
139
+ * Format a date string for display
140
+ */
141
+ export declare function formatStatsDate(timestamp: string | null): string;
142
+ //# sourceMappingURL=stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../src/stats.ts"],"names":[],"mappings":"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"}
package/dist/stats.js ADDED
@@ -0,0 +1,521 @@
1
+ /**
2
+ * Stats and Achievement System
3
+ *
4
+ * Tracks gameplay statistics and provides an achievement system with
5
+ * unlockable milestones. Data persists using XDG Base Directory conventions.
6
+ */
7
+ import { readFile, writeFile } from "node:fs/promises";
8
+ import { existsSync } from "node:fs";
9
+ import { getDataFilePath, ensureDataDir } from "./config.js";
10
+ // ============================================================================
11
+ // CONSTANTS
12
+ // ============================================================================
13
+ const DATA_VERSION = 1;
14
+ const STATS_FILE = "stats.json";
15
+ /** Default empty game stats */
16
+ const DEFAULT_GAME_STATS = {
17
+ gamesPlayed: 0,
18
+ timePlayed: 0,
19
+ highestScore: 0,
20
+ totalScore: 0,
21
+ wins: 0,
22
+ losses: 0,
23
+ lastPlayed: null,
24
+ };
25
+ /** Default global stats */
26
+ const DEFAULT_GLOBAL_STATS = {
27
+ totalGamesPlayed: 0,
28
+ totalTimePlayed: 0,
29
+ achievementsUnlocked: 0,
30
+ firstPlayed: null,
31
+ lastPlayed: null,
32
+ sessionCount: 0,
33
+ currentStreak: 0,
34
+ bestStreak: 0,
35
+ lastStreakDate: null,
36
+ };
37
+ // ============================================================================
38
+ // ACHIEVEMENT DEFINITIONS
39
+ // ============================================================================
40
+ export const ACHIEVEMENTS = [
41
+ // General achievements
42
+ {
43
+ id: "first_game",
44
+ name: "First Steps",
45
+ description: "Play your first game",
46
+ category: "general",
47
+ icon: "🎮",
48
+ condition: (stats) => stats.global.totalGamesPlayed >= 1,
49
+ },
50
+ {
51
+ id: "ten_games",
52
+ name: "Getting Started",
53
+ description: "Play 10 games",
54
+ category: "general",
55
+ icon: "🎯",
56
+ condition: (stats) => stats.global.totalGamesPlayed >= 10,
57
+ },
58
+ {
59
+ id: "fifty_games",
60
+ name: "Dedicated Player",
61
+ description: "Play 50 games",
62
+ category: "general",
63
+ icon: "⭐",
64
+ condition: (stats) => stats.global.totalGamesPlayed >= 50,
65
+ },
66
+ {
67
+ id: "hundred_games",
68
+ name: "Game Master",
69
+ description: "Play 100 games",
70
+ category: "general",
71
+ icon: "🏆",
72
+ condition: (stats) => stats.global.totalGamesPlayed >= 100,
73
+ },
74
+ {
75
+ id: "hour_played",
76
+ name: "Time Flies",
77
+ description: "Play for a total of 1 hour",
78
+ category: "general",
79
+ icon: "⏰",
80
+ condition: (stats) => stats.global.totalTimePlayed >= 3600,
81
+ },
82
+ {
83
+ id: "five_hours",
84
+ name: "Marathon Runner",
85
+ description: "Play for a total of 5 hours",
86
+ category: "general",
87
+ icon: "🏃",
88
+ condition: (stats) => stats.global.totalTimePlayed >= 18000,
89
+ },
90
+ {
91
+ id: "three_day_streak",
92
+ name: "Consistent",
93
+ description: "Play 3 days in a row",
94
+ category: "general",
95
+ icon: "🔥",
96
+ condition: (stats) => stats.global.bestStreak >= 3,
97
+ },
98
+ {
99
+ id: "week_streak",
100
+ name: "Dedicated",
101
+ description: "Play 7 days in a row",
102
+ category: "general",
103
+ icon: "📅",
104
+ condition: (stats) => stats.global.bestStreak >= 7,
105
+ },
106
+ {
107
+ id: "all_games",
108
+ name: "Variety Pack",
109
+ description: "Play all 4 games at least once",
110
+ category: "general",
111
+ icon: "🎲",
112
+ condition: (stats) => {
113
+ const games = ["snake", "pong", "tetris", "minesweeper"];
114
+ return games.every((g) => (stats.games[g]?.gamesPlayed ?? 0) >= 1);
115
+ },
116
+ },
117
+ // Snake achievements
118
+ {
119
+ id: "snake_first",
120
+ name: "Slithering Start",
121
+ description: "Play your first Snake game",
122
+ category: "snake",
123
+ icon: "🐍",
124
+ condition: (stats) => (stats.games.snake?.gamesPlayed ?? 0) >= 1,
125
+ },
126
+ {
127
+ id: "snake_50_score",
128
+ name: "Growing Snake",
129
+ description: "Score 50 points in Snake",
130
+ category: "snake",
131
+ icon: "📈",
132
+ condition: (stats) => (stats.games.snake?.highestScore ?? 0) >= 50,
133
+ },
134
+ {
135
+ id: "snake_100_score",
136
+ name: "Long Snake",
137
+ description: "Score 100 points in Snake",
138
+ category: "snake",
139
+ icon: "🏅",
140
+ condition: (stats) => (stats.games.snake?.highestScore ?? 0) >= 100,
141
+ },
142
+ {
143
+ id: "snake_200_score",
144
+ name: "Snake Charmer",
145
+ description: "Score 200 points in Snake",
146
+ category: "snake",
147
+ icon: "👑",
148
+ condition: (stats) => (stats.games.snake?.highestScore ?? 0) >= 200,
149
+ },
150
+ {
151
+ id: "snake_10_games",
152
+ name: "Snake Enthusiast",
153
+ description: "Play Snake 10 times",
154
+ category: "snake",
155
+ icon: "🎮",
156
+ condition: (stats) => (stats.games.snake?.gamesPlayed ?? 0) >= 10,
157
+ },
158
+ // Pong achievements
159
+ {
160
+ id: "pong_first",
161
+ name: "Paddle Ready",
162
+ description: "Play your first Pong game",
163
+ category: "pong",
164
+ icon: "🏓",
165
+ condition: (stats) => (stats.games.pong?.gamesPlayed ?? 0) >= 1,
166
+ },
167
+ {
168
+ id: "pong_first_win",
169
+ name: "First Victory",
170
+ description: "Win your first Pong game",
171
+ category: "pong",
172
+ icon: "✌️",
173
+ condition: (stats) => (stats.games.pong?.wins ?? 0) >= 1,
174
+ },
175
+ {
176
+ id: "pong_10_wins",
177
+ name: "Pong Champion",
178
+ description: "Win 10 Pong games",
179
+ category: "pong",
180
+ icon: "🥇",
181
+ condition: (stats) => (stats.games.pong?.wins ?? 0) >= 10,
182
+ },
183
+ {
184
+ id: "pong_50_wins",
185
+ name: "Pong Master",
186
+ description: "Win 50 Pong games",
187
+ category: "pong",
188
+ icon: "🏆",
189
+ condition: (stats) => (stats.games.pong?.wins ?? 0) >= 50,
190
+ },
191
+ // Tetris achievements
192
+ {
193
+ id: "tetris_first",
194
+ name: "Block Party",
195
+ description: "Play your first Tetris game",
196
+ category: "tetris",
197
+ icon: "🧱",
198
+ condition: (stats) => (stats.games.tetris?.gamesPlayed ?? 0) >= 1,
199
+ },
200
+ {
201
+ id: "tetris_1000_score",
202
+ name: "Stacking Up",
203
+ description: "Score 1000 points in Tetris",
204
+ category: "tetris",
205
+ icon: "📊",
206
+ condition: (stats) => (stats.games.tetris?.highestScore ?? 0) >= 1000,
207
+ },
208
+ {
209
+ id: "tetris_5000_score",
210
+ name: "Tetris Pro",
211
+ description: "Score 5000 points in Tetris",
212
+ category: "tetris",
213
+ icon: "🌟",
214
+ condition: (stats) => (stats.games.tetris?.highestScore ?? 0) >= 5000,
215
+ },
216
+ {
217
+ id: "tetris_10000_score",
218
+ name: "Tetris Legend",
219
+ description: "Score 10000 points in Tetris",
220
+ category: "tetris",
221
+ icon: "💎",
222
+ condition: (stats) => (stats.games.tetris?.highestScore ?? 0) >= 10000,
223
+ },
224
+ {
225
+ id: "tetris_lines_cleared",
226
+ name: "Line Clearer",
227
+ description: "Clear 100 lines in Tetris",
228
+ category: "tetris",
229
+ icon: "📏",
230
+ condition: (stats) => (stats.games.tetris?.custom?.linesCleared ?? 0) >= 100,
231
+ },
232
+ // Minesweeper achievements
233
+ {
234
+ id: "minesweeper_first",
235
+ name: "Mine Explorer",
236
+ description: "Play your first Minesweeper game",
237
+ category: "minesweeper",
238
+ icon: "💣",
239
+ condition: (stats) => (stats.games.minesweeper?.gamesPlayed ?? 0) >= 1,
240
+ },
241
+ {
242
+ id: "minesweeper_first_win",
243
+ name: "Bomb Defuser",
244
+ description: "Win your first Minesweeper game",
245
+ category: "minesweeper",
246
+ icon: "🛡️",
247
+ condition: (stats) => (stats.games.minesweeper?.wins ?? 0) >= 1,
248
+ },
249
+ {
250
+ id: "minesweeper_10_wins",
251
+ name: "Mine Sweeper",
252
+ description: "Win 10 Minesweeper games",
253
+ category: "minesweeper",
254
+ icon: "🎖️",
255
+ condition: (stats) => (stats.games.minesweeper?.wins ?? 0) >= 10,
256
+ },
257
+ {
258
+ id: "minesweeper_fast",
259
+ name: "Speed Demon",
260
+ description: "Complete Minesweeper in under 60 seconds",
261
+ category: "minesweeper",
262
+ icon: "⚡",
263
+ condition: (stats) => (stats.games.minesweeper?.custom?.fastestWin ?? 999) < 60,
264
+ },
265
+ ];
266
+ // ============================================================================
267
+ // FILE OPERATIONS
268
+ // ============================================================================
269
+ function getStatsFile() {
270
+ return getDataFilePath(STATS_FILE);
271
+ }
272
+ function createEmptyStats() {
273
+ return {
274
+ version: DATA_VERSION,
275
+ global: { ...DEFAULT_GLOBAL_STATS },
276
+ games: {},
277
+ achievements: [],
278
+ };
279
+ }
280
+ /**
281
+ * Load stats from disk
282
+ */
283
+ export async function loadStats() {
284
+ try {
285
+ const statsFile = getStatsFile();
286
+ if (!existsSync(statsFile)) {
287
+ return createEmptyStats();
288
+ }
289
+ const content = await readFile(statsFile, "utf-8");
290
+ const data = JSON.parse(content);
291
+ if (data.version !== DATA_VERSION) {
292
+ return createEmptyStats();
293
+ }
294
+ return data;
295
+ }
296
+ catch {
297
+ return createEmptyStats();
298
+ }
299
+ }
300
+ /**
301
+ * Save stats to disk
302
+ */
303
+ export async function saveStats(data) {
304
+ try {
305
+ await ensureDataDir();
306
+ const statsFile = getStatsFile();
307
+ await writeFile(statsFile, JSON.stringify(data, null, 2), "utf-8");
308
+ }
309
+ catch {
310
+ // Silently fail - stats are nice to have but not critical
311
+ }
312
+ }
313
+ // ============================================================================
314
+ // STATS OPERATIONS
315
+ // ============================================================================
316
+ /**
317
+ * Get stats for a specific game
318
+ */
319
+ export async function getGameStats(gameId) {
320
+ const data = await loadStats();
321
+ return data.games[gameId] ?? { ...DEFAULT_GAME_STATS };
322
+ }
323
+ /**
324
+ * Get global stats
325
+ */
326
+ export async function getGlobalStats() {
327
+ const data = await loadStats();
328
+ return data.global;
329
+ }
330
+ /**
331
+ * Update streak based on today's date
332
+ */
333
+ function updateStreak(stats) {
334
+ const today = new Date().toISOString().split("T")[0];
335
+ const lastDate = stats.global.lastStreakDate;
336
+ if (!lastDate) {
337
+ // First time playing
338
+ stats.global.currentStreak = 1;
339
+ stats.global.bestStreak = 1;
340
+ stats.global.lastStreakDate = today;
341
+ }
342
+ else if (lastDate === today) {
343
+ // Already played today, no change
344
+ }
345
+ else {
346
+ // Check if yesterday
347
+ const yesterday = new Date();
348
+ yesterday.setDate(yesterday.getDate() - 1);
349
+ const yesterdayStr = yesterday.toISOString().split("T")[0];
350
+ if (lastDate === yesterdayStr) {
351
+ // Consecutive day
352
+ stats.global.currentStreak++;
353
+ if (stats.global.currentStreak > stats.global.bestStreak) {
354
+ stats.global.bestStreak = stats.global.currentStreak;
355
+ }
356
+ }
357
+ else {
358
+ // Streak broken
359
+ stats.global.currentStreak = 1;
360
+ }
361
+ stats.global.lastStreakDate = today;
362
+ }
363
+ }
364
+ /**
365
+ * Record a game session
366
+ */
367
+ export async function recordGameSession(gameId, score, duration, won, customStats) {
368
+ const data = await loadStats();
369
+ const now = new Date().toISOString();
370
+ // Initialize game stats if needed
371
+ if (!data.games[gameId]) {
372
+ data.games[gameId] = { ...DEFAULT_GAME_STATS };
373
+ }
374
+ const gameStats = data.games[gameId];
375
+ // Update game stats
376
+ gameStats.gamesPlayed++;
377
+ gameStats.timePlayed += duration;
378
+ gameStats.totalScore += score;
379
+ if (score > gameStats.highestScore) {
380
+ gameStats.highestScore = score;
381
+ }
382
+ if (won !== undefined) {
383
+ if (won) {
384
+ gameStats.wins++;
385
+ }
386
+ else {
387
+ gameStats.losses++;
388
+ }
389
+ }
390
+ gameStats.lastPlayed = now;
391
+ // Merge custom stats
392
+ if (customStats) {
393
+ if (!gameStats.custom) {
394
+ gameStats.custom = {};
395
+ }
396
+ for (const [key, value] of Object.entries(customStats)) {
397
+ gameStats.custom[key] = (gameStats.custom[key] ?? 0) + value;
398
+ }
399
+ }
400
+ // Update global stats
401
+ data.global.totalGamesPlayed++;
402
+ data.global.totalTimePlayed += duration;
403
+ if (!data.global.firstPlayed) {
404
+ data.global.firstPlayed = now;
405
+ }
406
+ data.global.lastPlayed = now;
407
+ // Update streak
408
+ updateStreak(data);
409
+ // Check for new achievements
410
+ const newAchievements = checkAchievements(data);
411
+ // Save
412
+ await saveStats(data);
413
+ return newAchievements;
414
+ }
415
+ /**
416
+ * Record a session start (increment session count)
417
+ */
418
+ export async function recordSessionStart() {
419
+ const data = await loadStats();
420
+ data.global.sessionCount++;
421
+ await saveStats(data);
422
+ }
423
+ // ============================================================================
424
+ // ACHIEVEMENT OPERATIONS
425
+ // ============================================================================
426
+ /**
427
+ * Check for newly unlocked achievements
428
+ * Returns array of newly unlocked achievement IDs
429
+ */
430
+ export function checkAchievements(data) {
431
+ const newUnlocks = [];
432
+ const unlockedIds = new Set(data.achievements.map((a) => a.id));
433
+ for (const achievement of ACHIEVEMENTS) {
434
+ if (!unlockedIds.has(achievement.id) && achievement.condition(data)) {
435
+ newUnlocks.push(achievement.id);
436
+ data.achievements.push({
437
+ id: achievement.id,
438
+ unlockedAt: new Date().toISOString(),
439
+ });
440
+ }
441
+ }
442
+ // Update achievement count
443
+ data.global.achievementsUnlocked = data.achievements.length;
444
+ return newUnlocks;
445
+ }
446
+ /**
447
+ * Get all achievements with unlock status
448
+ */
449
+ export async function getAchievements() {
450
+ const data = await loadStats();
451
+ const unlockedMap = new Map(data.achievements.map((a) => [a.id, a.unlockedAt]));
452
+ return ACHIEVEMENTS.map((achievement) => ({
453
+ ...achievement,
454
+ unlocked: unlockedMap.has(achievement.id),
455
+ unlockedAt: unlockedMap.get(achievement.id) ?? null,
456
+ }));
457
+ }
458
+ /**
459
+ * Get achievement by ID
460
+ */
461
+ export function getAchievementById(id) {
462
+ return ACHIEVEMENTS.find((a) => a.id === id);
463
+ }
464
+ /**
465
+ * Get achievement count
466
+ */
467
+ export async function getAchievementCount() {
468
+ const data = await loadStats();
469
+ return {
470
+ unlocked: data.achievements.length,
471
+ total: ACHIEVEMENTS.length,
472
+ };
473
+ }
474
+ /**
475
+ * Get all stats data
476
+ */
477
+ export async function getAllStats() {
478
+ return loadStats();
479
+ }
480
+ // ============================================================================
481
+ // FORMATTING HELPERS
482
+ // ============================================================================
483
+ /**
484
+ * Format duration in seconds to human-readable string
485
+ */
486
+ export function formatDuration(seconds) {
487
+ if (seconds < 60) {
488
+ return `${Math.floor(seconds)}s`;
489
+ }
490
+ if (seconds < 3600) {
491
+ const mins = Math.floor(seconds / 60);
492
+ const secs = Math.floor(seconds % 60);
493
+ return secs > 0 ? `${mins}m ${secs}s` : `${mins}m`;
494
+ }
495
+ const hours = Math.floor(seconds / 3600);
496
+ const mins = Math.floor((seconds % 3600) / 60);
497
+ return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
498
+ }
499
+ /**
500
+ * Format a date string for display
501
+ */
502
+ export function formatStatsDate(timestamp) {
503
+ if (!timestamp)
504
+ return "Never";
505
+ const date = new Date(timestamp);
506
+ const now = new Date();
507
+ if (date.toDateString() === now.toDateString()) {
508
+ return "Today";
509
+ }
510
+ const yesterday = new Date();
511
+ yesterday.setDate(yesterday.getDate() - 1);
512
+ if (date.toDateString() === yesterday.toDateString()) {
513
+ return "Yesterday";
514
+ }
515
+ return date.toLocaleDateString([], {
516
+ month: "short",
517
+ day: "numeric",
518
+ year: date.getFullYear() !== now.getFullYear() ? "numeric" : undefined,
519
+ });
520
+ }
521
+ //# sourceMappingURL=stats.js.map