react-native-games 1.0.0 → 1.2.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 (212) hide show
  1. package/README.md +80 -368
  2. package/lib/module/games/balloon-blaster/BalloonBlaster.js +1 -167
  3. package/lib/module/games/balloon-blaster/BalloonBlaster.js.map +1 -1
  4. package/lib/module/games/balloon-blaster/BalloonBlasterConstants.js +1 -182
  5. package/lib/module/games/balloon-blaster/BalloonBlasterConstants.js.map +1 -1
  6. package/lib/module/games/balloon-blaster/BalloonBlasterService.js +1 -318
  7. package/lib/module/games/balloon-blaster/BalloonBlasterStore.js +1 -183
  8. package/lib/module/games/balloon-blaster/components/BalloonComponent.js +1 -237
  9. package/lib/module/games/balloon-blaster/components/GameArea.js +1 -156
  10. package/lib/module/games/balloon-blaster/components/GameBackground.js +1 -476
  11. package/lib/module/games/balloon-blaster/components/ScoreBoard.js +1 -112
  12. package/lib/module/games/balloon-blaster/components/ScoreBoard.js.map +1 -1
  13. package/lib/module/games/balloon-blaster/components/index.js +1 -7
  14. package/lib/module/games/candy-crush/CandyCrush.js +1 -131
  15. package/lib/module/games/candy-crush/CandyCrush.js.map +1 -1
  16. package/lib/module/games/candy-crush/CandyCrushConstants.js +1 -125
  17. package/lib/module/games/candy-crush/CandyCrushConstants.js.map +1 -1
  18. package/lib/module/games/candy-crush/CandyCrushService.js +1 -370
  19. package/lib/module/games/candy-crush/CandyCrushStore.js +1 -303
  20. package/lib/module/games/candy-crush/components/CandyItem.js +1 -191
  21. package/lib/module/games/candy-crush/components/GameBackground.js +1 -85
  22. package/lib/module/games/candy-crush/components/GameGrid.js +1 -314
  23. package/lib/module/games/candy-crush/components/ScoreBoard.js +1 -79
  24. package/lib/module/games/candy-crush/components/index.js +1 -7
  25. package/lib/module/games/candy-crush/index.js +1 -6
  26. package/lib/module/games/colors-sort/ColorsSort.js +1 -143
  27. package/lib/module/games/colors-sort/ColorsSort.js.map +1 -1
  28. package/lib/module/games/colors-sort/ColorsSortConstants.js +1 -72
  29. package/lib/module/games/colors-sort/ColorsSortConstants.js.map +1 -1
  30. package/lib/module/games/colors-sort/ColorsSortService.js +1 -255
  31. package/lib/module/games/colors-sort/ColorsSortStore.js +1 -257
  32. package/lib/module/games/colors-sort/ColorsSortStore.js.map +1 -1
  33. package/lib/module/games/colors-sort/components/ColorContainer.js +1 -140
  34. package/lib/module/games/colors-sort/components/GameBackground.js +1 -135
  35. package/lib/module/games/colors-sort/components/ScoreBoard.js +1 -70
  36. package/lib/module/games/colors-sort/components/index.js +1 -6
  37. package/lib/module/games/dino-jump/DinoJump.js +1 -209
  38. package/lib/module/games/dino-jump/DinoJump.js.map +1 -1
  39. package/lib/module/games/dino-jump/DinoJumpConstants.js +1 -189
  40. package/lib/module/games/dino-jump/DinoJumpConstants.js.map +1 -1
  41. package/lib/module/games/dino-jump/DinoJumpService.js +1 -270
  42. package/lib/module/games/dino-jump/DinoJumpStore.js +1 -381
  43. package/lib/module/games/dino-jump/components/DinoSprite.js +1 -418
  44. package/lib/module/games/dino-jump/components/DinoSprite.js.map +1 -1
  45. package/lib/module/games/dino-jump/components/GameArea.js +1 -68
  46. package/lib/module/games/dino-jump/components/GameBackground.js +1 -444
  47. package/lib/module/games/dino-jump/components/ObstacleSprite.js +1 -306
  48. package/lib/module/games/dino-jump/components/ScoreBoard.js +1 -105
  49. package/lib/module/games/dino-jump/components/ScoreBoard.js.map +1 -1
  50. package/lib/module/games/dino-jump/components/StarSprite.js +1 -45
  51. package/lib/module/games/dino-jump/components/index.js +1 -9
  52. package/lib/module/games/flappy-bird/FlappyBird.js +1 -126
  53. package/lib/module/games/flappy-bird/FlappyBird.js.map +1 -1
  54. package/lib/module/games/flappy-bird/FlappyBirdConstants.js +1 -90
  55. package/lib/module/games/flappy-bird/FlappyBirdConstants.js.map +1 -1
  56. package/lib/module/games/flappy-bird/FlappyBirdStore.js +1 -300
  57. package/lib/module/games/flappy-bird/components/Bird.js +1 -87
  58. package/lib/module/games/flappy-bird/components/GameArea.js +1 -87
  59. package/lib/module/games/flappy-bird/components/GameBackground.js +1 -79
  60. package/lib/module/games/flappy-bird/components/Pipes.js +1 -172
  61. package/lib/module/games/flappy-bird/components/ScoreBoard.js +1 -73
  62. package/lib/module/games/flappy-bird/components/index.js +1 -8
  63. package/lib/module/games/fruit-merger/FruitMerger.js +1 -120
  64. package/lib/module/games/fruit-merger/FruitMerger.js.map +1 -1
  65. package/lib/module/games/fruit-merger/FruitMergerConstants.js +1 -119
  66. package/lib/module/games/fruit-merger/FruitMergerConstants.js.map +1 -1
  67. package/lib/module/games/fruit-merger/FruitMergerService.js +1 -13
  68. package/lib/module/games/fruit-merger/FruitMergerStore.js +1 -315
  69. package/lib/module/games/fruit-merger/components/FruitItem.js +1 -102
  70. package/lib/module/games/fruit-merger/components/GameArea.js +1 -103
  71. package/lib/module/games/fruit-merger/components/GameBackground.js +1 -498
  72. package/lib/module/games/fruit-merger/components/ScoreBoard.js +1 -58
  73. package/lib/module/games/fruit-merger/components/index.js +1 -7
  74. package/lib/module/games/fruit-ninja/FruitNinja.js +1 -134
  75. package/lib/module/games/fruit-ninja/FruitNinja.js.map +1 -1
  76. package/lib/module/games/fruit-ninja/FruitNinjaConstants.js +1 -148
  77. package/lib/module/games/fruit-ninja/FruitNinjaConstants.js.map +1 -1
  78. package/lib/module/games/fruit-ninja/FruitNinjaService.js +1 -311
  79. package/lib/module/games/fruit-ninja/FruitNinjaStore.js +1 -191
  80. package/lib/module/games/fruit-ninja/FruitNinjaStore.js.map +1 -1
  81. package/lib/module/games/fruit-ninja/components/FruitComponent.js +1 -99
  82. package/lib/module/games/fruit-ninja/components/GameArea.js +1 -215
  83. package/lib/module/games/fruit-ninja/components/GameBackground.js +1 -1267
  84. package/lib/module/games/fruit-ninja/components/ScoreBoard.js +1 -92
  85. package/lib/module/games/fruit-ninja/components/ScoreBoard.js.map +1 -1
  86. package/lib/module/games/fruit-ninja/components/index.js +1 -7
  87. package/lib/module/games/game-2048/Game2048.js +1 -149
  88. package/lib/module/games/game-2048/Game2048.js.map +1 -1
  89. package/lib/module/games/game-2048/Game2048Constants.js +1 -263
  90. package/lib/module/games/game-2048/Game2048Constants.js.map +1 -1
  91. package/lib/module/games/game-2048/Game2048Service.js +1 -457
  92. package/lib/module/games/game-2048/Game2048Store.js +1 -236
  93. package/lib/module/games/game-2048/components/GameBackground.js +1 -247
  94. package/lib/module/games/game-2048/components/GameGrid.js +1 -139
  95. package/lib/module/games/game-2048/components/GameTile.js +1 -72
  96. package/lib/module/games/game-2048/components/ScoreBoard.js +1 -52
  97. package/lib/module/games/game-2048/components/index.js +1 -7
  98. package/lib/module/games/maze-runner/MazeRunner.js +1 -267
  99. package/lib/module/games/maze-runner/MazeRunner.js.map +1 -1
  100. package/lib/module/games/maze-runner/MazeRunnerConstants.js +1 -100
  101. package/lib/module/games/maze-runner/MazeRunnerConstants.js.map +1 -1
  102. package/lib/module/games/maze-runner/MazeRunnerService.js +1 -586
  103. package/lib/module/games/maze-runner/components/EnhancedBallComponent.js +1 -150
  104. package/lib/module/games/maze-runner/components/EnhancedGameArea.js +1 -370
  105. package/lib/module/games/maze-runner/components/GameBackground.js +1 -175
  106. package/lib/module/games/maze-runner/components/ScoreBoard.js +1 -61
  107. package/lib/module/games/maze-runner/components/SkiaPipeComponent.js +1 -209
  108. package/lib/module/games/maze-runner/components/StaticGameBackground.js +1 -169
  109. package/lib/module/games/maze-runner/components/WallComponent.js +1 -91
  110. package/lib/module/games/maze-runner/components/index.js +1 -8
  111. package/lib/module/games/popit-fidget/PopitFidget.js +1 -285
  112. package/lib/module/games/popit-fidget/PopitFidget.js.map +1 -1
  113. package/lib/module/games/popit-fidget/PopitFidgetConstants.js +1 -113
  114. package/lib/module/games/popit-fidget/PopitFidgetConstants.js.map +1 -1
  115. package/lib/module/games/popit-fidget/PopitFidgetService.js +1 -132
  116. package/lib/module/games/popit-fidget/PopitFidgetStore.js +1 -125
  117. package/lib/module/games/popit-fidget/components/BubbleComponent.js +1 -198
  118. package/lib/module/games/popit-fidget/components/FidgetGrid.js +1 -165
  119. package/lib/module/games/popit-fidget/components/GameBackground.js +1 -177
  120. package/lib/module/games/popit-fidget/components/ScoreBoard.js +1 -61
  121. package/lib/module/games/popit-fidget/components/index.js +1 -7
  122. package/lib/module/games/sliding-numbers/SlidingNumbers.js +1 -159
  123. package/lib/module/games/sliding-numbers/SlidingNumbers.js.map +1 -1
  124. package/lib/module/games/sliding-numbers/SlidingNumbersConstants.js +1 -207
  125. package/lib/module/games/sliding-numbers/SlidingNumbersConstants.js.map +1 -1
  126. package/lib/module/games/sliding-numbers/SlidingNumbersService.js +1 -248
  127. package/lib/module/games/sliding-numbers/SlidingNumbersStore.js +1 -274
  128. package/lib/module/games/sliding-numbers/components/GameBackground.js +1 -259
  129. package/lib/module/games/sliding-numbers/components/NumbersGrid.js +1 -174
  130. package/lib/module/games/sliding-numbers/components/NumbersTile.js +1 -116
  131. package/lib/module/games/sliding-numbers/components/ScoreBoard.js +1 -64
  132. package/lib/module/games/sliding-numbers/components/index.js +1 -7
  133. package/lib/module/games/snake/Snake.js +1 -189
  134. package/lib/module/games/snake/Snake.js.map +1 -1
  135. package/lib/module/games/snake/SnakeConstants.js +1 -138
  136. package/lib/module/games/snake/SnakeConstants.js.map +1 -1
  137. package/lib/module/games/snake/SnakeService.js +1 -148
  138. package/lib/module/games/snake/SnakeStore.js +1 -182
  139. package/lib/module/games/snake/components/GameBackground.js +1 -221
  140. package/lib/module/games/snake/components/GameGrid.js +1 -153
  141. package/lib/module/games/snake/components/ScoreBoard.js +1 -51
  142. package/lib/module/games/snake/components/index.js +1 -6
  143. package/lib/module/games/snake/index.js +1 -6
  144. package/lib/module/games/space-fighter/SpaceFighter.js +1 -165
  145. package/lib/module/games/space-fighter/SpaceFighter.js.map +1 -1
  146. package/lib/module/games/space-fighter/SpaceFighterConstants.js +1 -108
  147. package/lib/module/games/space-fighter/SpaceFighterConstants.js.map +1 -1
  148. package/lib/module/games/space-fighter/SpaceFighterService.js +1 -326
  149. package/lib/module/games/space-fighter/SpaceFighterStore.js +1 -209
  150. package/lib/module/games/space-fighter/components/AsteroidComponent.js +1 -113
  151. package/lib/module/games/space-fighter/components/GameArea.js +1 -289
  152. package/lib/module/games/space-fighter/components/GameBackground.js +1 -239
  153. package/lib/module/games/space-fighter/components/ScoreBoard.js +1 -136
  154. package/lib/module/games/space-fighter/components/Spacecraft3D.js +1 -202
  155. package/lib/module/games/space-fighter/components/SpacecraftPath.js +1 -52
  156. package/lib/module/games/space-fighter/components/index.js +1 -9
  157. package/lib/module/games/whack-a-mole/WhackAMole.js +1 -270
  158. package/lib/module/games/whack-a-mole/WhackAMole.js.map +1 -1
  159. package/lib/module/games/whack-a-mole/WhackAMoleConstants.js +1 -115
  160. package/lib/module/games/whack-a-mole/WhackAMoleConstants.js.map +1 -1
  161. package/lib/module/games/whack-a-mole/WhackAMoleService.js +1 -120
  162. package/lib/module/games/whack-a-mole/WhackAMoleStore.js +1 -172
  163. package/lib/module/games/whack-a-mole/components/GameBackground.js +1 -477
  164. package/lib/module/games/whack-a-mole/components/GameGrid.js +1 -97
  165. package/lib/module/games/whack-a-mole/components/GameHole.js +1 -196
  166. package/lib/module/games/whack-a-mole/components/MoleCharacter.js +1 -241
  167. package/lib/module/games/whack-a-mole/components/ScoreBoard.js +1 -67
  168. package/lib/module/games/whack-a-mole/components/ScoreBoard.js.map +1 -1
  169. package/lib/module/games/whack-a-mole/components/index.js +1 -8
  170. package/lib/module/helpers/AnimationFrame.js +1 -120
  171. package/lib/module/helpers/AnimationTracker.js +1 -89
  172. package/lib/module/helpers/ErrorHandler.js +1 -269
  173. package/lib/module/helpers/GameControlButton.js +1 -219
  174. package/lib/module/helpers/GameOverModal.js +1 -144
  175. package/lib/module/helpers/GameOverModal.js.map +1 -1
  176. package/lib/module/helpers/GameSettingsModal.js +1 -287
  177. package/lib/module/helpers/ParticleBlast.js +1 -134
  178. package/lib/module/helpers/ScoreBoardContainer.js +1 -34
  179. package/lib/module/helpers/index.js +1 -12
  180. package/lib/module/index.js +1 -22
  181. package/lib/module/services/GamesConstants.js +1 -178
  182. package/lib/module/services/GamesService.js +1 -112
  183. package/lib/module/services/GamesService.js.map +1 -1
  184. package/lib/module/services/HapticsService.js +1 -77
  185. package/lib/module/services/SoundsService.js +1 -302
  186. package/lib/module/services/UtilsService.js +1 -32
  187. package/lib/typescript/src/games/balloon-blaster/BalloonBlaster.d.ts.map +1 -1
  188. package/lib/typescript/src/games/balloon-blaster/BalloonBlasterConstants.d.ts +1 -1
  189. package/lib/typescript/src/games/balloon-blaster/components/ScoreBoard.d.ts.map +1 -1
  190. package/lib/typescript/src/games/candy-crush/CandyCrushConstants.d.ts +7 -7
  191. package/lib/typescript/src/games/colors-sort/ColorsSort.d.ts.map +1 -1
  192. package/lib/typescript/src/games/colors-sort/ColorsSortStore.d.ts.map +1 -1
  193. package/lib/typescript/src/games/dino-jump/DinoJump.d.ts.map +1 -1
  194. package/lib/typescript/src/games/dino-jump/components/DinoSprite.d.ts.map +1 -1
  195. package/lib/typescript/src/games/flappy-bird/FlappyBird.d.ts.map +1 -1
  196. package/lib/typescript/src/games/flappy-bird/FlappyBirdConstants.d.ts.map +1 -1
  197. package/lib/typescript/src/games/fruit-merger/FruitMerger.d.ts.map +1 -1
  198. package/lib/typescript/src/games/fruit-merger/FruitMergerConstants.d.ts.map +1 -1
  199. package/lib/typescript/src/games/fruit-ninja/FruitNinja.d.ts.map +1 -1
  200. package/lib/typescript/src/games/fruit-ninja/components/ScoreBoard.d.ts.map +1 -1
  201. package/lib/typescript/src/games/game-2048/Game2048.d.ts.map +1 -1
  202. package/lib/typescript/src/games/maze-runner/MazeRunner.d.ts.map +1 -1
  203. package/lib/typescript/src/games/popit-fidget/PopitFidget.d.ts.map +1 -1
  204. package/lib/typescript/src/games/sliding-numbers/SlidingNumbers.d.ts.map +1 -1
  205. package/lib/typescript/src/games/space-fighter/SpaceFighter.d.ts.map +1 -1
  206. package/lib/typescript/src/games/whack-a-mole/WhackAMole.d.ts.map +1 -1
  207. package/lib/typescript/src/games/whack-a-mole/WhackAMoleConstants.d.ts +1 -1
  208. package/lib/typescript/src/games/whack-a-mole/components/ScoreBoard.d.ts.map +1 -1
  209. package/lib/typescript/src/helpers/GameOverModal.d.ts +3 -0
  210. package/lib/typescript/src/helpers/GameOverModal.d.ts.map +1 -1
  211. package/lib/typescript/src/services/GamesConstants.d.ts +7 -7
  212. package/package.json +2 -2
@@ -1,586 +1 @@
1
- "use strict";
2
-
3
- export class MazeRunnerService {
4
- gameTimer = null;
5
- constructor(gridSize = 15, gameAreaWidth = 350, gameAreaHeight = 500, gameDuration = 120) {
6
- this.gameConfig = {
7
- GRID_SIZE: gridSize,
8
- CELL_SIZE: Math.min(gameAreaWidth, gameAreaHeight) / gridSize,
9
- BALL_SIZE: 18,
10
- PIPE_WIDTH: 8,
11
- GAME_AREA_WIDTH: gameAreaWidth,
12
- GAME_AREA_HEIGHT: gameAreaHeight,
13
- COLLISION_TOLERANCE: 5,
14
- SCORE_PER_COMPLETION: 100,
15
- TIME_BONUS_MULTIPLIER: 10,
16
- GAME_DURATION: gameDuration
17
- };
18
- this.gameState = {
19
- isPlaying: false,
20
- isPaused: false,
21
- score: 0,
22
- timeElapsed: 0,
23
- // Start from 0 and count up
24
- ballPosition: {
25
- x: 0,
26
- y: 0
27
- },
28
- targetPosition: {
29
- x: 0,
30
- y: 0
31
- },
32
- isCompleted: false,
33
- gameOver: false
34
- };
35
- this.maze = [];
36
- this.walls = [];
37
- this.initializeMaze();
38
- // Generate initial maze on service creation
39
- this.generateMazeWithBacktracking();
40
- this.generateWalls();
41
- this.setStartAndEndPositions();
42
- }
43
- setGameStateChangeCallback(callback) {
44
- this.onGameStateChange = callback;
45
- }
46
- notifyGameStateChange() {
47
- if (this.onGameStateChange) {
48
- this.onGameStateChange({
49
- ...this.gameState
50
- });
51
- }
52
- }
53
- initializeMaze() {
54
- // Initialize maze grid
55
- this.maze = [];
56
- for (let y = 0; y < this.gameConfig.GRID_SIZE; y++) {
57
- this.maze[y] = [];
58
- for (let x = 0; x < this.gameConfig.GRID_SIZE; x++) {
59
- if (this.maze[y]) {
60
- this.maze[y][x] = {
61
- x,
62
- y,
63
- walls: {
64
- top: true,
65
- right: true,
66
- bottom: true,
67
- left: true
68
- },
69
- visited: false
70
- };
71
- }
72
- }
73
- }
74
- }
75
- generateNewMaze() {
76
- this.initializeMaze();
77
- this.generateMazeWithBacktracking();
78
- this.generateWalls();
79
- this.setStartAndEndPositions();
80
-
81
- // Reset game state for new maze
82
- this.gameState.isCompleted = false;
83
- this.gameState.isPlaying = false; // Don't auto-start, let user click start
84
- this.gameState.isPaused = false;
85
- this.gameState.gameOver = false;
86
- this.gameState.score = 0; // Reset score for new maze
87
- this.gameState.timeElapsed = 0; // Start from 0 and count up
88
-
89
- this.notifyGameStateChange();
90
- }
91
- generateMazeWithBacktracking() {
92
- // Create a proper maze using recursive backtracking algorithm
93
- const stack = [];
94
- const startCell = this.maze[0]?.[0];
95
- if (startCell) {
96
- startCell.visited = true;
97
- stack.push(startCell);
98
- }
99
- while (stack.length > 0) {
100
- const current = stack[stack.length - 1];
101
- const neighbors = current ? this.getUnvisitedNeighbors(current) : [];
102
- if (neighbors.length > 0) {
103
- // Completely random neighbor selection for unpredictable maze generation
104
- const next = neighbors[Math.floor(Math.random() * neighbors.length)];
105
- if (current && next) this.removeWallBetween(current, next);
106
- if (next) {
107
- next.visited = true;
108
- stack.push(next);
109
- }
110
- } else {
111
- stack.pop();
112
- }
113
- }
114
-
115
- // Only minimal simplification to ensure connectivity
116
- this.ensureConnectivity();
117
- }
118
- ensureConnectivity() {
119
- // Only ensure basic connectivity without creating obvious paths
120
- // Check if there's a path from start to end using BFS
121
- if (!this.hasPathFromStartToEnd()) {
122
- // If no path exists, create minimal connections
123
- this.createMinimalPath();
124
- }
125
- }
126
- hasPathFromStartToEnd() {
127
- // Use BFS to check if there's a path from start (0,0) to end (GRID_SIZE-1, GRID_SIZE-1)
128
- const visited = Array(this.gameConfig.GRID_SIZE).fill(null).map(() => Array(this.gameConfig.GRID_SIZE).fill(false));
129
- const queue = [{
130
- x: 0,
131
- y: 0
132
- }];
133
- if (visited[0]) visited[0][0] = true;
134
- while (queue.length > 0) {
135
- const current = queue.shift();
136
- if (current.x === this.gameConfig.GRID_SIZE - 1 && current.y === this.gameConfig.GRID_SIZE - 1) {
137
- return true; // Found path to end
138
- }
139
-
140
- // Check all four directions
141
- const directions = [{
142
- dx: 0,
143
- dy: -1,
144
- wall: 'top'
145
- },
146
- // Up
147
- {
148
- dx: 1,
149
- dy: 0,
150
- wall: 'right'
151
- },
152
- // Right
153
- {
154
- dx: 0,
155
- dy: 1,
156
- wall: 'bottom'
157
- },
158
- // Down
159
- {
160
- dx: -1,
161
- dy: 0,
162
- wall: 'left'
163
- } // Left
164
- ];
165
- for (const dir of directions) {
166
- const newX = current.x + dir.dx;
167
- const newY = current.y + dir.dy;
168
- if (newX >= 0 && newX < this.gameConfig.GRID_SIZE && newY >= 0 && newY < this.gameConfig.GRID_SIZE && !visited[newY]?.[newX]) {
169
- // Check if there's no wall blocking this direction
170
- const currentCell = this.maze[current.y]?.[current.x];
171
- if (currentCell && !currentCell.walls[dir.wall]) {
172
- if (visited[newY]) visited[newY][newX] = true;
173
- queue.push({
174
- x: newX,
175
- y: newY
176
- });
177
- }
178
- }
179
- }
180
- }
181
- return false; // No path found
182
- }
183
- createMinimalPath() {
184
- // Create a winding, unpredictable path from start to end
185
- // This should only be called if no path exists after maze generation
186
- let currentX = 0;
187
- let currentY = 0;
188
- const endX = this.gameConfig.GRID_SIZE - 1;
189
- const endY = this.gameConfig.GRID_SIZE - 1;
190
- const visited = new Set();
191
- while (currentX !== endX || currentY !== endY) {
192
- const key = `${currentX},${currentY}`;
193
- if (visited.has(key)) {
194
- // Prevent infinite loops - break out and create direct path
195
- break;
196
- }
197
- visited.add(key);
198
-
199
- // Randomly choose direction, but prefer moving towards target occasionally
200
- const possibleMoves = [];
201
- if (currentX < endX) possibleMoves.push('right');
202
- if (currentY < endY) possibleMoves.push('down');
203
- if (currentX > 0) possibleMoves.push('left');
204
- if (currentY > 0) possibleMoves.push('up');
205
- if (possibleMoves.length === 0) break;
206
-
207
- // 60% chance to move towards target, 40% chance random direction
208
- let direction;
209
- if (Math.random() < 0.6 && (currentX !== endX || currentY !== endY)) {
210
- // Move towards target
211
- if (currentX < endX && Math.random() < 0.5) {
212
- direction = 'right';
213
- } else if (currentY < endY) {
214
- direction = 'down';
215
- } else if (currentX < endX) {
216
- direction = 'right';
217
- } else {
218
- direction = possibleMoves[Math.floor(Math.random() * possibleMoves.length)];
219
- }
220
- } else {
221
- // Random direction
222
- direction = possibleMoves[Math.floor(Math.random() * possibleMoves.length)];
223
- }
224
-
225
- // Remove wall in chosen direction
226
- const cell = this.maze[currentY]?.[currentX];
227
- if (direction === 'right' && currentX < this.gameConfig.GRID_SIZE - 1 && cell) {
228
- cell.walls.right = false;
229
- const rightCell = this.maze[currentY]?.[currentX + 1];
230
- if (rightCell) rightCell.walls.left = false;
231
- currentX++;
232
- } else if (direction === 'down' && currentY < this.gameConfig.GRID_SIZE - 1 && cell) {
233
- cell.walls.bottom = false;
234
- const bottomCell = this.maze[currentY + 1]?.[currentX];
235
- if (bottomCell) bottomCell.walls.top = false;
236
- currentY++;
237
- } else if (direction === 'left' && currentX > 0 && cell) {
238
- cell.walls.left = false;
239
- const leftCell = this.maze[currentY]?.[currentX - 1];
240
- if (leftCell) leftCell.walls.right = false;
241
- currentX--;
242
- } else if (direction === 'up' && currentY > 0 && cell) {
243
- cell.walls.top = false;
244
- const topCell = this.maze[currentY - 1]?.[currentX];
245
- if (topCell) topCell.walls.bottom = false;
246
- currentY--;
247
- }
248
- }
249
- }
250
- getUnvisitedNeighbors(cell) {
251
- const neighbors = [];
252
- const {
253
- x,
254
- y
255
- } = cell;
256
-
257
- // Top
258
- if (y > 0 && this.maze[y - 1]?.[x] && !this.maze[y - 1]?.[x]?.visited) {
259
- const topCell = this.maze[y - 1]?.[x];
260
- if (topCell) {
261
- neighbors.push(topCell);
262
- }
263
- }
264
- // Right
265
- if (x < this.gameConfig.GRID_SIZE - 1 && this.maze[y]?.[x + 1] && !this.maze[y]?.[x + 1]?.visited) {
266
- const rightCell = this.maze[y]?.[x + 1];
267
- if (rightCell) {
268
- neighbors.push(rightCell);
269
- }
270
- }
271
- // Bottom
272
- if (y < this.gameConfig.GRID_SIZE - 1 && this.maze[y + 1]?.[x] && !this.maze[y + 1]?.[x]?.visited) {
273
- const bottomCell = this.maze[y + 1]?.[x];
274
- if (bottomCell) {
275
- neighbors.push(bottomCell);
276
- }
277
- }
278
- // Left
279
- if (x > 0 && this.maze[y]?.[x - 1] && !this.maze[y]?.[x - 1]?.visited) {
280
- const leftCell = this.maze[y]?.[x - 1];
281
- if (leftCell) {
282
- neighbors.push(leftCell);
283
- }
284
- }
285
- return neighbors;
286
- }
287
- removeWallBetween(current, next) {
288
- const dx = current.x - next.x;
289
- const dy = current.y - next.y;
290
- if (dx === 1) {
291
- // Next is to the left
292
- current.walls.left = false;
293
- next.walls.right = false;
294
- } else if (dx === -1) {
295
- // Next is to the right
296
- current.walls.right = false;
297
- next.walls.left = false;
298
- } else if (dy === 1) {
299
- // Next is above
300
- current.walls.top = false;
301
- next.walls.bottom = false;
302
- } else if (dy === -1) {
303
- // Next is below
304
- current.walls.bottom = false;
305
- next.walls.top = false;
306
- }
307
- }
308
- generateWalls() {
309
- this.walls = [];
310
- const wallThickness = 4;
311
- const cellSize = this.gameConfig.CELL_SIZE;
312
- const mazeWidth = this.gameConfig.GRID_SIZE * cellSize;
313
- const mazeHeight = this.gameConfig.GRID_SIZE * cellSize;
314
-
315
- // Create outer boundary walls (solid perimeter around the maze area)
316
- // Top boundary
317
- this.walls.push({
318
- x: 0,
319
- y: 0,
320
- width: mazeWidth,
321
- height: wallThickness,
322
- type: 'horizontal'
323
- });
324
-
325
- // Bottom boundary
326
- this.walls.push({
327
- x: 0,
328
- y: mazeHeight - wallThickness,
329
- width: mazeWidth,
330
- height: wallThickness,
331
- type: 'horizontal'
332
- });
333
-
334
- // Left boundary
335
- this.walls.push({
336
- x: 0,
337
- y: 0,
338
- width: wallThickness,
339
- height: mazeHeight,
340
- type: 'vertical'
341
- });
342
-
343
- // Right boundary
344
- this.walls.push({
345
- x: mazeWidth - wallThickness,
346
- y: 0,
347
- width: wallThickness,
348
- height: mazeHeight,
349
- type: 'vertical'
350
- });
351
-
352
- // Generate internal walls based on maze cells
353
- // Only add walls where they are needed to avoid duplicates
354
- for (let y = 0; y < this.gameConfig.GRID_SIZE; y++) {
355
- for (let x = 0; x < this.gameConfig.GRID_SIZE; x++) {
356
- const cell = this.maze[y]?.[x];
357
- if (!cell) continue;
358
- const cellX = x * cellSize;
359
- const cellY = y * cellSize;
360
-
361
- // Add horizontal walls (only add each wall once)
362
- // Add top wall if this cell has a top wall and we're not at the top edge
363
- if (cell.walls.top && y > 0) {
364
- this.walls.push({
365
- x: cellX,
366
- y: cellY,
367
- width: cellSize,
368
- height: wallThickness,
369
- type: 'horizontal'
370
- });
371
- }
372
-
373
- // Add vertical walls (only add each wall once)
374
- // Add left wall if this cell has a left wall and we're not at the left edge
375
- if (cell.walls.left && x > 0) {
376
- this.walls.push({
377
- x: cellX,
378
- y: cellY,
379
- width: wallThickness,
380
- height: cellSize,
381
- type: 'vertical'
382
- });
383
- }
384
- }
385
- }
386
- }
387
- setStartAndEndPositions() {
388
- const cellSize = this.gameConfig.CELL_SIZE;
389
-
390
- // Start position (top-left)
391
- this.gameState.ballPosition = {
392
- x: cellSize / 2,
393
- y: cellSize / 2
394
- };
395
-
396
- // End position (bottom-right)
397
- this.gameState.targetPosition = {
398
- x: (this.gameConfig.GRID_SIZE - 1) * cellSize + cellSize / 2,
399
- y: (this.gameConfig.GRID_SIZE - 1) * cellSize + cellSize / 2
400
- };
401
- }
402
- updateSettings(gridSize, gameDuration, gameAreaWidth, gameAreaHeight) {
403
- // Update game configuration with new settings
404
- const newGameAreaWidth = gameAreaWidth || this.gameConfig.GAME_AREA_WIDTH;
405
- const newGameAreaHeight = gameAreaHeight || this.gameConfig.GAME_AREA_HEIGHT;
406
- this.gameConfig.GRID_SIZE = gridSize;
407
- this.gameConfig.GAME_AREA_WIDTH = newGameAreaWidth;
408
- this.gameConfig.GAME_AREA_HEIGHT = newGameAreaHeight;
409
- this.gameConfig.CELL_SIZE = Math.min(newGameAreaWidth, newGameAreaHeight) / gridSize;
410
- this.gameConfig.GAME_DURATION = gameDuration; // Update game duration
411
-
412
- // Generate new maze with updated settings
413
- this.generateNewMaze();
414
-
415
- // Reset game state
416
- this.gameState.isPlaying = false;
417
- this.gameState.isPaused = false;
418
- this.gameState.timeElapsed = 0; // Start from 0 and count up
419
- this.gameState.isCompleted = false;
420
- this.gameState.gameOver = false;
421
- this.stopTimer();
422
- this.notifyGameStateChange();
423
- }
424
- resetGame() {
425
- // Generate a completely new maze for fresh gameplay
426
- this.generateNewMaze();
427
-
428
- // Reset game state without starting - just prepare for a new game
429
- this.gameState.isPlaying = false; // Don't auto-start
430
- this.gameState.isPaused = false;
431
- this.gameState.score = 0; // Reset score for new game
432
- this.gameState.timeElapsed = 0; // Start from 0 and count up
433
- this.gameState.isCompleted = false;
434
- this.gameState.gameOver = false;
435
- this.stopTimer(); // Make sure timer is stopped
436
- this.notifyGameStateChange();
437
- }
438
- startGame() {
439
- // Start the game with current maze (don't regenerate here since resetGame already did)
440
- this.gameState.ballPosition = {
441
- x: this.gameConfig.CELL_SIZE / 2,
442
- y: this.gameConfig.CELL_SIZE / 2
443
- };
444
- this.gameState.targetPosition = {
445
- x: (this.gameConfig.GRID_SIZE - 1) * this.gameConfig.CELL_SIZE + this.gameConfig.CELL_SIZE / 2,
446
- y: (this.gameConfig.GRID_SIZE - 1) * this.gameConfig.CELL_SIZE + this.gameConfig.CELL_SIZE / 2
447
- };
448
- this.gameState.isPlaying = true;
449
- this.gameState.isPaused = false;
450
- this.gameState.score = 0; // Reset score when starting new game
451
- this.gameState.timeElapsed = 0; // Start from 0 and count up
452
- this.gameState.isCompleted = false;
453
- this.gameState.gameOver = false;
454
- this.startTimer();
455
- this.notifyGameStateChange();
456
- }
457
- pauseGame() {
458
- this.gameState.isPaused = true;
459
- this.stopTimer();
460
- this.notifyGameStateChange();
461
- }
462
- resumeGame() {
463
- this.gameState.isPaused = false;
464
- this.startTimer();
465
- this.notifyGameStateChange();
466
- }
467
- stopGame() {
468
- this.gameState.isPlaying = false;
469
- this.gameState.isPaused = false;
470
- this.stopTimer();
471
- this.notifyGameStateChange();
472
- }
473
- startTimer() {
474
- this.stopTimer();
475
- this.gameTimer = setInterval(() => {
476
- if (!this.gameState.isPaused && this.gameState.isPlaying) {
477
- this.gameState.timeElapsed += 1; // Count up from 0
478
-
479
- // No time limit - player can take as long as they need
480
- // Timer just tracks how long they've been playing
481
-
482
- this.notifyGameStateChange();
483
- }
484
- }, 1000);
485
- }
486
- stopTimer() {
487
- if (this.gameTimer) {
488
- clearInterval(this.gameTimer);
489
- this.gameTimer = null;
490
- }
491
- }
492
- updateBallPosition(newPosition) {
493
- if (!this.gameState.isPlaying || this.gameState.isPaused) {
494
- return false;
495
- }
496
-
497
- // Check if the new position is valid (within maze path)
498
- if (this.isValidPosition(newPosition)) {
499
- this.gameState.ballPosition = newPosition;
500
-
501
- // Check if reached target (only if not already completed)
502
- if (!this.gameState.isCompleted && this.isNearTarget(newPosition)) {
503
- this.completeLevel();
504
- }
505
- this.notifyGameStateChange();
506
- return true;
507
- }
508
- return false;
509
- }
510
- isValidPosition(position) {
511
- const {
512
- x,
513
- y
514
- } = position;
515
- const ballRadius = this.gameConfig.BALL_SIZE / 2;
516
-
517
- // Check if ball would collide with any wall
518
- for (const wall of this.walls) {
519
- if (this.ballCollidesWithWall(x, y, ballRadius, wall)) {
520
- return false;
521
- }
522
- }
523
-
524
- // Check game area boundaries (with some padding)
525
- const padding = ballRadius + 2;
526
- if (x - ballRadius < padding || x + ballRadius > this.gameConfig.GAME_AREA_WIDTH - padding || y - ballRadius < padding || y + ballRadius > this.gameConfig.GAME_AREA_HEIGHT - padding) {
527
- return false;
528
- }
529
- return true;
530
- }
531
- ballCollidesWithWall(ballX, ballY, ballRadius, wall) {
532
- // Calculate the closest point on the wall rectangle to the ball center
533
- const closestX = Math.max(wall.x, Math.min(ballX, wall.x + wall.width));
534
- const closestY = Math.max(wall.y, Math.min(ballY, wall.y + wall.height));
535
-
536
- // Calculate the distance from the ball center to this closest point
537
- const distanceX = ballX - closestX;
538
- const distanceY = ballY - closestY;
539
- const distanceSquared = distanceX * distanceX + distanceY * distanceY;
540
-
541
- // Check if the distance is less than the ball radius
542
- return distanceSquared < ballRadius * ballRadius;
543
- }
544
- isNearTarget(position) {
545
- const distance = Math.sqrt(Math.pow(position.x - this.gameState.targetPosition.x, 2) + Math.pow(position.y - this.gameState.targetPosition.y, 2));
546
- // Make target detection more generous - use 1.5x ball size for easier completion
547
- return distance < this.gameConfig.BALL_SIZE * 1.5;
548
- }
549
- completeLevel() {
550
- // No time bonus - faster completion is the goal
551
- this.gameState.score += this.gameConfig.SCORE_PER_COMPLETION;
552
- this.gameState.isCompleted = true;
553
- this.gameState.isPlaying = false; // Stop the game when maze is completed
554
- this.stopTimer();
555
- this.notifyGameStateChange();
556
- }
557
- getGameState() {
558
- return {
559
- ...this.gameState
560
- };
561
- }
562
- getGameConfig() {
563
- return {
564
- ...this.gameConfig
565
- };
566
- }
567
- getWalls() {
568
- return [...this.walls];
569
- }
570
- getMaze() {
571
- return this.maze.map(row => row.map(cell => ({
572
- ...cell
573
- })));
574
- }
575
-
576
- // Format time for display (MM:SS format)
577
- formatTime(seconds) {
578
- const minutes = Math.floor(seconds / 60);
579
- const remainingSeconds = seconds % 60;
580
- return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
581
- }
582
- cleanup() {
583
- this.stopTimer();
584
- }
585
- }
586
- //# sourceMappingURL=MazeRunnerService.js.map
1
+ "use strict";export class MazeRunnerService{gameTimer = null;constructor(gridSize = 15,gameAreaWidth = 350,gameAreaHeight = 500,gameDuration = 120){this.gameConfig ={GRID_SIZE:gridSize,CELL_SIZE:Math.min(gameAreaWidth,gameAreaHeight)/ gridSize,BALL_SIZE:18,PIPE_WIDTH:8,GAME_AREA_WIDTH:gameAreaWidth,GAME_AREA_HEIGHT:gameAreaHeight,COLLISION_TOLERANCE:5,SCORE_PER_COMPLETION:100,TIME_BONUS_MULTIPLIER:10,GAME_DURATION:gameDuration};this.gameState ={isPlaying:false,isPaused:false,score:0,timeElapsed:0,ballPosition:{x:0,y:0},targetPosition:{x:0,y:0},isCompleted:false,gameOver:false};this.maze = [];this.walls = [];this.initializeMaze();this.generateMazeWithBacktracking();this.generateWalls();this.setStartAndEndPositions();}setGameStateChangeCallback(callback){this.onGameStateChange = callback;}notifyGameStateChange(){if(this.onGameStateChange){this.onGameStateChange({...this.gameState});}}initializeMaze(){this.maze = [];for(let y = 0;y < this.gameConfig.GRID_SIZE;y++){this.maze[y] = [];for(let x = 0;x < this.gameConfig.GRID_SIZE;x++){if(this.maze[y]){this.maze[y][x] ={x,y,walls:{top:true,right:true,bottom:true,left:true},visited:false};}}}}generateNewMaze(){this.initializeMaze();this.generateMazeWithBacktracking();this.generateWalls();this.setStartAndEndPositions();this.gameState.isCompleted = false;this.gameState.isPlaying = false;this.gameState.isPaused = false;this.gameState.gameOver = false;this.gameState.score = 0;this.gameState.timeElapsed = 0;this.notifyGameStateChange();}generateMazeWithBacktracking(){const stack = [];const startCell = this.maze[0]?.[0];if(startCell){startCell.visited = true;stack.push(startCell);}while(stack.length > 0){const current = stack[stack.length - 1];const neighbors = current ? this.getUnvisitedNeighbors(current):[];if(neighbors.length > 0){const next = neighbors[Math.floor(Math.random()* neighbors.length)];if(current && next)this.removeWallBetween(current,next);if(next){next.visited = true;stack.push(next);}}else{stack.pop();}}this.ensureConnectivity();}ensureConnectivity(){if(!this.hasPathFromStartToEnd()){this.createMinimalPath();}}hasPathFromStartToEnd(){const visited = Array(this.gameConfig.GRID_SIZE).fill(null).map(()=> Array(this.gameConfig.GRID_SIZE).fill(false));const queue = [{x:0,y:0}];if(visited[0])visited[0][0] = true;while(queue.length > 0){const current = queue.shift();if(current.x === this.gameConfig.GRID_SIZE - 1 && current.y === this.gameConfig.GRID_SIZE - 1){return true;}const directions = [{dx:0,dy:-1,wall:'top'},{dx:1,dy:0,wall:'right'},{dx:0,dy:1,wall:'bottom'},{dx:-1,dy:0,wall:'left'}];for(const dir of directions){const newX = current.x + dir.dx;const newY = current.y + dir.dy;if(newX >= 0 && newX < this.gameConfig.GRID_SIZE && newY >= 0 && newY < this.gameConfig.GRID_SIZE && !visited[newY]?.[newX]){const currentCell = this.maze[current.y]?.[current.x];if(currentCell && !currentCell.walls[dir.wall]){if(visited[newY])visited[newY][newX] = true;queue.push({x:newX,y:newY});}}}}return false;}createMinimalPath(){let currentX = 0;let currentY = 0;const endX = this.gameConfig.GRID_SIZE - 1;const endY = this.gameConfig.GRID_SIZE - 1;const visited = new Set();while(currentX !== endX || currentY !== endY){const key = `${currentX},${currentY}`;if(visited.has(key)){break;}visited.add(key);const possibleMoves = [];if(currentX < endX)possibleMoves.push('right');if(currentY < endY)possibleMoves.push('down');if(currentX > 0)possibleMoves.push('left');if(currentY > 0)possibleMoves.push('up');if(possibleMoves.length === 0)break;let direction;if(Math.random()< 0.6 &&(currentX !== endX || currentY !== endY)){if(currentX < endX && Math.random()< 0.5){direction = 'right';}else if(currentY < endY){direction = 'down';}else if(currentX < endX){direction = 'right';}else{direction = possibleMoves[Math.floor(Math.random()* possibleMoves.length)];}}else{direction = possibleMoves[Math.floor(Math.random()* possibleMoves.length)];}const cell = this.maze[currentY]?.[currentX];if(direction === 'right' && currentX < this.gameConfig.GRID_SIZE - 1 && cell){cell.walls.right = false;const rightCell = this.maze[currentY]?.[currentX + 1];if(rightCell)rightCell.walls.left = false;currentX++;}else if(direction === 'down' && currentY < this.gameConfig.GRID_SIZE - 1 && cell){cell.walls.bottom = false;const bottomCell = this.maze[currentY + 1]?.[currentX];if(bottomCell)bottomCell.walls.top = false;currentY++;}else if(direction === 'left' && currentX > 0 && cell){cell.walls.left = false;const leftCell = this.maze[currentY]?.[currentX - 1];if(leftCell)leftCell.walls.right = false;currentX--;}else if(direction === 'up' && currentY > 0 && cell){cell.walls.top = false;const topCell = this.maze[currentY - 1]?.[currentX];if(topCell)topCell.walls.bottom = false;currentY--;}}}getUnvisitedNeighbors(cell){const neighbors = [];const{x,y}= cell;if(y > 0 && this.maze[y - 1]?.[x] && !this.maze[y - 1]?.[x]?.visited){const topCell = this.maze[y - 1]?.[x];if(topCell){neighbors.push(topCell);}}if(x < this.gameConfig.GRID_SIZE - 1 && this.maze[y]?.[x + 1] && !this.maze[y]?.[x + 1]?.visited){const rightCell = this.maze[y]?.[x + 1];if(rightCell){neighbors.push(rightCell);}}if(y < this.gameConfig.GRID_SIZE - 1 && this.maze[y + 1]?.[x] && !this.maze[y + 1]?.[x]?.visited){const bottomCell = this.maze[y + 1]?.[x];if(bottomCell){neighbors.push(bottomCell);}}if(x > 0 && this.maze[y]?.[x - 1] && !this.maze[y]?.[x - 1]?.visited){const leftCell = this.maze[y]?.[x - 1];if(leftCell){neighbors.push(leftCell);}}return neighbors;}removeWallBetween(current,next){const dx = current.x - next.x;const dy = current.y - next.y;if(dx === 1){current.walls.left = false;next.walls.right = false;}else if(dx === -1){current.walls.right = false;next.walls.left = false;}else if(dy === 1){current.walls.top = false;next.walls.bottom = false;}else if(dy === -1){current.walls.bottom = false;next.walls.top = false;}}generateWalls(){this.walls = [];const wallThickness = 4;const cellSize = this.gameConfig.CELL_SIZE;const mazeWidth = this.gameConfig.GRID_SIZE * cellSize;const mazeHeight = this.gameConfig.GRID_SIZE * cellSize;this.walls.push({x:0,y:0,width:mazeWidth,height:wallThickness,type:'horizontal'});this.walls.push({x:0,y:mazeHeight - wallThickness,width:mazeWidth,height:wallThickness,type:'horizontal'});this.walls.push({x:0,y:0,width:wallThickness,height:mazeHeight,type:'vertical'});this.walls.push({x:mazeWidth - wallThickness,y:0,width:wallThickness,height:mazeHeight,type:'vertical'});for(let y = 0;y < this.gameConfig.GRID_SIZE;y++){for(let x = 0;x < this.gameConfig.GRID_SIZE;x++){const cell = this.maze[y]?.[x];if(!cell)continue;const cellX = x * cellSize;const cellY = y * cellSize;if(cell.walls.top && y > 0){this.walls.push({x:cellX,y:cellY,width:cellSize,height:wallThickness,type:'horizontal'});}if(cell.walls.left && x > 0){this.walls.push({x:cellX,y:cellY,width:wallThickness,height:cellSize,type:'vertical'});}}}}setStartAndEndPositions(){const cellSize = this.gameConfig.CELL_SIZE;this.gameState.ballPosition ={x:cellSize / 2,y:cellSize / 2};this.gameState.targetPosition ={x:(this.gameConfig.GRID_SIZE - 1)* cellSize + cellSize / 2,y:(this.gameConfig.GRID_SIZE - 1)* cellSize + cellSize / 2};}updateSettings(gridSize,gameDuration,gameAreaWidth,gameAreaHeight){const newGameAreaWidth = gameAreaWidth || this.gameConfig.GAME_AREA_WIDTH;const newGameAreaHeight = gameAreaHeight || this.gameConfig.GAME_AREA_HEIGHT;this.gameConfig.GRID_SIZE = gridSize;this.gameConfig.GAME_AREA_WIDTH = newGameAreaWidth;this.gameConfig.GAME_AREA_HEIGHT = newGameAreaHeight;this.gameConfig.CELL_SIZE = Math.min(newGameAreaWidth,newGameAreaHeight)/ gridSize;this.gameConfig.GAME_DURATION = gameDuration;this.generateNewMaze();this.gameState.isPlaying = false;this.gameState.isPaused = false;this.gameState.timeElapsed = 0;this.gameState.isCompleted = false;this.gameState.gameOver = false;this.stopTimer();this.notifyGameStateChange();}resetGame(){this.generateNewMaze();this.gameState.isPlaying = false;this.gameState.isPaused = false;this.gameState.score = 0;this.gameState.timeElapsed = 0;this.gameState.isCompleted = false;this.gameState.gameOver = false;this.stopTimer();this.notifyGameStateChange();}startGame(){this.gameState.ballPosition ={x:this.gameConfig.CELL_SIZE / 2,y:this.gameConfig.CELL_SIZE / 2};this.gameState.targetPosition ={x:(this.gameConfig.GRID_SIZE - 1)* this.gameConfig.CELL_SIZE + this.gameConfig.CELL_SIZE / 2,y:(this.gameConfig.GRID_SIZE - 1)* this.gameConfig.CELL_SIZE + this.gameConfig.CELL_SIZE / 2};this.gameState.isPlaying = true;this.gameState.isPaused = false;this.gameState.score = 0;this.gameState.timeElapsed = 0;this.gameState.isCompleted = false;this.gameState.gameOver = false;this.startTimer();this.notifyGameStateChange();}pauseGame(){this.gameState.isPaused = true;this.stopTimer();this.notifyGameStateChange();}resumeGame(){this.gameState.isPaused = false;this.startTimer();this.notifyGameStateChange();}stopGame(){this.gameState.isPlaying = false;this.gameState.isPaused = false;this.stopTimer();this.notifyGameStateChange();}startTimer(){this.stopTimer();this.gameTimer = setInterval(()=>{if(!this.gameState.isPaused && this.gameState.isPlaying){this.gameState.timeElapsed += 1;this.notifyGameStateChange();}},1000);}stopTimer(){if(this.gameTimer){clearInterval(this.gameTimer);this.gameTimer = null;}}updateBallPosition(newPosition){if(!this.gameState.isPlaying || this.gameState.isPaused){return false;}if(this.isValidPosition(newPosition)){this.gameState.ballPosition = newPosition;if(!this.gameState.isCompleted && this.isNearTarget(newPosition)){this.completeLevel();}this.notifyGameStateChange();return true;}return false;}isValidPosition(position){const{x,y}= position;const ballRadius = this.gameConfig.BALL_SIZE / 2;for(const wall of this.walls){if(this.ballCollidesWithWall(x,y,ballRadius,wall)){return false;}}const padding = ballRadius + 2;if(x - ballRadius < padding || x + ballRadius > this.gameConfig.GAME_AREA_WIDTH - padding || y - ballRadius < padding || y + ballRadius > this.gameConfig.GAME_AREA_HEIGHT - padding){return false;}return true;}ballCollidesWithWall(ballX,ballY,ballRadius,wall){const closestX = Math.max(wall.x,Math.min(ballX,wall.x + wall.width));const closestY = Math.max(wall.y,Math.min(ballY,wall.y + wall.height));const distanceX = ballX - closestX;const distanceY = ballY - closestY;const distanceSquared = distanceX * distanceX + distanceY * distanceY;return distanceSquared < ballRadius * ballRadius;}isNearTarget(position){const distance = Math.sqrt(Math.pow(position.x - this.gameState.targetPosition.x,2)+ Math.pow(position.y - this.gameState.targetPosition.y,2));return distance < this.gameConfig.BALL_SIZE * 1.5;}completeLevel(){this.gameState.score += this.gameConfig.SCORE_PER_COMPLETION;this.gameState.isCompleted = true;this.gameState.isPlaying = false;this.stopTimer();this.notifyGameStateChange();}getGameState(){return{...this.gameState};}getGameConfig(){return{...this.gameConfig};}getWalls(){return [...this.walls];}getMaze(){return this.maze.map(row => row.map(cell =>({...cell})));}formatTime(seconds){const minutes = Math.floor(seconds / 60);const remainingSeconds = seconds % 60;return `${minutes}:${remainingSeconds.toString().padStart(2,'0')}`;}cleanup(){this.stopTimer();}}