react-native-games 1.3.0 → 1.5.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 (195) hide show
  1. package/lib/module/games/balloon-blaster/BalloonBlaster.js +1 -1
  2. package/lib/module/games/balloon-blaster/BalloonBlasterConstants.js +1 -1
  3. package/lib/module/games/balloon-blaster/BalloonBlasterService.js +1 -1
  4. package/lib/module/games/balloon-blaster/BalloonBlasterStore.js +1 -1
  5. package/lib/module/games/balloon-blaster/components/BalloonComponent.js +1 -1
  6. package/lib/module/games/balloon-blaster/components/GameArea.js +1 -1
  7. package/lib/module/games/balloon-blaster/components/GameBackground.js +1 -1
  8. package/lib/module/games/balloon-blaster/components/ScoreBoard.js +1 -1
  9. package/lib/module/games/balloon-blaster/components/index.js +1 -1
  10. package/lib/module/games/bike-racing/BikeRacing.js +1 -1
  11. package/lib/module/games/bike-racing/BikeRacingConstants.js +1 -1
  12. package/lib/module/games/bike-racing/BikeRacingService.js +1 -1
  13. package/lib/module/games/bike-racing/BikeRacingStore.js +1 -1
  14. package/lib/module/games/bike-racing/components/BikeComponent.js +1 -1
  15. package/lib/module/games/bike-racing/components/GameBackground.js +1 -1
  16. package/lib/module/games/bike-racing/components/ScoreBoard.js +1 -1
  17. package/lib/module/games/bike-racing/components/index.js +1 -1
  18. package/lib/module/games/bike-racing/index.js +1 -1
  19. package/lib/module/games/block-blast/BlockBlast.js +1 -1
  20. package/lib/module/games/block-blast/BlockBlastConstants.js +1 -1
  21. package/lib/module/games/block-blast/BlockBlastService.js +1 -1
  22. package/lib/module/games/block-blast/BlockBlastStore.js +1 -1
  23. package/lib/module/games/block-blast/components/BlockPieceComponent.js +1 -1
  24. package/lib/module/games/block-blast/components/GameArea.js +1 -1
  25. package/lib/module/games/block-blast/components/GameBackground.js +1 -1
  26. package/lib/module/games/block-blast/components/GridComponent.js +1 -1
  27. package/lib/module/games/block-blast/components/ScoreBoard.js +1 -1
  28. package/lib/module/games/block-blast/components/index.js +1 -1
  29. package/lib/module/games/block-blast/index.js +1 -1
  30. package/lib/module/games/bubble-shooter/BubbleShooter.js +1 -1
  31. package/lib/module/games/bubble-shooter/BubbleShooterConstants.js +1 -1
  32. package/lib/module/games/bubble-shooter/BubbleShooterService.js +1 -1
  33. package/lib/module/games/bubble-shooter/BubbleShooterStore.js +1 -1
  34. package/lib/module/games/bubble-shooter/components/GameArea.js +1 -1
  35. package/lib/module/games/bubble-shooter/components/GameBackground.js +1 -1
  36. package/lib/module/games/bubble-shooter/components/ScoreBoard.js +1 -1
  37. package/lib/module/games/bubble-shooter/components/index.js +1 -1
  38. package/lib/module/games/bubble-shooter/index.js +1 -1
  39. package/lib/module/games/candy-crush/CandyCrush.js +1 -1
  40. package/lib/module/games/candy-crush/CandyCrushConstants.js +1 -1
  41. package/lib/module/games/candy-crush/CandyCrushService.js +1 -1
  42. package/lib/module/games/candy-crush/CandyCrushStore.js +1 -1
  43. package/lib/module/games/candy-crush/components/CandyItem.js +1 -1
  44. package/lib/module/games/candy-crush/components/GameBackground.js +1 -1
  45. package/lib/module/games/candy-crush/components/GameGrid.js +1 -1
  46. package/lib/module/games/candy-crush/components/ScoreBoard.js +1 -1
  47. package/lib/module/games/candy-crush/components/index.js +1 -1
  48. package/lib/module/games/candy-crush/index.js +1 -1
  49. package/lib/module/games/car-racing/CarRacing.js +1 -1
  50. package/lib/module/games/car-racing/CarRacingConstants.js +1 -1
  51. package/lib/module/games/car-racing/CarRacingService.js +1 -1
  52. package/lib/module/games/car-racing/CarRacingStore.js +1 -1
  53. package/lib/module/games/car-racing/components/CarComponent.js +1 -1
  54. package/lib/module/games/car-racing/components/GameBackground.js +1 -1
  55. package/lib/module/games/car-racing/components/ScoreBoard.js +1 -1
  56. package/lib/module/games/car-racing/components/index.js +1 -1
  57. package/lib/module/games/colors-sort/ColorsSort.js +1 -1
  58. package/lib/module/games/colors-sort/ColorsSortConstants.js +1 -1
  59. package/lib/module/games/colors-sort/ColorsSortService.js +1 -1
  60. package/lib/module/games/colors-sort/ColorsSortStore.js +1 -1
  61. package/lib/module/games/colors-sort/components/ColorContainer.js +1 -1
  62. package/lib/module/games/colors-sort/components/GameBackground.js +1 -1
  63. package/lib/module/games/colors-sort/components/ScoreBoard.js +1 -1
  64. package/lib/module/games/colors-sort/components/index.js +1 -1
  65. package/lib/module/games/dino-jump/DinoJump.js +1 -1
  66. package/lib/module/games/dino-jump/DinoJumpConstants.js +1 -1
  67. package/lib/module/games/dino-jump/DinoJumpService.js +1 -1
  68. package/lib/module/games/dino-jump/DinoJumpStore.js +1 -1
  69. package/lib/module/games/dino-jump/components/DinoSprite.js +1 -1
  70. package/lib/module/games/dino-jump/components/GameArea.js +1 -1
  71. package/lib/module/games/dino-jump/components/GameBackground.js +1 -1
  72. package/lib/module/games/dino-jump/components/ObstacleSprite.js +1 -1
  73. package/lib/module/games/dino-jump/components/ScoreBoard.js +1 -1
  74. package/lib/module/games/dino-jump/components/StarSprite.js +1 -1
  75. package/lib/module/games/dino-jump/components/index.js +1 -1
  76. package/lib/module/games/flappy-bird/FlappyBird.js +1 -1
  77. package/lib/module/games/flappy-bird/FlappyBirdConstants.js +1 -1
  78. package/lib/module/games/flappy-bird/FlappyBirdStore.js +1 -1
  79. package/lib/module/games/flappy-bird/components/Bird.js +1 -1
  80. package/lib/module/games/flappy-bird/components/GameArea.js +1 -1
  81. package/lib/module/games/flappy-bird/components/GameBackground.js +1 -1
  82. package/lib/module/games/flappy-bird/components/Pipes.js +1 -1
  83. package/lib/module/games/flappy-bird/components/ScoreBoard.js +1 -1
  84. package/lib/module/games/flappy-bird/components/index.js +1 -1
  85. package/lib/module/games/fruit-merger/FruitMerger.js +1 -1
  86. package/lib/module/games/fruit-merger/FruitMergerConstants.js +1 -1
  87. package/lib/module/games/fruit-merger/FruitMergerService.js +1 -1
  88. package/lib/module/games/fruit-merger/FruitMergerStore.js +1 -1
  89. package/lib/module/games/fruit-merger/components/FruitItem.js +1 -1
  90. package/lib/module/games/fruit-merger/components/GameArea.js +1 -1
  91. package/lib/module/games/fruit-merger/components/GameBackground.js +1 -1
  92. package/lib/module/games/fruit-merger/components/ScoreBoard.js +1 -1
  93. package/lib/module/games/fruit-merger/components/index.js +1 -1
  94. package/lib/module/games/fruit-ninja/FruitNinja.js +1 -1
  95. package/lib/module/games/fruit-ninja/FruitNinjaConstants.js +1 -1
  96. package/lib/module/games/fruit-ninja/FruitNinjaService.js +1 -1
  97. package/lib/module/games/fruit-ninja/FruitNinjaStore.js +1 -1
  98. package/lib/module/games/fruit-ninja/components/FruitComponent.js +1 -1
  99. package/lib/module/games/fruit-ninja/components/GameArea.js +1 -1
  100. package/lib/module/games/fruit-ninja/components/GameBackground.js +1 -1
  101. package/lib/module/games/fruit-ninja/components/ScoreBoard.js +1 -1
  102. package/lib/module/games/fruit-ninja/components/index.js +1 -1
  103. package/lib/module/games/game-2048/Game2048.js +1 -1
  104. package/lib/module/games/game-2048/Game2048Constants.js +1 -1
  105. package/lib/module/games/game-2048/Game2048Service.js +1 -1
  106. package/lib/module/games/game-2048/Game2048Store.js +1 -1
  107. package/lib/module/games/game-2048/components/GameBackground.js +1 -1
  108. package/lib/module/games/game-2048/components/GameGrid.js +1 -1
  109. package/lib/module/games/game-2048/components/GameTile.js +1 -1
  110. package/lib/module/games/game-2048/components/ScoreBoard.js +1 -1
  111. package/lib/module/games/game-2048/components/index.js +1 -1
  112. package/lib/module/games/maze-runner/MazeRunner.js +1 -1
  113. package/lib/module/games/maze-runner/MazeRunnerConstants.js +1 -1
  114. package/lib/module/games/maze-runner/MazeRunnerService.js +1 -1
  115. package/lib/module/games/maze-runner/components/EnhancedBallComponent.js +1 -1
  116. package/lib/module/games/maze-runner/components/EnhancedGameArea.js +1 -1
  117. package/lib/module/games/maze-runner/components/GameBackground.js +1 -1
  118. package/lib/module/games/maze-runner/components/ScoreBoard.js +1 -1
  119. package/lib/module/games/maze-runner/components/SkiaPipeComponent.js +1 -1
  120. package/lib/module/games/maze-runner/components/StaticGameBackground.js +1 -1
  121. package/lib/module/games/maze-runner/components/WallComponent.js +1 -1
  122. package/lib/module/games/maze-runner/components/index.js +1 -1
  123. package/lib/module/games/perfect-circle/PerfectCircle.js +1 -1
  124. package/lib/module/games/perfect-circle/PerfectCircleConstants.js +1 -1
  125. package/lib/module/games/perfect-circle/PerfectCircleService.js +1 -1
  126. package/lib/module/games/perfect-circle/PerfectCircleStore.js +1 -1
  127. package/lib/module/games/perfect-circle/components/DrawingCanvas.js +1 -1
  128. package/lib/module/games/perfect-circle/components/GameBackground.js +1 -1
  129. package/lib/module/games/perfect-circle/components/ScoreBoard.js +1 -1
  130. package/lib/module/games/perfect-circle/index.js +1 -1
  131. package/lib/module/games/popit-fidget/PopitFidget.js +1 -1
  132. package/lib/module/games/popit-fidget/PopitFidgetConstants.js +1 -1
  133. package/lib/module/games/popit-fidget/PopitFidgetService.js +1 -1
  134. package/lib/module/games/popit-fidget/PopitFidgetStore.js +1 -1
  135. package/lib/module/games/popit-fidget/components/BubbleComponent.js +1 -1
  136. package/lib/module/games/popit-fidget/components/FidgetGrid.js +1 -1
  137. package/lib/module/games/popit-fidget/components/GameBackground.js +1 -1
  138. package/lib/module/games/popit-fidget/components/ScoreBoard.js +1 -1
  139. package/lib/module/games/popit-fidget/components/index.js +1 -1
  140. package/lib/module/games/sliding-numbers/SlidingNumbers.js +1 -1
  141. package/lib/module/games/sliding-numbers/SlidingNumbersConstants.js +1 -1
  142. package/lib/module/games/sliding-numbers/SlidingNumbersService.js +1 -1
  143. package/lib/module/games/sliding-numbers/SlidingNumbersStore.js +1 -1
  144. package/lib/module/games/sliding-numbers/components/GameBackground.js +1 -1
  145. package/lib/module/games/sliding-numbers/components/NumbersGrid.js +1 -1
  146. package/lib/module/games/sliding-numbers/components/NumbersTile.js +1 -1
  147. package/lib/module/games/sliding-numbers/components/ScoreBoard.js +1 -1
  148. package/lib/module/games/sliding-numbers/components/index.js +1 -1
  149. package/lib/module/games/snake/Snake.js +1 -1
  150. package/lib/module/games/snake/SnakeConstants.js +1 -1
  151. package/lib/module/games/snake/SnakeService.js +1 -1
  152. package/lib/module/games/snake/SnakeStore.js +1 -1
  153. package/lib/module/games/snake/components/GameBackground.js +1 -1
  154. package/lib/module/games/snake/components/GameGrid.js +1 -1
  155. package/lib/module/games/snake/components/ScoreBoard.js +1 -1
  156. package/lib/module/games/snake/components/index.js +1 -1
  157. package/lib/module/games/snake/index.js +1 -1
  158. package/lib/module/games/space-fighter/SpaceFighter.js +1 -1
  159. package/lib/module/games/space-fighter/SpaceFighterConstants.js +1 -1
  160. package/lib/module/games/space-fighter/SpaceFighterService.js +1 -1
  161. package/lib/module/games/space-fighter/SpaceFighterStore.js +1 -1
  162. package/lib/module/games/space-fighter/components/AsteroidComponent.js +1 -1
  163. package/lib/module/games/space-fighter/components/GameArea.js +1 -1
  164. package/lib/module/games/space-fighter/components/GameBackground.js +1 -1
  165. package/lib/module/games/space-fighter/components/ScoreBoard.js +1 -1
  166. package/lib/module/games/space-fighter/components/Spacecraft3D.js +1 -1
  167. package/lib/module/games/space-fighter/components/SpacecraftPath.js +1 -1
  168. package/lib/module/games/space-fighter/components/index.js +1 -1
  169. package/lib/module/games/whack-a-mole/WhackAMole.js +1 -1
  170. package/lib/module/games/whack-a-mole/WhackAMoleConstants.js +1 -1
  171. package/lib/module/games/whack-a-mole/WhackAMoleService.js +1 -1
  172. package/lib/module/games/whack-a-mole/WhackAMoleStore.js +1 -1
  173. package/lib/module/games/whack-a-mole/components/GameBackground.js +1 -1
  174. package/lib/module/games/whack-a-mole/components/GameGrid.js +1 -1
  175. package/lib/module/games/whack-a-mole/components/GameHole.js +1 -1
  176. package/lib/module/games/whack-a-mole/components/MoleCharacter.js +1 -1
  177. package/lib/module/games/whack-a-mole/components/ScoreBoard.js +1 -1
  178. package/lib/module/games/whack-a-mole/components/index.js +1 -1
  179. package/lib/module/helpers/AnimationFrame.js +1 -1
  180. package/lib/module/helpers/AnimationTracker.js +1 -1
  181. package/lib/module/helpers/ErrorHandler.js +1 -1
  182. package/lib/module/helpers/GameControlButton.js +1 -1
  183. package/lib/module/helpers/GameOverModal.js +1 -1
  184. package/lib/module/helpers/GameSettingsModal.js +1 -1
  185. package/lib/module/helpers/ParticleBlast.js +1 -1
  186. package/lib/module/helpers/ScoreBoardContainer.js +1 -1
  187. package/lib/module/helpers/index.js +1 -1
  188. package/lib/module/index.js +1 -1
  189. package/lib/module/services/GamesConstants.js +1 -1
  190. package/lib/module/services/GamesService.js +1 -1
  191. package/lib/module/services/HapticsService.js +1 -1
  192. package/lib/module/services/ScoringService.js +1 -1
  193. package/lib/module/services/SoundsService.js +1 -1
  194. package/lib/module/services/UtilsService.js +1 -1
  195. package/package.json +5 -2
@@ -1 +1 @@
1
- "use strict";import{Dimensions}from 'react-native';const{width}= Dimensions.get('window');export const SLIDING_NUMBERS_CONSTANTS ={gridSize:{easy:3,medium:4,hard:5},maxNumbers:{easy:8,medium:15,hard:24},baseScore:{easy:1000,medium:2000,hard:3000},scoreMultiplier:{easy:5,medium:10,hard:15}};export const SLIDING_NUMBERS_GAME_CONFIG ={TILE_SIZE:Math.min(width * 0.16,70),TILE_GAP:4,GRID_PADDING:20,GAME_DURATION:600,SHUFFLE_MOVES:100,ANIMATION_DURATION:200};export const SLIDING_NUMBERS_ANIMATION_CONFIG ={TILE_SLIDE:{damping:25,stiffness:400,mass:1,restDisplacementThreshold:0.01,restSpeedThreshold:0.01},TILE_PRESS:{damping:15,stiffness:200},GESTURE:{activeScale:1.05,snapThreshold:0.3,velocityThreshold:500},COMPLETION_CELEBRATION:{duration:500}};export const SLIDING_NUMBERS_COLORS ={BACKGROUND:'#F5F5DC',GRID_BACKGROUND:'#FFFFFF',GRID_BORDER:'#E0E0E0',TILE_COLORS:['#FFB3D9','#B3E5FC','#C8E6C9','#FFF9C4','#E1BEE7','#FFCDD2','#B2DFDB','#F8BBD9','#A5D6A7','#FFECB3','#D1C4E9','#B3E5FC','#FFE0B2','#C8E6C9','#F8BBD9','#E0F2F1','#FCE4EC','#E8F5E8','#FFF3E0','#F3E5F5','#E1F5FE','#F9FBE7','#FFF8E1','#EFEBE9' ],TILE_BORDER:'#D0D0D0',TILE_SHADOW:'rgba(0,0,0,0.1)',NUMBER_TEXT:'#333333',WORD_TEXT:'#666666',EMPTY_TILE:'#F8F8F8',SCORE_BOARD:'#f59e0b',SCORE_TEXT:'#FFFFFF',TIMER_TEXT:'#f59e0b',UI:'#f59e0b',BUTTON_ACTIVE:'#f59e0b',BUTTON_INACTIVE:'#fbbf24',TEXT_PRIMARY:'#FFFFFF',TEXT_SECONDARY:'#fef3c7',WHITE:'#FFFFFF',START_BUTTON:'#f59e0b',COMPLETION_OVERLAY:'rgba(245,158,11,0.9)',BUTTON_BORDER:'#fbbf24'};export const NUMBER_WORDS ={1:'one',2:'two',3:'three',4:'four',5:'five',6:'six',7:'seven',8:'eight',9:'nine',10:'ten',11:'eleven',12:'twelve',13:'thirteen',14:'fourteen',15:'fifteen',16:'sixteen',17:'seventeen',18:'eighteen',19:'nineteen',20:'twenty',21:'twenty-one',22:'twenty-two',23:'twenty-three',24:'twenty-four'};export const SLIDING_NUMBERS_THEME ={backgroundColor:'rgba(0,0,0,0.7)',headerBackgroundColor:'#f59e0b',headerTextColor:'#ffffff',sectionBackgroundColor:'rgba(245,158,11,0.15)',sectionTitleColor:'#f59e0b',buttonSelectedColor:'#f59e0b',buttonUnselectedColor:'rgba(255,255,255,0.2)',buttonSelectedTextColor:'#ffffff',buttonUnselectedTextColor:'rgba(255,255,255,0.7)',switchTrackColorFalse:'rgba(245,158,11,0.3)',switchTrackColorTrue:'#f59e0b',switchThumbColor:'#ffffff',infoTextColor:'rgba(255,255,255,0.9)'};export const SLIDING_NUMBERS_DIFFICULTY_DESCRIPTIONS ={easy:'3x3 grid with numbers 1-8,perfect for beginners',medium:'4x4 grid with numbers 1-15,moderate challenge',hard:'5x5 grid with numbers 1-24,expert level puzzle'};
1
+ "use strict";import{Dimensions as e}from"react-native";const{width:t}=e.get("window");export const SLIDING_NUMBERS_CONSTANTS={gridSize:{easy:3,medium:4,hard:5},maxNumbers:{easy:8,medium:15,hard:24},baseScore:{easy:1e3,medium:2e3,hard:3e3},scoreMultiplier:{easy:5,medium:10,hard:15}};export const SLIDING_NUMBERS_GAME_CONFIG={TILE_SIZE:Math.min(.16*t,70),TILE_GAP:4,GRID_PADDING:20,GAME_DURATION:600,SHUFFLE_MOVES:100,ANIMATION_DURATION:200};export const SLIDING_NUMBERS_ANIMATION_CONFIG={TILE_SLIDE:{damping:25,stiffness:400,mass:1,restDisplacementThreshold:.01,restSpeedThreshold:.01},TILE_PRESS:{damping:15,stiffness:200},GESTURE:{activeScale:1.05,snapThreshold:.3,velocityThreshold:500},COMPLETION_CELEBRATION:{duration:500}};export const SLIDING_NUMBERS_COLORS={BACKGROUND:"#F5F5DC",GRID_BACKGROUND:"#FFFFFF",GRID_BORDER:"#E0E0E0",TILE_COLORS:["#FFB3D9","#B3E5FC","#C8E6C9","#FFF9C4","#E1BEE7","#FFCDD2","#B2DFDB","#F8BBD9","#A5D6A7","#FFECB3","#D1C4E9","#B3E5FC","#FFE0B2","#C8E6C9","#F8BBD9","#E0F2F1","#FCE4EC","#E8F5E8","#FFF3E0","#F3E5F5","#E1F5FE","#F9FBE7","#FFF8E1","#EFEBE9"],TILE_BORDER:"#D0D0D0",TILE_SHADOW:"rgba(0,0,0,0.1)",NUMBER_TEXT:"#333333",WORD_TEXT:"#666666",EMPTY_TILE:"#F8F8F8",SCORE_BOARD:"#f59e0b",SCORE_TEXT:"#FFFFFF",TIMER_TEXT:"#f59e0b",UI:"#f59e0b",BUTTON_ACTIVE:"#f59e0b",BUTTON_INACTIVE:"#fbbf24",TEXT_PRIMARY:"#FFFFFF",TEXT_SECONDARY:"#fef3c7",WHITE:"#FFFFFF",START_BUTTON:"#f59e0b",COMPLETION_OVERLAY:"rgba(245, 158, 11, 0.9)",BUTTON_BORDER:"#fbbf24"};export const NUMBER_WORDS={1:"one",2:"two",3:"three",4:"four",5:"five",6:"six",7:"seven",8:"eight",9:"nine",10:"ten",11:"eleven",12:"twelve",13:"thirteen",14:"fourteen",15:"fifteen",16:"sixteen",17:"seventeen",18:"eighteen",19:"nineteen",20:"twenty",21:"twenty-one",22:"twenty-two",23:"twenty-three",24:"twenty-four"};export const SLIDING_NUMBERS_THEME={backgroundColor:"rgba(0, 0, 0, 0.7)",headerBackgroundColor:"#f59e0b",headerTextColor:"#ffffff",sectionBackgroundColor:"rgba(245, 158, 11, 0.15)",sectionTitleColor:"#f59e0b",buttonSelectedColor:"#f59e0b",buttonUnselectedColor:"rgba(255, 255, 255, 0.2)",buttonSelectedTextColor:"#ffffff",buttonUnselectedTextColor:"rgba(255, 255, 255, 0.7)",switchTrackColorFalse:"rgba(245, 158, 11, 0.3)",switchTrackColorTrue:"#f59e0b",switchThumbColor:"#ffffff",infoTextColor:"rgba(255, 255, 255, 0.9)"};export const SLIDING_NUMBERS_DIFFICULTY_DESCRIPTIONS={easy:"3x3 grid with numbers 1-8, perfect for beginners",medium:"4x4 grid with numbers 1-15, moderate challenge",hard:"5x5 grid with numbers 1-24, expert level puzzle"};
@@ -1 +1 @@
1
- "use strict";import{SLIDING_NUMBERS_CONSTANTS}from "./SlidingNumbersConstants.js";export class SlidingNumbersService{gameTimer = null;createSolvedPuzzle(gridSize){const tiles = [];const totalTiles = gridSize * gridSize;for(let i = 0;i < totalTiles;i++){const row = Math.floor(i / gridSize);const col = i % gridSize;const value = i === totalTiles - 1 ? 0:i + 1;tiles.push({id:i,value,position:{row,col},isCorrect:true});}return tiles;}canMoveTile(tile,emptyPosition){const rowDiff = Math.abs(tile.position.row - emptyPosition.row);const colDiff = Math.abs(tile.position.col - emptyPosition.col);return rowDiff === 1 && colDiff === 0 || rowDiff === 0 && colDiff === 1;}getMovableTiles(tiles,emptyPosition){return tiles.filter(tile => tile.value !== 0 && this.canMoveTile(tile,emptyPosition));}isPuzzleSolvable(tiles,gridSize){const flatArray = [];let emptyRow = 0;for(let row = 0;row < gridSize;row++){for(let col = 0;col < gridSize;col++){const tile = tiles.find(t => t.position.row === row && t.position.col === col);if(tile){if(tile.value === 0){emptyRow = row;}else{flatArray.push(tile.value);}}}}let inversions = 0;for(let i = 0;i < flatArray.length - 1;i++){for(let j = i + 1;j < flatArray.length;j++){if((flatArray[i] || 0)>(flatArray[j] || 0)){inversions++;}}}if(gridSize % 2 === 1){return inversions % 2 === 0;}const emptyRowFromBottom = gridSize - emptyRow;if(emptyRowFromBottom % 2 === 0){return inversions % 2 === 1;}else{return inversions % 2 === 0;}}shufflePuzzle(tiles,gridSize){const shuffledTiles = [...tiles];let attempts = 0;const maxAttempts = 100;do{const positions = [];for(let row = 0;row < gridSize;row++){for(let col = 0;col < gridSize;col++){positions.push({row,col});}}for(let i = positions.length - 1;i > 0;i--){const j = Math.floor(Math.random()*(i + 1));const posI = positions[i];const posJ = positions[j];if(posI && posJ){[positions[i],positions[j]] = [posJ,posI];}}shuffledTiles.forEach((tile,index)=>{const position = positions[index];if(position){tile.position = position;}});attempts++;}while(!this.isPuzzleSolvable(shuffledTiles,gridSize)&& attempts < maxAttempts);if(attempts >= maxAttempts){return this.shuffleWithValidMoves(tiles,gridSize);}shuffledTiles.forEach((tile,index)=>{const correctRow = Math.floor(index / gridSize);const correctCol = index % gridSize;tile.isCorrect = tile.position.row === correctRow && tile.position.col === correctCol;});return shuffledTiles;}shuffleWithValidMoves(tiles,gridSize){const shuffledTiles = [...tiles];let emptyPosition ={row:gridSize - 1,col:gridSize - 1};const shuffleMoves = 1000;for(let i = 0;i < shuffleMoves;i++){const movableTiles = this.getMovableTiles(shuffledTiles,emptyPosition);if(movableTiles.length > 0){const randomTile = movableTiles[Math.floor(Math.random()* movableTiles.length)];const emptyTile = shuffledTiles.find(t => t.value === 0);if(emptyTile && randomTile){const tilePos = randomTile.position;randomTile.position = emptyPosition;emptyTile.position = tilePos;emptyPosition = tilePos;}}}shuffledTiles.forEach((tile,index)=>{const correctRow = Math.floor(index / gridSize);const correctCol = index % gridSize;tile.isCorrect = tile.position.row === correctRow && tile.position.col === correctCol;});return shuffledTiles;}isPuzzleCompleted(tiles,gridSize){return tiles.every((tile,index)=>{const correctRow = Math.floor(index / gridSize);const correctCol = index % gridSize;return tile.position.row === correctRow && tile.position.col === correctCol;});}calculateScore(timeElapsed,difficulty){const baseScore = SLIDING_NUMBERS_CONSTANTS.baseScore[difficulty];const multiplier = SLIDING_NUMBERS_CONSTANTS.scoreMultiplier[difficulty];const timeScore = Math.max(0,baseScore - timeElapsed * multiplier);return timeScore;}getOptimalMoveCount(difficulty){switch(difficulty){case 'easy':return 50;case 'medium':return 100;case 'hard':return 200;default:return 50;}}startTimer(onTick){this.stopTimer();this.gameTimer = setInterval(onTick,1000);}stopTimer(){if(this.gameTimer){clearInterval(this.gameTimer);this.gameTimer = null;}}getHint(tiles,emptyPosition){const movableTiles = this.getMovableTiles(tiles,emptyPosition);const incorrectMovableTiles = movableTiles.filter(tile => !tile.isCorrect);if(incorrectMovableTiles.length > 0){return incorrectMovableTiles[0] || null;}return movableTiles.length > 0 ? movableTiles[0] || null:null;}formatTime(seconds){const minutes = Math.floor(seconds / 60);const remainingSeconds = seconds % 60;return `${minutes}:${remainingSeconds.toString().padStart(2,'0')}`;}getDifficultySettings(difficulty){return{gridSize:SLIDING_NUMBERS_CONSTANTS.gridSize[difficulty],maxNumbers:SLIDING_NUMBERS_CONSTANTS.maxNumbers[difficulty],baseScore:SLIDING_NUMBERS_CONSTANTS.baseScore[difficulty],scoreMultiplier:SLIDING_NUMBERS_CONSTANTS.scoreMultiplier[difficulty]};}cleanup(){this.stopTimer();}}
1
+ "use strict";import{SLIDING_NUMBERS_CONSTANTS as t}from"./SlidingNumbersConstants.js";export class SlidingNumbersService{gameTimer=null;createSolvedPuzzle(t){const e=[],r=t*t;for(let s=0;r>s;s++){const l=Math.floor(s/t),o=s%t,n=s===r-1?0:s+1;e.push({id:s,value:n,position:{row:l,col:o},isCorrect:!0})}return e}canMoveTile(t,e){const r=Math.abs(t.position.row-e.row),s=Math.abs(t.position.col-e.col);return 1===r&&0===s||0===r&&1===s}getMovableTiles(t,e){return t.filter(t=>0!==t.value&&this.canMoveTile(t,e))}isPuzzleSolvable(t,e){const r=[];let s=0;for(let l=0;e>l;l++)for(let o=0;e>o;o++){const e=t.find(t=>t.position.row===l&&t.position.col===o);e&&(0===e.value?s=l:r.push(e.value))}let l=0;for(let t=0;t<r.length-1;t++)for(let e=t+1;e<r.length;e++)(r[t]||0)>(r[e]||0)&&l++;return e%2==1?l%2==0:(e-s)%2==0?l%2==1:l%2==0}shufflePuzzle(t,e){const r=[...t];let s=0;do{const t=[];for(let r=0;e>r;r++)for(let s=0;e>s;s++)t.push({row:r,col:s});for(let e=t.length-1;e>0;e--){const r=Math.floor(Math.random()*(e+1)),s=t[e],l=t[r];s&&l&&([t[e],t[r]]=[l,s])}r.forEach((e,r)=>{const s=t[r];s&&(e.position=s)}),s++}while(!this.isPuzzleSolvable(r,e)&&100>s);return 100>s?(r.forEach((t,r)=>{const s=Math.floor(r/e),l=r%e;t.isCorrect=t.position.row===s&&t.position.col===l}),r):this.shuffleWithValidMoves(t,e)}shuffleWithValidMoves(t,e){const r=[...t];let s={row:e-1,col:e-1};for(let t=0;1e3>t;t++){const t=this.getMovableTiles(r,s);if(t.length>0){const e=t[Math.floor(Math.random()*t.length)],l=r.find(t=>0===t.value);if(l&&e){const t=e.position;e.position=s,l.position=t,s=t}}}return r.forEach((t,r)=>{const s=Math.floor(r/e),l=r%e;t.isCorrect=t.position.row===s&&t.position.col===l}),r}isPuzzleCompleted(t,e){return t.every((t,r)=>{const s=Math.floor(r/e),l=r%e;return t.position.row===s&&t.position.col===l})}calculateScore(e,r){const s=t.baseScore[r],l=t.scoreMultiplier[r];return Math.max(0,s-e*l)}getOptimalMoveCount(t){switch(t){case"easy":default:return 50;case"medium":return 100;case"hard":return 200}}startTimer(t){this.stopTimer(),this.gameTimer=setInterval(t,1e3)}stopTimer(){this.gameTimer&&(clearInterval(this.gameTimer),this.gameTimer=null)}getHint(t,e){const r=this.getMovableTiles(t,e),s=r.filter(t=>!t.isCorrect);return s.length>0?s[0]||null:r.length>0&&r[0]||null}formatTime(t){return`${Math.floor(t/60)}:${(""+t%60).padStart(2,"0")}`}getDifficultySettings(e){return{gridSize:t.gridSize[e],maxNumbers:t.maxNumbers[e],baseScore:t.baseScore[e],scoreMultiplier:t.scoreMultiplier[e]}}cleanup(){this.stopTimer()}}
@@ -1 +1 @@
1
- "use strict";import{create}from 'zustand';import{subscribeWithSelector}from 'zustand/middleware';import{immerMiddleware}from "../../services/UtilsService.js";export const useSlidingNumbersStore = create()(subscribeWithSelector(immerMiddleware((set,get)=>({isPlaying:false,isCompleted:false,tiles:[],gridSize:3,emptyPosition:{row:2,col:2},score:0,timeLeft:600,timeElapsed:0,startTime:null,initializePuzzle:gridSize =>{set(state =>{const tiles = [];const totalTiles = gridSize * gridSize;for(let i = 0;i < totalTiles;i++){const row = Math.floor(i / gridSize);const col = i % gridSize;const value = i === totalTiles - 1 ? 0:i + 1;tiles.push({id:i,value,position:{row,col},isCorrect:true});}state.tiles = tiles;state.gridSize = gridSize;state.emptyPosition ={row:gridSize - 1,col:gridSize - 1};state.score = 0;state.timeLeft = 600;state.timeElapsed = 0;state.isCompleted = false;state.isPlaying = false;state.startTime = null;});get().shufflePuzzle();},moveTile:tileId =>{const state = get();if(!state.isPlaying || state.isCompleted)return false;const tile = state.tiles[tileId];const emptyPos = state.emptyPosition;const rowDiff = Math.abs(tile.position.row - emptyPos.row);const colDiff = Math.abs(tile.position.col - emptyPos.col);const isAdjacent = rowDiff === 1 && colDiff === 0 || rowDiff === 0 && colDiff === 1;if(!isAdjacent)return false;set(state =>{const tile = state.tiles[tileId];if(!tile)return;const tilePos = tile.position;const emptyTileId = state.tiles.findIndex(t => t.value === 0);const emptyTile = state.tiles[emptyTileId];if(!emptyTile)return;tile.position = state.emptyPosition;emptyTile.position = tilePos;state.emptyPosition = tilePos;if(state.startTime){state.timeElapsed = Math.floor((Date.now()- state.startTime)/ 1000);}state.tiles.forEach((tile,index)=>{const correctRow = Math.floor(index / state.gridSize);const correctCol = index % state.gridSize;tile.isCorrect = tile.position.row === correctRow && tile.position.col === correctCol;});});return true;},moveTileInDirection:(tileId,direction)=>{const state = get();if(!state.isPlaying || state.isCompleted)return false;const tile = state.tiles[tileId];if(!tile)return false;const emptyPos = state.emptyPosition;let expectedEmptyPos;switch(direction){case 'up':expectedEmptyPos ={row:tile.position.row - 1,col:tile.position.col};break;case 'down':expectedEmptyPos ={row:tile.position.row + 1,col:tile.position.col};break;case 'left':expectedEmptyPos ={row:tile.position.row,col:tile.position.col - 1};break;case 'right':expectedEmptyPos ={row:tile.position.row,col:tile.position.col + 1};break;}if(emptyPos.row !== expectedEmptyPos.row || emptyPos.col !== expectedEmptyPos.col){return false;}return get().moveTile(tileId);},shufflePuzzle:()=>{const shuffleMoves = 100;set(state =>{for(let i = 0;i < shuffleMoves;i++){const emptyPos = state.emptyPosition;const possibleMoves = [];state.tiles.forEach((tile,index)=>{if(tile.value === 0)return;const rowDiff = Math.abs(tile.position.row - emptyPos.row);const colDiff = Math.abs(tile.position.col - emptyPos.col);const isAdjacent = rowDiff === 1 && colDiff === 0 || rowDiff === 0 && colDiff === 1;if(isAdjacent){possibleMoves.push(index);}});if(possibleMoves.length > 0){const randomTileIndex = Math.floor(Math.random()* possibleMoves.length);const randomTileId = possibleMoves[randomTileIndex];if(randomTileId !== undefined){const tile = state.tiles[randomTileId];const emptyTileId = state.tiles.findIndex(t => t.value === 0);const emptyTile = state.tiles[emptyTileId];if(tile && emptyTile){const tilePos = tile.position;tile.position = state.emptyPosition;emptyTile.position = tilePos;state.emptyPosition = tilePos;}}}}state.tiles.forEach((tile,index)=>{const correctRow = Math.floor(index / state.gridSize);const correctCol = index % state.gridSize;tile.isCorrect = tile.position.row === correctRow && tile.position.col === correctCol;});state.score = 0;state.timeElapsed = 0;});},startGame:()=>{set(state =>{state.isPlaying = true;state.isCompleted = false;state.startTime = Date.now();});},stopGame:()=>{const state = get();state.resetGame();},resetGame:()=>{set(state =>{state.isPlaying = false;state.isCompleted = false;state.score = 0;state.timeLeft = 600;state.timeElapsed = 0;state.startTime = null;});},decrementTime:()=>{set(state =>{if(state.timeLeft > 0){state.timeLeft--;if(state.startTime){state.timeElapsed = Math.floor((Date.now()- state.startTime)/ 1000);}}else{state.isPlaying = false;state.startTime = null;}});},checkCompletion:difficulty =>{const state = get();const allCorrect = state.tiles.every(tile => tile.isCorrect);if(allCorrect && state.isPlaying){set(state =>{state.isCompleted = true;state.isPlaying = false;const baseScore = difficulty === 'easy' ? 1000:difficulty === 'medium' ? 2000:3000;const multiplier = difficulty === 'easy' ? 5:difficulty === 'medium' ? 10:15;const timeScore = Math.max(0,baseScore - state.timeElapsed * multiplier);const remainingTimeBonus = state.timeLeft * multiplier;state.score = timeScore + remainingTimeBonus;});}},updateScore:points =>{set(state =>{state.score += points;});}}))));export const useGameState =()=> useSlidingNumbersStore(state =>({isPlaying:state.isPlaying,isCompleted:state.isCompleted}));export const useScore =()=> useSlidingNumbersStore(state => state.score);export const useTimeLeft =()=> useSlidingNumbersStore(state => state.timeLeft);export const useTimeElapsed =()=> useSlidingNumbersStore(state => state.timeElapsed);export const useTiles =()=> useSlidingNumbersStore(state => state.tiles);export const useGridSize =()=> useSlidingNumbersStore(state => state.gridSize);export const useEmptyPosition =()=> useSlidingNumbersStore(state => state.emptyPosition);
1
+ "use strict";import{create as e}from"zustand";import{subscribeWithSelector as t}from"zustand/middleware";import{immerMiddleware as o}from"../../services/UtilsService.js";export const useSlidingNumbersStore=e()(t(o((e,t)=>({isPlaying:!1,isCompleted:!1,tiles:[],gridSize:3,emptyPosition:{row:2,col:2},score:0,timeLeft:600,timeElapsed:0,startTime:null,initializePuzzle:o=>{e(e=>{const t=[],s=o*o;for(let e=0;s>e;e++){const r=Math.floor(e/o),i=e%o,n=e===s-1?0:e+1;t.push({id:e,value:n,position:{row:r,col:i},isCorrect:!0})}e.tiles=t,e.gridSize=o,e.emptyPosition={row:o-1,col:o-1},e.score=0,e.timeLeft=600,e.timeElapsed=0,e.isCompleted=!1,e.isPlaying=!1,e.startTime=null}),t().shufflePuzzle()},moveTile:o=>{const s=t();if(!s.isPlaying||s.isCompleted)return!1;const r=s.tiles[o],i=s.emptyPosition,n=Math.abs(r.position.row-i.row),c=Math.abs(r.position.col-i.col);return(1===n&&0===c||0===n&&1===c)&&(e(e=>{const t=e.tiles[o];if(!t)return;const s=t.position,r=e.tiles.findIndex(e=>0===e.value),i=e.tiles[r];i&&(t.position=e.emptyPosition,i.position=s,e.emptyPosition=s,e.startTime&&(e.timeElapsed=Math.floor((Date.now()-e.startTime)/1e3)),e.tiles.forEach((t,o)=>{const s=Math.floor(o/e.gridSize),r=o%e.gridSize;t.isCorrect=t.position.row===s&&t.position.col===r}))}),!0)},moveTileInDirection:(e,o)=>{const s=t();if(!s.isPlaying||s.isCompleted)return!1;const r=s.tiles[e];if(!r)return!1;const i=s.emptyPosition;let n;switch(o){case"up":n={row:r.position.row-1,col:r.position.col};break;case"down":n={row:r.position.row+1,col:r.position.col};break;case"left":n={row:r.position.row,col:r.position.col-1};break;case"right":n={row:r.position.row,col:r.position.col+1}}return i.row===n.row&&i.col===n.col&&t().moveTile(e)},shufflePuzzle:()=>{e(e=>{for(let t=0;100>t;t++){const t=e.emptyPosition,o=[];if(e.tiles.forEach((e,s)=>{if(0===e.value)return;const r=Math.abs(e.position.row-t.row),i=Math.abs(e.position.col-t.col);(1===r&&0===i||0===r&&1===i)&&o.push(s)}),o.length>0){const t=Math.floor(Math.random()*o.length),s=o[t];if(void 0!==s){const t=e.tiles[s],o=e.tiles.findIndex(e=>0===e.value),r=e.tiles[o];if(t&&r){const o=t.position;t.position=e.emptyPosition,r.position=o,e.emptyPosition=o}}}}e.tiles.forEach((t,o)=>{const s=Math.floor(o/e.gridSize),r=o%e.gridSize;t.isCorrect=t.position.row===s&&t.position.col===r}),e.score=0,e.timeElapsed=0})},startGame:()=>{e(e=>{e.isPlaying=!0,e.isCompleted=!1,e.startTime=Date.now()})},stopGame:()=>{t().resetGame()},resetGame:()=>{e(e=>{e.isPlaying=!1,e.isCompleted=!1,e.score=0,e.timeLeft=600,e.timeElapsed=0,e.startTime=null})},decrementTime:()=>{e(e=>{e.timeLeft>0?(e.timeLeft--,e.startTime&&(e.timeElapsed=Math.floor((Date.now()-e.startTime)/1e3))):(e.isPlaying=!1,e.startTime=null)})},checkCompletion:o=>{const s=t();s.tiles.every(e=>e.isCorrect)&&s.isPlaying&&e(e=>{e.isCompleted=!0,e.isPlaying=!1;const t="easy"===o?5:"medium"===o?10:15,s=Math.max(0,("easy"===o?1e3:"medium"===o?2e3:3e3)-e.timeElapsed*t),r=e.timeLeft*t;e.score=s+r})},updateScore:t=>{e(e=>{e.score+=t})}}))));export const useGameState=()=>useSlidingNumbersStore(e=>({isPlaying:e.isPlaying,isCompleted:e.isCompleted}));export const useScore=()=>useSlidingNumbersStore(e=>e.score);export const useTimeLeft=()=>useSlidingNumbersStore(e=>e.timeLeft);export const useTimeElapsed=()=>useSlidingNumbersStore(e=>e.timeElapsed);export const useTiles=()=>useSlidingNumbersStore(e=>e.tiles);export const useGridSize=()=>useSlidingNumbersStore(e=>e.gridSize);export const useEmptyPosition=()=>useSlidingNumbersStore(e=>e.emptyPosition);
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{View,Dimensions}from 'react-native';import{Canvas,LinearGradient,Rect,vec,Circle,RoundedRect}from '@shopify/react-native-skia';import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const{width,height}= Dimensions.get('window');const PUZZLE_COLORS = ['#E91E63','#F44336','#FF5722','#FF9800','#FFC107','#CDDC39','#4CAF50','#009688','#00BCD4','#2196F3','#3F51B5','#9C27B0' ];export const GameBackground = React.memo(({children})=>{const generateFloatingTiles = React.useMemo(()=>{const elements = [];const tileCount = 25;for(let i = 0;i < tileCount;i++){const baseX = width / tileCount * i;const baseY = height / tileCount * i;const x = baseX + Math.sin(i * 0.5)* 50 + Math.random()* 100;const y = baseY + Math.cos(i * 0.7)* 60 + Math.random()* 120;const tileSize = 20 + Math.random()* 20;const colorIndex = i % PUZZLE_COLORS.length;const opacity = 0.3 + Math.sin(i)* 0.15;const tileX = Math.max(tileSize / 2,Math.min(width - tileSize,x));const tileY = Math.max(tileSize / 2,Math.min(height - tileSize,y));if(i % 4 === 0){elements.push(_jsx(RoundedRect,{x:tileX,y:tileY,width:tileSize,height:tileSize,r:tileSize * 0.2,color:`${PUZZLE_COLORS[colorIndex]}${Math.floor(opacity * 255).toString(16).padStart(2,'0')}`},`square-tile-${i}`));}else if(i % 4 === 1){elements.push(_jsx(RoundedRect,{x:tileX,y:tileY,width:tileSize * 0.6,height:tileSize,r:tileSize * 0.15,color:`${PUZZLE_COLORS[colorIndex]}${Math.floor(opacity * 255).toString(16).padStart(2,'0')}`},`rect-tile-${i}`));}else if(i % 4 === 2){elements.push(_jsx(RoundedRect,{x:tileX,y:tileY,width:tileSize * 1.2,height:tileSize * 0.8,r:tileSize * 0.1,color:`${PUZZLE_COLORS[colorIndex]}${Math.floor(opacity * 255).toString(16).padStart(2,'0')}`},`wide-tile-${i}`));}else{elements.push(_jsx(Circle,{cx:tileX + tileSize / 2,cy:tileY + tileSize / 2,r:tileSize / 2,color:`${PUZZLE_COLORS[colorIndex]}${Math.floor(opacity * 255).toString(16).padStart(2,'0')}`},`circle-tile-${i}`));}}return elements;},[width,height]);return _jsxs(View,{style:{flex:1},children:[_jsxs(Canvas,{style:{position:'absolute',top:0,left:0,right:0,bottom:0,width,height},children:[_jsx(Rect,{x:0,y:0,width:width,height:height,children:_jsx(LinearGradient,{start:vec(0,0),end:vec(width,height),colors:[`${PUZZLE_COLORS[0]}30`,`${PUZZLE_COLORS[3]}25`,`${PUZZLE_COLORS[6]}30`,`${PUZZLE_COLORS[9]}25`,`${PUZZLE_COLORS[11]}30` ]})}),_jsx(Rect,{x:0,y:0,width:width,height:height,children:_jsx(LinearGradient,{start:vec(0,0),end:vec(width / 2,height / 2),colors:[`${PUZZLE_COLORS[1]}20`,'transparent',`${PUZZLE_COLORS[4]}20`,'transparent',`${PUZZLE_COLORS[7]}20`,'transparent']})}),_jsx(Rect,{x:0,y:0,width:width,height:height,children:_jsx(LinearGradient,{start:vec(width,0),end:vec(0,height),colors:[`${PUZZLE_COLORS[2]}15`,'transparent',`${PUZZLE_COLORS[5]}15`,'transparent',`${PUZZLE_COLORS[8]}15`,'transparent']})}),generateFloatingTiles,_jsx(RoundedRect,{x:width * 0.08,y:height * 0.12,width:50,height:50,r:8,color:`${PUZZLE_COLORS[0]}50`}),_jsx(RoundedRect,{x:width * 0.85,y:height * 0.82,width:45,height:45,r:7,color:`${PUZZLE_COLORS[3]}50`}),_jsx(RoundedRect,{x:width * 0.82,y:height * 0.18,width:40,height:40,r:6,color:`${PUZZLE_COLORS[6]}50`}),_jsx(RoundedRect,{x:width * 0.12,y:height * 0.75,width:55,height:55,r:9,color:`${PUZZLE_COLORS[9]}50`}),_jsx(RoundedRect,{x:width * 0.47,y:height * 0.08,width:35,height:35,r:5,color:`${PUZZLE_COLORS[11]}50`}),_jsx(RoundedRect,{x:width * 0.15,y:height * 0.47,width:42,height:42,r:6,color:`${PUZZLE_COLORS[5]}50`}),_jsx(RoundedRect,{x:width * 0.75,y:height * 0.57,width:38,height:38,r:6,color:`${PUZZLE_COLORS[8]}50`}),_jsx(RoundedRect,{x:width * 0.57,y:height * 0.87,width:32,height:32,r:5,color:`${PUZZLE_COLORS[1]}50`}),_jsx(Circle,{cx:width * 0.25,cy:height * 0.25,r:18,color:`${PUZZLE_COLORS[4]}45`}),_jsx(Circle,{cx:width * 0.65,cy:height * 0.35,r:22,color:`${PUZZLE_COLORS[7]}45`}),_jsx(Circle,{cx:width * 0.35,cy:height * 0.65,r:16,color:`${PUZZLE_COLORS[10]}45`}),Array.from({length:Math.floor(width / 60)},(_,i)=> _jsx(Rect,{x:i * 60,y:0,width:1,height:height,color:"rgba(255,255,255,0.05)"},`grid-v-${i}`)),Array.from({length:Math.floor(height / 60)},(_,i)=> _jsx(Rect,{x:0,y:i * 60,width:width,height:1,color:"rgba(255,255,255,0.05)"},`grid-h-${i}`))]}),children]});});GameBackground.displayName = 'GameBackground';
1
+ "use strict";import t from"react";import{View as r,Dimensions as h}from"react-native";import{Canvas as o,LinearGradient as i,Rect as e,vec as a,Circle as c,RoundedRect as l}from"@shopify/react-native-skia";import{jsx as n,jsxs as d}from"react/jsx-runtime";const{width:s,height:g}=h.get("window"),x=["#E91E63","#F44336","#FF5722","#FF9800","#FFC107","#CDDC39","#4CAF50","#009688","#00BCD4","#2196F3","#3F51B5","#9C27B0"];export const GameBackground=t.memo(({children:h})=>{const y=t.useMemo(()=>{const t=[];for(let r=0;25>r;r++){const h=g/25*r,o=s/25*r+50*Math.sin(.5*r)+100*Math.random(),i=h+60*Math.cos(.7*r)+120*Math.random(),e=20+20*Math.random(),a=r%12,d=.3+.15*Math.sin(r),y=Math.max(e/2,Math.min(s-e,o)),w=Math.max(e/2,Math.min(g-e,i));r%4==0?t.push(n(l,{x:y,y:w,width:e,height:e,r:.2*e,color:`${x[a]}${Math.floor(255*d).toString(16).padStart(2,"0")}`},"square-tile-"+r)):r%4==1?t.push(n(l,{x:y,y:w,width:.6*e,height:e,r:.15*e,color:`${x[a]}${Math.floor(255*d).toString(16).padStart(2,"0")}`},"rect-tile-"+r)):r%4==2?t.push(n(l,{x:y,y:w,width:1.2*e,height:.8*e,r:.1*e,color:`${x[a]}${Math.floor(255*d).toString(16).padStart(2,"0")}`},"wide-tile-"+r)):t.push(n(c,{cx:y+e/2,cy:w+e/2,r:e/2,color:`${x[a]}${Math.floor(255*d).toString(16).padStart(2,"0")}`},"circle-tile-"+r))}return t},[s,g]);return d(r,{style:{flex:1},children:[d(o,{style:{position:"absolute",top:0,left:0,right:0,bottom:0,width:s,height:g},children:[n(e,{x:0,y:0,width:s,height:g,children:n(i,{start:a(0,0),end:a(s,g),colors:[x[0]+"30",x[3]+"25",x[6]+"30",x[9]+"25",x[11]+"30"]})}),n(e,{x:0,y:0,width:s,height:g,children:n(i,{start:a(0,0),end:a(s/2,g/2),colors:[x[1]+"20","transparent",x[4]+"20","transparent",x[7]+"20","transparent"]})}),n(e,{x:0,y:0,width:s,height:g,children:n(i,{start:a(s,0),end:a(0,g),colors:[x[2]+"15","transparent",x[5]+"15","transparent",x[8]+"15","transparent"]})}),y,n(l,{x:.08*s,y:.12*g,width:50,height:50,r:8,color:x[0]+"50"}),n(l,{x:.85*s,y:.82*g,width:45,height:45,r:7,color:x[3]+"50"}),n(l,{x:.82*s,y:.18*g,width:40,height:40,r:6,color:x[6]+"50"}),n(l,{x:.12*s,y:.75*g,width:55,height:55,r:9,color:x[9]+"50"}),n(l,{x:.47*s,y:.08*g,width:35,height:35,r:5,color:x[11]+"50"}),n(l,{x:.15*s,y:.47*g,width:42,height:42,r:6,color:x[5]+"50"}),n(l,{x:.75*s,y:.57*g,width:38,height:38,r:6,color:x[8]+"50"}),n(l,{x:.57*s,y:.87*g,width:32,height:32,r:5,color:x[1]+"50"}),n(c,{cx:.25*s,cy:.25*g,r:18,color:x[4]+"45"}),n(c,{cx:.65*s,cy:.35*g,r:22,color:x[7]+"45"}),n(c,{cx:.35*s,cy:.65*g,r:16,color:x[10]+"45"}),Array.from({length:Math.floor(s/60)},(t,r)=>n(e,{x:60*r,y:0,width:1,height:g,color:"rgba(255, 255, 255, 0.05)"},"grid-v-"+r)),Array.from({length:Math.floor(g/60)},(t,r)=>n(e,{x:0,y:60*r,width:s,height:1,color:"rgba(255, 255, 255, 0.05)"},"grid-h-"+r))]}),h]})});GameBackground.displayName="GameBackground";
@@ -1 +1 @@
1
- "use strict";import React,{useMemo}from 'react';import{View,StyleSheet,Dimensions}from 'react-native';import Animated,{useAnimatedStyle,withSpring,useSharedValue,withSequence}from 'react-native-reanimated';import{NumbersTile}from "./NumbersTile.js";import{useTiles,useGridSize,useEmptyPosition,useSlidingNumbersStore}from "../SlidingNumbersStore.js";import{SLIDING_NUMBERS_GAME_CONFIG,SLIDING_NUMBERS_ANIMATION_CONFIG}from "../SlidingNumbersConstants.js";import{jsx as _jsx}from "react/jsx-runtime";export const NumbersGrid = React.memo(({difficulty})=>{const tiles = useTiles();const gridSize = useGridSize();const emptyPosition = useEmptyPosition();const isPlaying = useSlidingNumbersStore(state => state.isPlaying);const isCompleted = useSlidingNumbersStore(state => state.isCompleted);const moveTile = useSlidingNumbersStore(state => state.moveTile);const checkCompletion = useSlidingNumbersStore(state => state.checkCompletion);const completionScale = useSharedValue(1);const gridDimensions = useMemo(()=>{const gap = SLIDING_NUMBERS_GAME_CONFIG.TILE_GAP;const padding =(gap + 8)* 2;const{width:screenWidth}= Dimensions.get('window');const maxAvailableWidth = screenWidth - padding - 40;const availableForTiles = maxAvailableWidth - gap *(gridSize - 1);const optimalTileSize = Math.floor(availableForTiles / gridSize);const minTileSize = 45;const maxTileSize = 80;const tileSize = Math.max(minTileSize,Math.min(maxTileSize,optimalTileSize));const tilesWidth = tileSize * gridSize;const gapsWidth = gap *(gridSize - 1);const contentSize = tilesWidth + gapsWidth;const totalSize = contentSize + padding;return{width:totalSize,height:totalSize,contentWidth:contentSize,contentHeight:contentSize,tileSize,gap};},[gridSize]);const gridLayout = useMemo(()=>{const layout = [];for(let row = 0;row < gridSize;row++){layout[row] = [];for(let col = 0;col < gridSize;col++){const rowArray = layout[row];if(rowArray){rowArray[col] = null;}}}tiles.forEach(tile =>{const{row,col}= tile.position;if(row >= 0 && row < gridSize && col >= 0 && col < gridSize && layout[row]){layout[row][col] = tile;}});return layout;},[tiles,gridSize]);const handleTilePress = React.useCallback(tileId =>{if(!isPlaying || isCompleted)return;const success = moveTile(tileId);if(success){checkCompletion(difficulty);}},[isPlaying,isCompleted,moveTile,checkCompletion,difficulty]);const isTileMovable = React.useCallback(tile =>{if(!tile || tile.value === 0 || !isPlaying || isCompleted)return false;const emptyPos = emptyPosition;const rowDiff = Math.abs(tile.position.row - emptyPos.row);const colDiff = Math.abs(tile.position.col - emptyPos.col);return rowDiff === 1 && colDiff === 0 || rowDiff === 0 && colDiff === 1;},[emptyPosition,isPlaying,isCompleted]);const animatedGridStyle = useAnimatedStyle(()=>({transform:[{scale:completionScale.value}]}));React.useEffect(()=>{if(isCompleted){completionScale.value = withSequence(withSpring(1.1,SLIDING_NUMBERS_ANIMATION_CONFIG.COMPLETION_CELEBRATION),withSpring(1,SLIDING_NUMBERS_ANIMATION_CONFIG.COMPLETION_CELEBRATION));}},[isCompleted]);const gridContainerStyle = useMemo(()=> [styles.gridContainer,{width:gridDimensions.width,height:gridDimensions.height},animatedGridStyle],[gridDimensions.width,gridDimensions.height,animatedGridStyle]);const getTileSlotStyle = React.useCallback(tileSize => [styles.tileSlot,{width:tileSize,height:tileSize}],[]);return _jsx(Animated.View,{style:gridContainerStyle,children:gridLayout.map((row,rowIndex)=> _jsx(View,{style:styles.row,children:row.map((tile,colIndex)=> _jsx(View,{style:getTileSlotStyle(gridDimensions.tileSize),children:tile && tile.value !== 0 && _jsx(NumbersTile,{tile:tile,onPress:handleTilePress,isMovable:isTileMovable(tile),tileSize:gridDimensions.tileSize})},`${rowIndex}-${colIndex}`))},rowIndex))});});const styles = StyleSheet.create({gridContainer:{backgroundColor:'#FFFFFF',borderRadius:20,padding:SLIDING_NUMBERS_GAME_CONFIG.TILE_GAP + 8,shadowColor:'#000',shadowOffset:{width:0,height:4},shadowOpacity:0.1,shadowRadius:8,elevation:8,alignSelf:'center'},row:{flexDirection:'row',justifyContent:'center',alignItems:'center'},tileSlot:{justifyContent:'center',alignItems:'center',margin:SLIDING_NUMBERS_GAME_CONFIG.TILE_GAP / 2}});NumbersGrid.displayName = 'NumbersGrid';
1
+ "use strict";import t,{useMemo as e}from"react";import{View as r,StyleSheet as i,Dimensions as o}from"react-native";import n,{useAnimatedStyle as s,withSpring as a,useSharedValue as c,withSequence as l}from"react-native-reanimated";import{NumbersTile as d}from"./NumbersTile.js";import{useTiles as h,useGridSize as m,useEmptyPosition as u,useSlidingNumbersStore as f}from"../SlidingNumbersStore.js";import{SLIDING_NUMBERS_GAME_CONFIG as g,SLIDING_NUMBERS_ANIMATION_CONFIG as w}from"../SlidingNumbersConstants.js";import{jsx as p}from"react/jsx-runtime";export const NumbersGrid=t.memo(({difficulty:i})=>{const j=h(),y=m(),S=u(),C=f(t=>t.isPlaying),F=f(t=>t.isCompleted),M=f(t=>t.moveTile),N=f(t=>t.checkCompletion),v=c(1),x=e(()=>{const t=g.TILE_GAP,e=2*(t+8),{width:r}=o.get("window"),i=Math.max(45,Math.min(80,Math.floor((r-e-40-t*(y-1))/y))),n=i*y+t*(y-1),s=n+e;return{width:s,height:s,contentWidth:n,contentHeight:n,tileSize:i,gap:t}},[y]),G=e(()=>{const t=[];for(let e=0;y>e;e++){t[e]=[];for(let r=0;y>r;r++){const i=t[e];i&&(i[r]=null)}}return j.forEach(e=>{const{row:r,col:i}=e.position;r>=0&&y>r&&i>=0&&y>i&&t[r]&&(t[r][i]=e)}),t},[j,y]),z=t.useCallback(t=>{C&&!F&&M(t)&&N(i)},[C,F,M,N,i]),I=t.useCallback(t=>{if(!t||0===t.value||!C||F)return!1;const e=S,r=Math.abs(t.position.row-e.row),i=Math.abs(t.position.col-e.col);return 1===r&&0===i||0===r&&1===i},[S,C,F]),O=s(()=>({transform:[{scale:v.value}]}));t.useEffect(()=>{F&&(v.value=l(a(1.1,w.COMPLETION_CELEBRATION),a(1,w.COMPLETION_CELEBRATION)))},[F]);const R=e(()=>[b.gridContainer,{width:x.width,height:x.height},O],[x.width,x.height,O]),$=t.useCallback(t=>[b.tileSlot,{width:t,height:t}],[]);return p(n.View,{style:R,children:G.map((t,e)=>p(r,{style:b.row,children:t.map((t,i)=>p(r,{style:$(x.tileSize),children:t&&0!==t.value&&p(d,{tile:t,onPress:z,isMovable:I(t),tileSize:x.tileSize})},`${e}-${i}`))},e))})});const b=i.create({gridContainer:{backgroundColor:"#FFFFFF",borderRadius:20,padding:g.TILE_GAP+8,shadowColor:"#000",shadowOffset:{width:0,height:4},shadowOpacity:.1,shadowRadius:8,elevation:8,alignSelf:"center"},row:{flexDirection:"row",justifyContent:"center",alignItems:"center"},tileSlot:{justifyContent:"center",alignItems:"center",margin:g.TILE_GAP/2}});NumbersGrid.displayName="NumbersGrid";
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{StyleSheet,TouchableOpacity,Text}from 'react-native';import Animated,{useSharedValue,useAnimatedStyle,withSpring,runOnJS}from 'react-native-reanimated';import{SLIDING_NUMBERS_COLORS,SLIDING_NUMBERS_GAME_CONFIG,SLIDING_NUMBERS_ANIMATION_CONFIG,NUMBER_WORDS}from "../SlidingNumbersConstants.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const AnimatedTouchableOpacity = Animated.createAnimatedComponent(TouchableOpacity);export const NumbersTile = React.memo(({tile,onPress,isMovable,tileSize})=>{const scale = useSharedValue(1);const opacity = useSharedValue(tile.value === 0 ? 0:1);const handlePress = React.useCallback(()=>{if(tile.value !== 0 && isMovable){scale.value = withSpring(0.95,SLIDING_NUMBERS_ANIMATION_CONFIG.TILE_PRESS,()=>{scale.value = withSpring(1,SLIDING_NUMBERS_ANIMATION_CONFIG.TILE_PRESS);});runOnJS(onPress)(tile.id);}},[tile.value,tile.id,isMovable,onPress,scale]);const animatedStyle = useAnimatedStyle(()=>({transform:[{scale:scale.value}],opacity:opacity.value}));if(tile.value === 0){return null;}const{tileColor,numberWord}= React.useMemo(()=>{const colorIndex =(tile.value - 1)% SLIDING_NUMBERS_COLORS.TILE_COLORS.length;const color = SLIDING_NUMBERS_COLORS.TILE_COLORS[colorIndex] || SLIDING_NUMBERS_COLORS.TILE_COLORS[0];const word = NUMBER_WORDS[tile.value] || '';return{tileColor:color,numberWord:word};},[tile.value]);const{numberFontSize,wordFontSize}= React.useMemo(()=>({numberFontSize:Math.max(16,tileSize * 0.4),wordFontSize:Math.max(8,tileSize * 0.15)}),[tileSize]);return _jsxs(AnimatedTouchableOpacity,{style:[styles.tile,{width:tileSize,height:tileSize,backgroundColor:tileColor,borderColor:tile.isCorrect ? 'transparent':'#FF4444',borderWidth:tile.isCorrect ? 0:2},animatedStyle],onPress:handlePress,activeOpacity:0.8,disabled:!isMovable || tile.value === 0,children:[_jsx(Text,{style:[styles.number,{fontSize:numberFontSize}],children:tile.value}),_jsx(Text,{style:[styles.word,{fontSize:wordFontSize}],children:numberWord})]});},(prevProps,nextProps)=>{return prevProps.tile.id === nextProps.tile.id && prevProps.tile.value === nextProps.tile.value && prevProps.tile.isCorrect === nextProps.tile.isCorrect && prevProps.tile.position.row === nextProps.tile.position.row && prevProps.tile.position.col === nextProps.tile.position.col && prevProps.isMovable === nextProps.isMovable && prevProps.tileSize === nextProps.tileSize;});const styles = StyleSheet.create({tile:{margin:SLIDING_NUMBERS_GAME_CONFIG.TILE_GAP / 2,justifyContent:'center',alignItems:'center',borderRadius:16,shadowColor:'#000',shadowOffset:{width:0,height:3},shadowOpacity:0.15,shadowRadius:6,elevation:6},number:{fontWeight:'bold',color:'#000000',textAlign:'center',marginBottom:2},word:{fontWeight:'normal',color:'#000000',textAlign:'center'}});NumbersTile.displayName = 'NumbersTile';
1
+ "use strict";import t from"react";import{StyleSheet as e,TouchableOpacity as r,Text as o}from"react-native";import i,{useSharedValue as n,useAnimatedStyle as s,withSpring as a,runOnJS as l}from"react-native-reanimated";import{SLIDING_NUMBERS_COLORS as m,SLIDING_NUMBERS_GAME_CONFIG as c,SLIDING_NUMBERS_ANIMATION_CONFIG as d,NUMBER_WORDS as u}from"../SlidingNumbersConstants.js";import{jsx as h,jsxs as b}from"react/jsx-runtime";const f=i.createAnimatedComponent(r);export const NumbersTile=t.memo(({tile:e,onPress:r,isMovable:i,tileSize:c})=>{const p=n(1),w=n(0===e.value?0:1),S=t.useCallback(()=>{0!==e.value&&i&&(p.value=a(.95,d.TILE_PRESS,()=>{p.value=a(1,d.TILE_PRESS)}),l(r)(e.id))},[e.value,e.id,i,r,p]),y=s(()=>({transform:[{scale:p.value}],opacity:w.value}));if(0===e.value)return null;const{tileColor:z,numberWord:C}=t.useMemo(()=>{const t=(e.value-1)%m.TILE_COLORS.length;return{tileColor:m.TILE_COLORS[t]||m.TILE_COLORS[0],numberWord:u[e.value]||""}},[e.value]),{numberFontSize:x,wordFontSize:F}=t.useMemo(()=>({numberFontSize:Math.max(16,.4*c),wordFontSize:Math.max(8,.15*c)}),[c]);return b(f,{style:[g.tile,{width:c,height:c,backgroundColor:z,borderColor:e.isCorrect?"transparent":"#FF4444",borderWidth:e.isCorrect?0:2},y],onPress:S,activeOpacity:.8,disabled:!i||0===e.value,children:[h(o,{style:[g.number,{fontSize:x}],children:e.value}),h(o,{style:[g.word,{fontSize:F}],children:C})]})},(t,e)=>t.tile.id===e.tile.id&&t.tile.value===e.tile.value&&t.tile.isCorrect===e.tile.isCorrect&&t.tile.position.row===e.tile.position.row&&t.tile.position.col===e.tile.position.col&&t.isMovable===e.isMovable&&t.tileSize===e.tileSize);const g=e.create({tile:{margin:c.TILE_GAP/2,justifyContent:"center",alignItems:"center",borderRadius:16,shadowColor:"#000",shadowOffset:{width:0,height:3},shadowOpacity:.15,shadowRadius:6,elevation:6},number:{fontWeight:"bold",color:"#000000",textAlign:"center",marginBottom:2},word:{fontWeight:"normal",color:"#000000",textAlign:"center"}});NumbersTile.displayName="NumbersTile";
@@ -1 +1 @@
1
- "use strict";import React,{useMemo}from 'react';import{View,Text,StyleSheet}from 'react-native';import{useTimeElapsed}from "../SlidingNumbersStore.js";import{SlidingNumbersService}from "../SlidingNumbersService.js";import{ScoreBoardContainer}from "../../../helpers/index.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";export const ScoreBoard = React.memo(({offset = 0})=>{const gameService = useMemo(()=> new SlidingNumbersService(),[]);const timeElapsed = useTimeElapsed();const formattedTime = useMemo(()=> gameService.formatTime(timeElapsed),[gameService,timeElapsed]);return _jsx(ScoreBoardContainer,{offset:offset,backgroundColor:"rgba(245,245,220,0.6)",borderColor:"rgba(255,255,255,0.3)",children:_jsx(View,{style:styles.scoreBoard,children:_jsxs(View,{style:styles.scoreSection,children:[_jsx(Text,{style:styles.scoreLabel,children:"Time Elapsed"}),_jsx(Text,{style:[styles.timeValue,{color:'#3b82f6'}],children:formattedTime})]})})});});const styles = StyleSheet.create({scoreBoard:{flexDirection:'row',justifyContent:'space-between',alignItems:'center'},scoreSection:{alignItems:'center',flex:1},scoreLabel:{fontSize:16,fontWeight:'bold',color:'#4a5568',marginBottom:4},timeValue:{fontSize:25,fontWeight:'bold'},hintText:{fontSize:18,fontWeight:'600',color:'#10b981'}});ScoreBoard.displayName = 'ScoreBoard';
1
+ "use strict";import e,{useMemo as o}from"react";import{View as r,Text as t,StyleSheet as i}from"react-native";import{useTimeElapsed as n}from"../SlidingNumbersStore.js";import{SlidingNumbersService as s}from"../SlidingNumbersService.js";import{ScoreBoardContainer as c}from"../../../helpers/index.js";import{jsx as l,jsxs as a}from"react/jsx-runtime";export const ScoreBoard=e.memo(({offset:e=0})=>{const i=o(()=>new s,[]),f=n(),d=o(()=>i.formatTime(f),[i,f]);return l(c,{offset:e,backgroundColor:"rgba(245, 245, 220, 0.6)",borderColor:"rgba(255, 255, 255, 0.3)",children:l(r,{style:m.scoreBoard,children:a(r,{style:m.scoreSection,children:[l(t,{style:m.scoreLabel,children:"Time Elapsed"}),l(t,{style:[m.timeValue,{color:"#3b82f6"}],children:d})]})})})});const m=i.create({scoreBoard:{flexDirection:"row",justifyContent:"space-between",alignItems:"center"},scoreSection:{alignItems:"center",flex:1},scoreLabel:{fontSize:16,fontWeight:"bold",color:"#4a5568",marginBottom:4},timeValue:{fontSize:25,fontWeight:"bold"},hintText:{fontSize:18,fontWeight:"600",color:"#10b981"}});ScoreBoard.displayName="ScoreBoard";
@@ -1 +1 @@
1
- "use strict";export{NumbersTile}from "./NumbersTile.js";export{NumbersGrid}from "./NumbersGrid.js";export{ScoreBoard}from "./ScoreBoard.js";export{GameBackground}from "./GameBackground.js";
1
+ "use strict";export{NumbersTile}from"./NumbersTile.js";export{NumbersGrid}from"./NumbersGrid.js";export{ScoreBoard}from"./ScoreBoard.js";export{GameBackground}from"./GameBackground.js";
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{View,StyleSheet}from 'react-native';import{GestureHandlerRootView,Gesture,GestureDetector}from 'react-native-gesture-handler';import{runOnJS}from 'react-native-reanimated';import{GameControlButton,GameOverModal,GameSettingsModal}from "../../helpers/index.js";import{DEFAULT_GAME_SETTINGS,GAME_IDS}from "../../services/UtilsService.js";import{GameBackground,GameGrid,ScoreBoard}from "./components/index.js";import{useSnakeStore,useScore,useIsPlaying,useIsGameOver}from "./SnakeStore.js";import{SNAKE_COLORS}from "./SnakeConstants.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";export const Snake = React.memo(({settings,onSettingsChange,onEndGame})=>{const score = useScore();const isPlaying = useIsPlaying();const isGameOver = useIsGameOver();const gameReportedRef = React.useRef(false);const reportGameEnd = React.useCallback(status =>{if(onEndGame && !gameReportedRef.current){onEndGame({status,score:score.toString()});gameReportedRef.current = true;}},[onEndGame,score]);const startGame = useSnakeStore(state => state.startGame);const stopGame = useSnakeStore(state => state.stopGame);const resetGame = useSnakeStore(state => state.resetGame);const initializeGame = useSnakeStore(state => state.initializeGame);const updateSettings = useSnakeStore(state => state.updateSettings);const changeDirection = useSnakeStore(state => state.changeDirection);const prevSettingsRef = React.useRef({enableSounds:true,enableHaptics:true});const prevDifficultyRef = React.useRef('easy');React.useEffect(()=>{const enableSounds = settings?.enableSounds ?? true;const enableHaptics = settings?.enableHaptics ?? true;if(prevSettingsRef.current.enableSounds !== enableSounds || prevSettingsRef.current.enableHaptics !== enableHaptics){prevSettingsRef.current ={enableSounds,enableHaptics};updateSettings(enableSounds,enableHaptics);}},[updateSettings,settings?.enableSounds,settings?.enableHaptics]);React.useEffect(()=>{const difficulty = settings?.difficulty || 'easy';if(prevDifficultyRef.current !== difficulty){prevDifficultyRef.current = difficulty;initializeGame(difficulty);}},[initializeGame,settings?.difficulty]);React.useEffect(()=>{if(isGameOver && !gameReportedRef.current){reportGameEnd('lose');}if(!isGameOver){gameReportedRef.current = false;}},[isGameOver,reportGameEnd]);const handleStartGame = React.useCallback(()=>{gameReportedRef.current = false;startGame();},[startGame]);const handleResetGame = React.useCallback(()=>{resetGame();},[resetGame]);const handleStopGame = React.useCallback(()=>{reportGameEnd('cancel');stopGame();},[stopGame,reportGameEnd]);const handleSwipe = React.useCallback(direction =>{try{if(!isPlaying)return;requestAnimationFrame(()=>{try{changeDirection(direction);}catch(error){console.error('Direction change error:',error);}});}catch(error){console.error('Swipe handler error:',error);}},[isPlaying,changeDirection]);const panGesture = React.useMemo(()=> Gesture.Pan().onEnd(event =>{const threshold = 30;const absX = Math.abs(event.translationX);const absY = Math.abs(event.translationY);if(absX > threshold || absY > threshold){if(absX > absY){if(event.translationX > 0){runOnJS(handleSwipe)('RIGHT');}else{runOnJS(handleSwipe)('LEFT');}}else{if(event.translationY > 0){runOnJS(handleSwipe)('DOWN');}else{runOnJS(handleSwipe)('UP');}}}}),[handleSwipe]);const gameControlButtonProps = React.useMemo(()=>({isPlaying,gameOver:isGameOver,onStartGame:handleStartGame,onStopGame:handleStopGame,onResetGame:handleResetGame,startButtonText:'Start Slithering!',startButtonSubtext:'Swipe to move or turn.',startButtonColor:SNAKE_COLORS.BUTTON_PRIMARY,startButtonBorderColor:SNAKE_COLORS.BUTTON_SECONDARY}),[isPlaying,isGameOver,handleStartGame,handleStopGame,handleResetGame]);const gameOverModalProps = React.useMemo(()=>({isVisible:isGameOver,score,onPlayAgain:handleResetGame,buttonText:'Play Again!',primaryColor:"rgba(0,204,106,0.5)",borderColor:"rgba(0,204,106,0.5)",buttonColor:"#ffffff",buttonBorderColor:"#ffffff",buttonTextColor:"#00cc6a",scoreFormatter:s => `${s}`}),[isGameOver,score,handleResetGame]);const gameSettingsModalProps = React.useMemo(()=>({gameId:GAME_IDS.SNAKE,settings:settings || DEFAULT_GAME_SETTINGS,onSettingsChange}),[settings,onSettingsChange]);const offset = settings?.offset ?? 0;return _jsx(GameBackground,{children:_jsxs(GestureHandlerRootView,{style:styles.container,children:[_jsx(GestureDetector,{gesture:panGesture,children:_jsx(View,{style:styles.container,children:_jsx(View,{style:[styles.gridContainer,{paddingTop:offset}],children:_jsx(GameGrid,{})})})}),_jsx(ScoreBoard,{offset:offset}),_jsx(GameControlButton,{...gameControlButtonProps}),_jsx(GameOverModal,{...gameOverModalProps}),_jsx(GameSettingsModal,{...gameSettingsModalProps})]})});});Snake.displayName = 'Snake';const styles = StyleSheet.create({container:{flex:1},gridContainer:{flex:1,justifyContent:'center',alignItems:'center'}});
1
+ "use strict";import t from"react";import{View as e,StyleSheet as r}from"react-native";import{GestureHandlerRootView as o,Gesture as n,GestureDetector as a}from"react-native-gesture-handler";import{runOnJS as s}from"react-native-reanimated";import{GameControlButton as i,GameOverModal as c,GameSettingsModal as m}from"../../helpers/index.js";import{DEFAULT_GAME_SETTINGS as l,GAME_IDS as f}from"../../services/UtilsService.js";import{GameBackground as u,GameGrid as p,ScoreBoard as d}from"./components/index.js";import{useSnakeStore as g,useScore as S,useIsPlaying as h,useIsGameOver as b}from"./SnakeStore.js";import{SNAKE_COLORS as x}from"./SnakeConstants.js";import{jsx as y,jsxs as C}from"react/jsx-runtime";export const Snake=t.memo(({settings:r,onSettingsChange:v,onEndGame:B})=>{const T=S(),k=h(),G=b(),P=t.useRef(!1),A=t.useCallback(t=>{B&&!P.current&&(B({status:t,score:T.toString()}),P.current=!0)},[B,T]),F=g(t=>t.startGame),H=g(t=>t.stopGame),I=g(t=>t.resetGame),E=g(t=>t.initializeGame),M=g(t=>t.updateSettings),O=g(t=>t.changeDirection),R=t.useRef({enableSounds:!0,enableHaptics:!0}),U=t.useRef("easy");t.useEffect(()=>{const t=r?.enableSounds??!0,e=r?.enableHaptics??!0;R.current.enableSounds===t&&R.current.enableHaptics===e||(R.current={enableSounds:t,enableHaptics:e},M(t,e))},[M,r?.enableSounds,r?.enableHaptics]),t.useEffect(()=>{const t=r?.difficulty||"easy";U.current!==t&&(U.current=t,E(t))},[E,r?.difficulty]),t.useEffect(()=>{G&&!P.current&&A("lose"),G||(P.current=!1)},[G,A]);const q=t.useCallback(()=>{P.current=!1,F()},[F]),w=t.useCallback(()=>{I()},[I]),D=t.useCallback(()=>{A("cancel"),H()},[H,A]),L=t.useCallback(t=>{try{if(!k)return;requestAnimationFrame(()=>{try{O(t)}catch(t){}})}catch(t){}},[k,O]),N=t.useMemo(()=>n.Pan().onEnd(t=>{const e=Math.abs(t.translationX),r=Math.abs(t.translationY);(e>30||r>30)&&(e>r?t.translationX>0?s(L)("RIGHT"):s(L)("LEFT"):t.translationY>0?s(L)("DOWN"):s(L)("UP"))}),[L]),V=t.useMemo(()=>({isPlaying:k,gameOver:G,onStartGame:q,onStopGame:D,onResetGame:w,startButtonText:"Start Slithering!",startButtonSubtext:"Swipe to move or turn.",startButtonColor:x.BUTTON_PRIMARY,startButtonBorderColor:x.BUTTON_SECONDARY}),[k,G,q,D,w]),W=t.useMemo(()=>({isVisible:G,score:T,onPlayAgain:w,buttonText:"Play Again!",primaryColor:"rgba(0, 204, 106, 0.5)",borderColor:"rgba(0, 204, 106, 0.5)",buttonColor:"#ffffff",buttonBorderColor:"#ffffff",buttonTextColor:"#00cc6a",scoreFormatter:t=>""+t}),[G,T,w]),z=t.useMemo(()=>({gameId:f.SNAKE,settings:r||l,onSettingsChange:v}),[r,v]),J=r?.offset??0;return y(u,{children:C(o,{style:j.container,children:[y(a,{gesture:N,children:y(e,{style:j.container,children:y(e,{style:[j.gridContainer,{paddingTop:J}],children:y(p,{})})})}),y(d,{offset:J}),y(i,{...V}),y(c,{...W}),y(m,{...z})]})})});Snake.displayName="Snake";const j=r.create({container:{flex:1},gridContainer:{flex:1,justifyContent:"center",alignItems:"center"}});
@@ -1 +1 @@
1
- "use strict";import{Dimensions}from 'react-native';const{width}= Dimensions.get('window');export const SNAKE_GAME_CONFIG ={GRID_SIZE:20,CELL_SIZE:Math.floor((width - 40)/ 20),GAME_SPEED:{easy:400,medium:300,hard:200},POINTS_PER_FOOD:10,POINTS_PER_SPECIAL_FOOD:25,DIFFICULTY_MULTIPLIER:{easy:1,medium:2,hard:3},SPECIAL_FOOD_CHANCE:0.15,SPECIAL_FOOD_DURATION:5000,INITIAL_SNAKE_LENGTH:3};export const SNAKE_COLORS ={BACKGROUND:'#252541',GRID_LINE:'rgba(255,255,255,0.15)',SNAKE_HEAD:'#00ff88',SNAKE_BODY:'#00cc6a',SNAKE_TAIL:'#00994d',SNAKE_OUTLINE:'#004d26',FOOD_NORMAL:'#ff4757',FOOD_SPECIAL:'#ffa502',FOOD_GLOW:'rgba(255,71,87,0.5)',SCORE_TEXT:'#ffffff',SCORE_BACKGROUND:'rgba(0,255,136,0.2)',BUTTON_PRIMARY:'#00cc6a',BUTTON_SECONDARY:'#00994d',MODAL_PRIMARY:'rgba(0,255,136,0.95)',MODAL_BORDER:'rgba(0,204,106,0.9)',MODAL_BUTTON:'#00ff88',MODAL_BUTTON_BORDER:'#00cc6a',COLLISION_COLOR:'#ff4757'};export const DIRECTIONS ={UP:{x:0,y:-1},DOWN:{x:0,y:1},LEFT:{x:-1,y:0},RIGHT:{x:1,y:0}};export const SNAKE_THEME ={backgroundColor:'rgba(0,0,0,0.7)',headerBackgroundColor:'#00cc6a',headerTextColor:'#ffffff',sectionBackgroundColor:'rgba(0,204,106,0.15)',sectionTitleColor:'#00cc6a',buttonSelectedColor:'#00cc6a',buttonUnselectedColor:'rgba(255,255,255,0.2)',buttonSelectedTextColor:'#ffffff',buttonUnselectedTextColor:'rgba(255,255,255,0.7)',switchTrackColorFalse:'rgba(0,204,106,0.3)',switchTrackColorTrue:'#00cc6a',switchThumbColor:'#ffffff',infoTextColor:'rgba(255,255,255,0.9)'};export const SNAKE_DIFFICULTY_DESCRIPTIONS ={easy:'Slow speed 1x points Perfect for beginners',medium:'Medium speed 2x points Balanced challenge',hard:'Fast speed 3x points Expert level'};export const SNAKE_SOUNDS ={EAT:{text:'Nom!',pitch:1.2,rate:1.5},SPECIAL:{text:'Bonus!',pitch:1.5,rate:2.0},COLLISION:{text:'Ouch!',pitch:0.8,rate:1.2},WIN:{text:'Victory!',pitch:1.6,rate:1.8},LOSE:{text:'Game Over',pitch:0.8,rate:1.2}};
1
+ "use strict";import{Dimensions as e}from"react-native";const{width:t}=e.get("window");export const SNAKE_GAME_CONFIG={GRID_SIZE:20,CELL_SIZE:Math.floor((t-40)/20),GAME_SPEED:{easy:400,medium:300,hard:200},POINTS_PER_FOOD:10,POINTS_PER_SPECIAL_FOOD:25,DIFFICULTY_MULTIPLIER:{easy:1,medium:2,hard:3},SPECIAL_FOOD_CHANCE:.15,SPECIAL_FOOD_DURATION:5e3,INITIAL_SNAKE_LENGTH:3};export const SNAKE_COLORS={BACKGROUND:"#252541",GRID_LINE:"rgba(255, 255, 255, 0.15)",SNAKE_HEAD:"#00ff88",SNAKE_BODY:"#00cc6a",SNAKE_TAIL:"#00994d",SNAKE_OUTLINE:"#004d26",FOOD_NORMAL:"#ff4757",FOOD_SPECIAL:"#ffa502",FOOD_GLOW:"rgba(255, 71, 87, 0.5)",SCORE_TEXT:"#ffffff",SCORE_BACKGROUND:"rgba(0, 255, 136, 0.2)",BUTTON_PRIMARY:"#00cc6a",BUTTON_SECONDARY:"#00994d",MODAL_PRIMARY:"rgba(0, 255, 136, 0.95)",MODAL_BORDER:"rgba(0, 204, 106, 0.9)",MODAL_BUTTON:"#00ff88",MODAL_BUTTON_BORDER:"#00cc6a",COLLISION_COLOR:"#ff4757"};export const DIRECTIONS={UP:{x:0,y:-1},DOWN:{x:0,y:1},LEFT:{x:-1,y:0},RIGHT:{x:1,y:0}};export const SNAKE_THEME={backgroundColor:"rgba(0, 0, 0, 0.7)",headerBackgroundColor:"#00cc6a",headerTextColor:"#ffffff",sectionBackgroundColor:"rgba(0, 204, 106, 0.15)",sectionTitleColor:"#00cc6a",buttonSelectedColor:"#00cc6a",buttonUnselectedColor:"rgba(255, 255, 255, 0.2)",buttonSelectedTextColor:"#ffffff",buttonUnselectedTextColor:"rgba(255, 255, 255, 0.7)",switchTrackColorFalse:"rgba(0, 204, 106, 0.3)",switchTrackColorTrue:"#00cc6a",switchThumbColor:"#ffffff",infoTextColor:"rgba(255, 255, 255, 0.9)"};export const SNAKE_DIFFICULTY_DESCRIPTIONS={easy:"Slow speed \u2022 1x points \u2022 Perfect for beginners",medium:"Medium speed \u2022 2x points \u2022 Balanced challenge",hard:"Fast speed \u2022 3x points \u2022 Expert level"};export const SNAKE_SOUNDS={EAT:{text:"Nom!",pitch:1.2,rate:1.5},SPECIAL:{text:"Bonus!",pitch:1.5,rate:2},COLLISION:{text:"Ouch!",pitch:.8,rate:1.2},WIN:{text:"Victory!",pitch:1.6,rate:1.8},LOSE:{text:"Game Over",pitch:.8,rate:1.2}};
@@ -1 +1 @@
1
- "use strict";import{SNAKE_GAME_CONFIG,DIRECTIONS}from "./SnakeConstants.js";export class SnakeService{nextId = 1;initializeSnake(){const centerX = Math.floor(SNAKE_GAME_CONFIG.GRID_SIZE / 2);const centerY = Math.floor(SNAKE_GAME_CONFIG.GRID_SIZE / 2);const snake = [];for(let i = 0;i < SNAKE_GAME_CONFIG.INITIAL_SNAKE_LENGTH;i++){snake.push({id:this.nextId++,x:centerX - i,y:centerY});}return snake;}generateFood(snake,isSpecial = false){let position;let attempts = 0;const maxAttempts = 100;do{position ={x:Math.floor(Math.random()* SNAKE_GAME_CONFIG.GRID_SIZE),y:Math.floor(Math.random()* SNAKE_GAME_CONFIG.GRID_SIZE)};attempts++;}while(attempts < maxAttempts && this.isPositionOnSnake(position,snake));const food ={id:this.nextId++,x:position.x,y:position.y,isSpecial};if(isSpecial){food.expiresAt = Date.now()+ SNAKE_GAME_CONFIG.SPECIAL_FOOD_DURATION;}return food;}isPositionOnSnake(position,snake){return snake.some(segment => segment.x === position.x && segment.y === position.y);}moveSnake(snake,direction){const head = snake[0];if(!head)return snake;const directionVector = DIRECTIONS[direction];const newHead ={id:this.nextId++,x:head.x + directionVector.x,y:head.y + directionVector.y};const newSnake = [newHead,...snake];newSnake.pop();return newSnake;}checkWallCollision(head){return head.x < 0 || head.x >= SNAKE_GAME_CONFIG.GRID_SIZE || head.y < 0 || head.y >= SNAKE_GAME_CONFIG.GRID_SIZE;}checkSelfCollision(snake){const head = snake[0];if(!head)return false;for(let i = 1;i < snake.length;i++){const segment = snake[i];if(segment && head.x === segment.x && head.y === segment.y){return true;}}return false;}checkFoodCollision(head,food){return head.x === food.x && head.y === food.y;}growSnake(snake){const tail = snake[snake.length - 1];if(!tail)return snake;const newSegment ={id:this.nextId++,x:tail.x,y:tail.y};return [...snake,newSegment];}isValidDirectionChange(currentDirection,newDirection){const opposites ={UP:'DOWN',DOWN:'UP',LEFT:'RIGHT',RIGHT:'LEFT'};return opposites[currentDirection] !== newDirection;}getOppositeDirection(direction){const opposites ={UP:'DOWN',DOWN:'UP',LEFT:'RIGHT',RIGHT:'LEFT'};return opposites[direction];}resetIdCounter(){this.nextId = 1;}isSpecialFoodExpired(food){if(!food.isSpecial || !food.expiresAt)return false;return Date.now()> food.expiresAt;}calculateScore(isSpecialFood,difficulty){const baseScore = isSpecialFood ? SNAKE_GAME_CONFIG.POINTS_PER_SPECIAL_FOOD:SNAKE_GAME_CONFIG.POINTS_PER_FOOD;const difficultyMultiplier = SNAKE_GAME_CONFIG.DIFFICULTY_MULTIPLIER[difficulty];return baseScore * difficultyMultiplier;}}
1
+ "use strict";import{SNAKE_GAME_CONFIG as t,DIRECTIONS as e}from"./SnakeConstants.js";export class SnakeService{nextId=1;initializeSnake(){const e=Math.floor(t.GRID_SIZE/2),r=Math.floor(t.GRID_SIZE/2),n=[];for(let i=0;i<t.INITIAL_SNAKE_LENGTH;i++)n.push({id:this.nextId++,x:e-i,y:r});return n}generateFood(e,r=!1){let n,i=0;do{n={x:Math.floor(Math.random()*t.GRID_SIZE),y:Math.floor(Math.random()*t.GRID_SIZE)},i++}while(100>i&&this.isPositionOnSnake(n,e));const o={id:this.nextId++,x:n.x,y:n.y,isSpecial:r};return r&&(o.expiresAt=Date.now()+t.SPECIAL_FOOD_DURATION),o}isPositionOnSnake(t,e){return e.some(e=>e.x===t.x&&e.y===t.y)}moveSnake(t,r){const n=t[0];if(!n)return t;const i=e[r],o=[{id:this.nextId++,x:n.x+i.x,y:n.y+i.y},...t];return o.pop(),o}checkWallCollision(e){return 0>e.x||e.x>=t.GRID_SIZE||0>e.y||e.y>=t.GRID_SIZE}checkSelfCollision(t){const e=t[0];if(!e)return!1;for(let r=1;r<t.length;r++){const n=t[r];if(n&&e.x===n.x&&e.y===n.y)return!0}return!1}checkFoodCollision(t,e){return t.x===e.x&&t.y===e.y}growSnake(t){const e=t[t.length-1];if(!e)return t;const r={id:this.nextId++,x:e.x,y:e.y};return[...t,r]}isValidDirectionChange(t,e){return{UP:"DOWN",DOWN:"UP",LEFT:"RIGHT",RIGHT:"LEFT"}[t]!==e}getOppositeDirection(t){return{UP:"DOWN",DOWN:"UP",LEFT:"RIGHT",RIGHT:"LEFT"}[t]}resetIdCounter(){this.nextId=1}isSpecialFoodExpired(t){return!(!t.isSpecial||!t.expiresAt)&&Date.now()>t.expiresAt}calculateScore(e,r){return(e?t.POINTS_PER_SPECIAL_FOOD:t.POINTS_PER_FOOD)*t.DIFFICULTY_MULTIPLIER[r]}}
@@ -1 +1 @@
1
- "use strict";import{create}from 'zustand';import{subscribeWithSelector}from 'zustand/middleware';import{immerMiddleware}from "../../services/UtilsService.js";import{SnakeService}from "./SnakeService.js";import{SNAKE_GAME_CONFIG,SNAKE_SOUNDS}from "./SnakeConstants.js";import{playSound}from "../../services/SoundsService.js";import{playHaptic,HapticType}from "../../services/HapticsService.js";const gameService = new SnakeService();export const useSnakeStore = create()(subscribeWithSelector(immerMiddleware((set,get)=>({snake:gameService.initializeSnake(),food:null,direction:'RIGHT',nextDirection:'RIGHT',score:0,timeLeft:300,isPlaying:false,isGameOver:false,isPaused:false,difficulty:'easy',enableSounds:true,enableHaptics:true,moveTimer:null,countdownTimer:null,startGame:()=>{const state = get();if(state.moveTimer)clearInterval(state.moveTimer);if(state.countdownTimer)clearInterval(state.countdownTimer);const initialFood = gameService.generateFood(state.snake,false);const gameSpeed = SNAKE_GAME_CONFIG.GAME_SPEED[state.difficulty];const moveTimer = setInterval(()=>{const currentState = get();if(currentState.isPlaying && !currentState.isPaused){currentState.gameLoop();}},gameSpeed);const countdownTimer = setInterval(()=>{const currentState = get();if(currentState.isPlaying && !currentState.isPaused){currentState.decrementTime();}},1000);set(state =>{state.isPlaying = true;state.isPaused = false;state.food = initialFood;state.moveTimer = moveTimer;state.countdownTimer = countdownTimer;});},stopGame:()=>{const state = get();if(state.moveTimer)clearInterval(state.moveTimer);if(state.countdownTimer)clearInterval(state.countdownTimer);playSound(SNAKE_SOUNDS.LOSE,state.enableSounds);playHaptic(HapticType.MEDIUM,state.enableHaptics);state.initializeGame(state.difficulty);},pauseGame:()=>{set(state =>{state.isPaused = true;});},resumeGame:()=>{set(state =>{state.isPaused = false;});},resetGame:()=>{const state = get();state.initializeGame(state.difficulty);},initializeGame:(difficulty = 'easy')=>{const state = get();if(state.moveTimer)clearInterval(state.moveTimer);if(state.countdownTimer)clearInterval(state.countdownTimer);gameService.resetIdCounter();const newSnake = gameService.initializeSnake();const timeLimit = difficulty === 'easy' ? 300:600;set(state =>{state.snake = newSnake;state.food = null;state.direction = 'RIGHT';state.nextDirection = 'RIGHT';state.score = 0;state.timeLeft = timeLimit;state.isPlaying = false;state.isGameOver = false;state.isPaused = false;state.difficulty = difficulty;state.moveTimer = null;state.countdownTimer = null;});},changeDirection:newDirection =>{const state = get();if(!state.isPlaying || state.isPaused)return;if(gameService.isValidDirectionChange(state.direction,newDirection)){set(state =>{state.nextDirection = newDirection;});}},gameLoop:()=>{const state = get();if(!state.isPlaying || state.isPaused || state.isGameOver)return;set(draft =>{draft.direction = draft.nextDirection;const newSnake = gameService.moveSnake(draft.snake,draft.direction);const head = newSnake[0];if(!head)return;if(gameService.checkWallCollision(head)){draft.isGameOver = true;draft.isPlaying = false;if(draft.moveTimer){clearInterval(draft.moveTimer);draft.moveTimer = null;}if(draft.countdownTimer){clearInterval(draft.countdownTimer);draft.countdownTimer = null;}playSound(SNAKE_SOUNDS.COLLISION,draft.enableSounds);playHaptic(HapticType.HEAVY,draft.enableHaptics);return;}if(gameService.checkSelfCollision(newSnake)){draft.isGameOver = true;draft.isPlaying = false;if(draft.moveTimer){clearInterval(draft.moveTimer);draft.moveTimer = null;}if(draft.countdownTimer){clearInterval(draft.countdownTimer);draft.countdownTimer = null;}playSound(SNAKE_SOUNDS.COLLISION,draft.enableSounds);playHaptic(HapticType.HEAVY,draft.enableHaptics);return;}if(draft.food && gameService.checkFoodCollision(head,draft.food)){const grownSnake = gameService.growSnake(newSnake);draft.snake = grownSnake;const points = gameService.calculateScore(draft.food.isSpecial,draft.difficulty);draft.score += points;const sound = draft.food.isSpecial ? SNAKE_SOUNDS.SPECIAL:SNAKE_SOUNDS.EAT;playSound(sound,draft.enableSounds);playHaptic(draft.food.isSpecial ? HapticType.HEAVY:HapticType.MEDIUM,draft.enableHaptics);const isSpecial = Math.random()< SNAKE_GAME_CONFIG.SPECIAL_FOOD_CHANCE;draft.food = gameService.generateFood(grownSnake,isSpecial);}else{draft.snake = newSnake;}if(draft.food && gameService.isSpecialFoodExpired(draft.food)){draft.food = gameService.generateFood(draft.snake,false);}});},updateSettings:(enableSounds,enableHaptics)=>{set(state =>{state.enableSounds = enableSounds;state.enableHaptics = enableHaptics;});},decrementTime:()=>{set(state =>{if(state.timeLeft > 0){state.timeLeft--;}else{state.isGameOver = true;state.isPlaying = false;if(state.moveTimer){clearInterval(state.moveTimer);state.moveTimer = null;}if(state.countdownTimer){clearInterval(state.countdownTimer);state.countdownTimer = null;}}});}}))));export const useSnake =()=> useSnakeStore(state => state.snake);export const useFood =()=> useSnakeStore(state => state.food);export const useScore =()=> useSnakeStore(state => state.score);export const useTimeLeft =()=> useSnakeStore(state => state.timeLeft);export const useIsPlaying =()=> useSnakeStore(state => state.isPlaying);export const useIsGameOver =()=> useSnakeStore(state => state.isGameOver);export const useIsPaused =()=> useSnakeStore(state => state.isPaused);
1
+ "use strict";import{create as e}from"zustand";import{subscribeWithSelector as t}from"zustand/middleware";import{immerMiddleware as n}from"../../services/UtilsService.js";import{SnakeService as s}from"./SnakeService.js";import{SNAKE_GAME_CONFIG as r,SNAKE_SOUNDS as o}from"./SnakeConstants.js";import{playSound as a}from"../../services/SoundsService.js";import{playHaptic as l,HapticType as c}from"../../services/HapticsService.js";const i=new s;export const useSnakeStore=e()(t(n((e,t)=>({snake:i.initializeSnake(),food:null,direction:"RIGHT",nextDirection:"RIGHT",score:0,timeLeft:300,isPlaying:!1,isGameOver:!1,isPaused:!1,difficulty:"easy",enableSounds:!0,enableHaptics:!0,moveTimer:null,countdownTimer:null,startGame:()=>{const n=t();n.moveTimer&&clearInterval(n.moveTimer),n.countdownTimer&&clearInterval(n.countdownTimer);const s=i.generateFood(n.snake,!1),o=r.GAME_SPEED[n.difficulty],a=setInterval(()=>{const e=t();e.isPlaying&&!e.isPaused&&e.gameLoop()},o),l=setInterval(()=>{const e=t();e.isPlaying&&!e.isPaused&&e.decrementTime()},1e3);e(e=>{e.isPlaying=!0,e.isPaused=!1,e.food=s,e.moveTimer=a,e.countdownTimer=l})},stopGame:()=>{const e=t();e.moveTimer&&clearInterval(e.moveTimer),e.countdownTimer&&clearInterval(e.countdownTimer),a(o.LOSE,e.enableSounds),l(c.MEDIUM,e.enableHaptics),e.initializeGame(e.difficulty)},pauseGame:()=>{e(e=>{e.isPaused=!0})},resumeGame:()=>{e(e=>{e.isPaused=!1})},resetGame:()=>{const e=t();e.initializeGame(e.difficulty)},initializeGame:(n="easy")=>{const s=t();s.moveTimer&&clearInterval(s.moveTimer),s.countdownTimer&&clearInterval(s.countdownTimer),i.resetIdCounter();const r=i.initializeSnake(),o="easy"===n?300:600;e(e=>{e.snake=r,e.food=null,e.direction="RIGHT",e.nextDirection="RIGHT",e.score=0,e.timeLeft=o,e.isPlaying=!1,e.isGameOver=!1,e.isPaused=!1,e.difficulty=n,e.moveTimer=null,e.countdownTimer=null})},changeDirection:n=>{const s=t();s.isPlaying&&!s.isPaused&&i.isValidDirectionChange(s.direction,n)&&e(e=>{e.nextDirection=n})},gameLoop:()=>{const n=t();!n.isPlaying||n.isPaused||n.isGameOver||e(e=>{e.direction=e.nextDirection;const t=i.moveSnake(e.snake,e.direction),n=t[0];if(n){if(i.checkWallCollision(n))return e.isGameOver=!0,e.isPlaying=!1,e.moveTimer&&(clearInterval(e.moveTimer),e.moveTimer=null),e.countdownTimer&&(clearInterval(e.countdownTimer),e.countdownTimer=null),a(o.COLLISION,e.enableSounds),void l(c.HEAVY,e.enableHaptics);if(i.checkSelfCollision(t))return e.isGameOver=!0,e.isPlaying=!1,e.moveTimer&&(clearInterval(e.moveTimer),e.moveTimer=null),e.countdownTimer&&(clearInterval(e.countdownTimer),e.countdownTimer=null),a(o.COLLISION,e.enableSounds),void l(c.HEAVY,e.enableHaptics);if(e.food&&i.checkFoodCollision(n,e.food)){const n=i.growSnake(t);e.snake=n;const s=i.calculateScore(e.food.isSpecial,e.difficulty);e.score+=s;const u=e.food.isSpecial?o.SPECIAL:o.EAT;a(u,e.enableSounds),l(e.food.isSpecial?c.HEAVY:c.MEDIUM,e.enableHaptics);const m=Math.random()<r.SPECIAL_FOOD_CHANCE;e.food=i.generateFood(n,m)}else e.snake=t;e.food&&i.isSpecialFoodExpired(e.food)&&(e.food=i.generateFood(e.snake,!1))}})},updateSettings:(t,n)=>{e(e=>{e.enableSounds=t,e.enableHaptics=n})},decrementTime:()=>{e(e=>{e.timeLeft>0?e.timeLeft--:(e.isGameOver=!0,e.isPlaying=!1,e.moveTimer&&(clearInterval(e.moveTimer),e.moveTimer=null),e.countdownTimer&&(clearInterval(e.countdownTimer),e.countdownTimer=null))})}}))));export const useSnake=()=>useSnakeStore(e=>e.snake);export const useFood=()=>useSnakeStore(e=>e.food);export const useScore=()=>useSnakeStore(e=>e.score);export const useTimeLeft=()=>useSnakeStore(e=>e.timeLeft);export const useIsPlaying=()=>useSnakeStore(e=>e.isPlaying);export const useIsGameOver=()=>useSnakeStore(e=>e.isGameOver);export const useIsPaused=()=>useSnakeStore(e=>e.isPaused);
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{Canvas,Rect,LinearGradient,vec,Circle,RoundedRect}from '@shopify/react-native-skia';import{Dimensions,View}from 'react-native';import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const{width,height}= Dimensions.get('window');const SNAKE_NEON_COLORS = ['#00ff88','#00ffaa','#00cc6a','#00dd77','#00ff99','#00ee77','#00bb55','#00aa44' ];export const GameBackground = React.memo(({children})=>{const generateFloatingElements = React.useMemo(()=>{const elements = [];const elementCount = 20;for(let i = 0;i < elementCount;i++){const baseX = width / elementCount * i;const baseY = height / elementCount * i;const x = baseX + Math.sin(i * 0.5)* 60 + Math.random()* 80;const y = baseY + Math.cos(i * 0.7)* 70 + Math.random()* 100;const size = 15 + Math.random()* 25;const colorIndex = i % SNAKE_NEON_COLORS.length;const opacity = 0.15 + Math.sin(i)* 0.1;const elementX = Math.max(size / 2,Math.min(width - size,x));const elementY = Math.max(size / 2,Math.min(height - size,y));if(i % 3 === 0){elements.push(_jsx(Circle,{cx:elementX + size / 2,cy:elementY + size / 2,r:size / 2,color:`${SNAKE_NEON_COLORS[colorIndex]}${Math.floor(opacity * 255).toString(16).padStart(2,'0')}`},`circle-${i}`));}else{elements.push(_jsx(RoundedRect,{x:elementX,y:elementY,width:size,height:size,r:size * 0.3,color:`${SNAKE_NEON_COLORS[colorIndex]}${Math.floor(opacity * 255).toString(16).padStart(2,'0')}`},`rect-${i}`));}}return elements;},[]);return _jsxs(View,{style:{flex:1},children:[_jsxs(Canvas,{style:{position:'absolute',top:0,left:0,right:0,bottom:0,width,height},children:[_jsx(Rect,{x:0,y:0,width:width,height:height,children:_jsx(LinearGradient,{start:vec(0,0),end:vec(width,height),colors:['#1a1a2e','#252541','#1e3a3a','#1a2e1a','#1a1a2e' ]})}),_jsx(Rect,{x:0,y:0,width:width,height:height,children:_jsx(LinearGradient,{start:vec(0,0),end:vec(width / 2,height / 2),colors:[`${SNAKE_NEON_COLORS[0]}15`,'transparent',`${SNAKE_NEON_COLORS[1]}10`,'transparent',`${SNAKE_NEON_COLORS[4]}15`,'transparent']})}),_jsx(Rect,{x:0,y:0,width:width,height:height,children:_jsx(LinearGradient,{start:vec(width,0),end:vec(0,height),colors:[`${SNAKE_NEON_COLORS[2]}12`,'transparent',`${SNAKE_NEON_COLORS[5]}10`,'transparent',`${SNAKE_NEON_COLORS[3]}12`,'transparent']})}),generateFloatingElements,_jsx(Circle,{cx:width * 0.12,cy:height * 0.15,r:35,color:`${SNAKE_NEON_COLORS[0]}30`}),_jsx(RoundedRect,{x:width * 0.82,y:height * 0.78,width:45,height:45,r:12,color:`${SNAKE_NEON_COLORS[1]}35`}),_jsx(Circle,{cx:width * 0.85,cy:height * 0.2,r:28,color:`${SNAKE_NEON_COLORS[2]}30`}),_jsx(RoundedRect,{x:width * 0.08,y:height * 0.75,width:50,height:50,r:15,color:`${SNAKE_NEON_COLORS[3]}35`}),_jsx(Circle,{cx:width * 0.5,cy:height * 0.1,r:25,color:`${SNAKE_NEON_COLORS[4]}28`}),_jsx(RoundedRect,{x:width * 0.1,y:height * 0.45,width:40,height:40,r:10,color:`${SNAKE_NEON_COLORS[5]}32`}),_jsx(Circle,{cx:width * 0.88,cy:height * 0.55,r:30,color:`${SNAKE_NEON_COLORS[6]}30`}),_jsx(RoundedRect,{x:width * 0.55,y:height * 0.85,width:35,height:35,r:8,color:`${SNAKE_NEON_COLORS[7]}33`}),_jsx(Circle,{cx:width * 0.3,cy:height * 0.3,r:20,color:`${SNAKE_NEON_COLORS[0]}25`}),_jsx(Circle,{cx:width * 0.7,cy:height * 0.4,r:24,color:`${SNAKE_NEON_COLORS[1]}28`}),_jsx(Circle,{cx:width * 0.4,cy:height * 0.65,r:18,color:`${SNAKE_NEON_COLORS[2]}26`}),Array.from({length:Math.floor(width / 70)},(_,i)=> _jsx(Rect,{x:i * 70,y:0,width:1,height:height,color:"rgba(0,255,136,0.04)"},`grid-v-${i}`)),Array.from({length:Math.floor(height / 70)},(_,i)=> _jsx(Rect,{x:0,y:i * 70,width:width,height:1,color:"rgba(0,255,136,0.04)"},`grid-h-${i}`))]}),children]});});GameBackground.displayName = 'GameBackground';
1
+ "use strict";import t from"react";import{Canvas as r,Rect as a,LinearGradient as o,vec as h,Circle as c,RoundedRect as e}from"@shopify/react-native-skia";import{Dimensions as i,View as n}from"react-native";import{jsx as l,jsxs as s}from"react/jsx-runtime";const{width:d,height:x}=i.get("window"),y=["#00ff88","#00ffaa","#00cc6a","#00dd77","#00ff99","#00ee77","#00bb55","#00aa44"];export const GameBackground=t.memo(({children:i})=>{const g=t.useMemo(()=>{const t=[];for(let r=0;20>r;r++){const a=x/20*r,o=d/20*r+60*Math.sin(.5*r)+80*Math.random(),h=a+70*Math.cos(.7*r)+100*Math.random(),i=15+25*Math.random(),n=r%8,s=.15+.1*Math.sin(r),g=Math.max(i/2,Math.min(d-i,o)),f=Math.max(i/2,Math.min(x-i,h));r%3==0?t.push(l(c,{cx:g+i/2,cy:f+i/2,r:i/2,color:`${y[n]}${Math.floor(255*s).toString(16).padStart(2,"0")}`},"circle-"+r)):t.push(l(e,{x:g,y:f,width:i,height:i,r:.3*i,color:`${y[n]}${Math.floor(255*s).toString(16).padStart(2,"0")}`},"rect-"+r))}return t},[]);return s(n,{style:{flex:1},children:[s(r,{style:{position:"absolute",top:0,left:0,right:0,bottom:0,width:d,height:x},children:[l(a,{x:0,y:0,width:d,height:x,children:l(o,{start:h(0,0),end:h(d,x),colors:["#1a1a2e","#252541","#1e3a3a","#1a2e1a","#1a1a2e"]})}),l(a,{x:0,y:0,width:d,height:x,children:l(o,{start:h(0,0),end:h(d/2,x/2),colors:[y[0]+"15","transparent",y[1]+"10","transparent",y[4]+"15","transparent"]})}),l(a,{x:0,y:0,width:d,height:x,children:l(o,{start:h(d,0),end:h(0,x),colors:[y[2]+"12","transparent",y[5]+"10","transparent",y[3]+"12","transparent"]})}),g,l(c,{cx:.12*d,cy:.15*x,r:35,color:y[0]+"30"}),l(e,{x:.82*d,y:.78*x,width:45,height:45,r:12,color:y[1]+"35"}),l(c,{cx:.85*d,cy:.2*x,r:28,color:y[2]+"30"}),l(e,{x:.08*d,y:.75*x,width:50,height:50,r:15,color:y[3]+"35"}),l(c,{cx:.5*d,cy:.1*x,r:25,color:y[4]+"28"}),l(e,{x:.1*d,y:.45*x,width:40,height:40,r:10,color:y[5]+"32"}),l(c,{cx:.88*d,cy:.55*x,r:30,color:y[6]+"30"}),l(e,{x:.55*d,y:.85*x,width:35,height:35,r:8,color:y[7]+"33"}),l(c,{cx:.3*d,cy:.3*x,r:20,color:y[0]+"25"}),l(c,{cx:.7*d,cy:.4*x,r:24,color:y[1]+"28"}),l(c,{cx:.4*d,cy:.65*x,r:18,color:y[2]+"26"}),Array.from({length:Math.floor(d/70)},(t,r)=>l(a,{x:70*r,y:0,width:1,height:x,color:"rgba(0, 255, 136, 0.04)"},"grid-v-"+r)),Array.from({length:Math.floor(x/70)},(t,r)=>l(a,{x:0,y:70*r,width:d,height:1,color:"rgba(0, 255, 136, 0.04)"},"grid-h-"+r))]}),i]})});GameBackground.displayName="GameBackground";
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{View,StyleSheet}from 'react-native';import{Canvas,RoundedRect,Circle,Group,Shadow}from '@shopify/react-native-skia';import{useSnake,useFood}from "../SnakeStore.js";import{SNAKE_GAME_CONFIG,SNAKE_COLORS}from "../SnakeConstants.js";import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from "react/jsx-runtime";export const GameGrid = React.memo(()=>{const snake = useSnake();const food = useFood();const cellSize = SNAKE_GAME_CONFIG.CELL_SIZE;const gridSize = SNAKE_GAME_CONFIG.GRID_SIZE;const canvasSize = cellSize * gridSize;const gridLines = React.useMemo(()=>{const lines = [];for(let i = 0;i <= gridSize;i++){lines.push(i);}return lines;},[gridSize]);return _jsx(View,{style:styles.container,children:_jsx(View,{style:[styles.gridContainer,{width:canvasSize,height:canvasSize}],children:_jsx(Canvas,{style:{width:canvasSize,height:canvasSize},children:_jsxs(Group,{children:[_jsx(RoundedRect,{x:0,y:0,width:canvasSize,height:canvasSize,r:15,color:SNAKE_COLORS.BACKGROUND,children:_jsx(Shadow,{dx:0,dy:4,blur:8,color:"rgba(0,0,0,0.3)"})}),gridLines.map(i =>{const xPos = i === gridSize ? canvasSize - 2:i * cellSize;return _jsx(RoundedRect,{x:xPos,y:0,width:i === 0 || i === gridSize ? 2:1,height:canvasSize,r:0,color:SNAKE_COLORS.GRID_LINE},`v-${i}`);}),gridLines.map(i =>{const yPos = i === gridSize ? canvasSize - 2:i * cellSize;return _jsx(RoundedRect,{x:0,y:yPos,width:canvasSize,height:i === 0 || i === gridSize ? 2:1,r:0,color:SNAKE_COLORS.GRID_LINE},`h-${i}`);}),food && _jsxs(Group,{children:[_jsx(Circle,{cx:food.x * cellSize + cellSize / 2,cy:food.y * cellSize + cellSize / 2,r:cellSize * 0.6,color:food.isSpecial ? 'rgba(255,165,2,0.3)':SNAKE_COLORS.FOOD_GLOW}),_jsx(Circle,{cx:food.x * cellSize + cellSize / 2,cy:food.y * cellSize + cellSize / 2,r:cellSize * 0.4,color:food.isSpecial ? SNAKE_COLORS.FOOD_SPECIAL:SNAKE_COLORS.FOOD_NORMAL,children:_jsx(Shadow,{dx:0,dy:2,blur:4,color:"rgba(0,0,0,0.4)"})}),_jsx(Circle,{cx:food.x * cellSize + cellSize / 2 - cellSize * 0.1,cy:food.y * cellSize + cellSize / 2 - cellSize * 0.1,r:cellSize * 0.15,color:"rgba(255,255,255,0.6)"})]}),snake.map((segment,index)=>{const isHead = index === 0;const isTail = index === snake.length - 1;const segmentColor = isHead ? SNAKE_COLORS.SNAKE_HEAD:isTail ? SNAKE_COLORS.SNAKE_TAIL:SNAKE_COLORS.SNAKE_BODY;return _jsxs(Group,{children:[_jsx(RoundedRect,{x:segment.x * cellSize + 2,y:segment.y * cellSize + 2,width:cellSize - 4,height:cellSize - 4,r:isHead ? 8:6,color:segmentColor,children:_jsx(Shadow,{dx:0,dy:2,blur:4,color:"rgba(0,0,0,0.3)"})}),_jsx(RoundedRect,{x:segment.x * cellSize + cellSize * 0.2,y:segment.y * cellSize + cellSize * 0.2,width:cellSize * 0.3,height:cellSize * 0.3,r:4,color:"rgba(255,255,255,0.3)"}),isHead && _jsxs(_Fragment,{children:[_jsx(Circle,{cx:segment.x * cellSize + cellSize * 0.35,cy:segment.y * cellSize + cellSize * 0.4,r:cellSize * 0.08,color:"#000000"}),_jsx(Circle,{cx:segment.x * cellSize + cellSize * 0.65,cy:segment.y * cellSize + cellSize * 0.4,r:cellSize * 0.08,color:"#000000"})]})]},segment.id);})]})})})});});GameGrid.displayName = 'GameGrid';const styles = StyleSheet.create({container:{flex:1,justifyContent:'center',alignItems:'center'},gridContainer:{}});
1
+ "use strict";import r from"react";import{View as t,StyleSheet as o}from"react-native";import{Canvas as e,RoundedRect as c,Circle as i,Group as n,Shadow as h}from"@shopify/react-native-skia";import{useSnake as l,useFood as d}from"../SnakeStore.js";import{SNAKE_GAME_CONFIG as a,SNAKE_COLORS as s}from"../SnakeConstants.js";import{jsx as m,jsxs as x,Fragment as y}from"react/jsx-runtime";export const GameGrid=r.memo(()=>{const o=l(),f=d(),b=a.CELL_SIZE,u=a.GRID_SIZE,p=b*u,w=r.useMemo(()=>{const r=[];for(let t=0;u>=t;t++)r.push(t);return r},[u]);return m(t,{style:g.container,children:m(t,{style:[g.gridContainer,{width:p,height:p}],children:m(e,{style:{width:p,height:p},children:x(n,{children:[m(c,{x:0,y:0,width:p,height:p,r:15,color:s.BACKGROUND,children:m(h,{dx:0,dy:4,blur:8,color:"rgba(0, 0, 0, 0.3)"})}),w.map(r=>m(c,{x:r===u?p-2:r*b,y:0,width:0===r||r===u?2:1,height:p,r:0,color:s.GRID_LINE},"v-"+r)),w.map(r=>m(c,{x:0,y:r===u?p-2:r*b,width:p,height:0===r||r===u?2:1,r:0,color:s.GRID_LINE},"h-"+r)),f&&x(n,{children:[m(i,{cx:f.x*b+b/2,cy:f.y*b+b/2,r:.6*b,color:f.isSpecial?"rgba(255, 165, 2, 0.3)":s.FOOD_GLOW}),m(i,{cx:f.x*b+b/2,cy:f.y*b+b/2,r:.4*b,color:f.isSpecial?s.FOOD_SPECIAL:s.FOOD_NORMAL,children:m(h,{dx:0,dy:2,blur:4,color:"rgba(0, 0, 0, 0.4)"})}),m(i,{cx:f.x*b+b/2-.1*b,cy:f.y*b+b/2-.1*b,r:.15*b,color:"rgba(255, 255, 255, 0.6)"})]}),o.map((r,t)=>{const e=0===t,l=t===o.length-1,d=e?s.SNAKE_HEAD:l?s.SNAKE_TAIL:s.SNAKE_BODY;return x(n,{children:[m(c,{x:r.x*b+2,y:r.y*b+2,width:b-4,height:b-4,r:e?8:6,color:d,children:m(h,{dx:0,dy:2,blur:4,color:"rgba(0, 0, 0, 0.3)"})}),m(c,{x:r.x*b+.2*b,y:r.y*b+.2*b,width:.3*b,height:.3*b,r:4,color:"rgba(255, 255, 255, 0.3)"}),e&&x(y,{children:[m(i,{cx:r.x*b+.35*b,cy:r.y*b+.4*b,r:.08*b,color:"#000000"}),m(i,{cx:r.x*b+.65*b,cy:r.y*b+.4*b,r:.08*b,color:"#000000"})]})]},r.id)})]})})})})});GameGrid.displayName="GameGrid";const g=o.create({container:{flex:1,justifyContent:"center",alignItems:"center"},gridContainer:{}});
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{View,Text,StyleSheet}from 'react-native';import{useScore,useTimeLeft}from "../SnakeStore.js";import{SNAKE_COLORS}from "../SnakeConstants.js";import{ScoreBoardContainer}from "../../../helpers/index.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";export const ScoreBoard = React.memo(({offset = 0})=>{const score = useScore();const timeLeft = useTimeLeft();const formattedTime = React.useMemo(()=>{const minutes = Math.floor(timeLeft / 60);const seconds = timeLeft % 60;return `${minutes}:${seconds.toString().padStart(2,'0')}`;},[timeLeft]);return _jsx(ScoreBoardContainer,{offset:offset,backgroundColor:SNAKE_COLORS.SCORE_BACKGROUND,borderColor:SNAKE_COLORS.SCORE_BACKGROUND,children:_jsxs(View,{style:styles.scoreContainer,children:[_jsxs(View,{style:styles.scoreItem,children:[_jsx(Text,{style:styles.label,children:"Score"}),_jsx(Text,{style:styles.value,children:score})]}),_jsxs(View,{style:styles.scoreItem,children:[_jsx(Text,{style:styles.label,children:"Time"}),_jsx(Text,{style:styles.value,children:formattedTime})]})]})});});ScoreBoard.displayName = 'ScoreBoard';const styles = StyleSheet.create({scoreContainer:{flex:1,flexDirection:'row',justifyContent:'space-around',alignItems:'center'},scoreItem:{alignItems:'center'},label:{fontSize:16,color:'rgba(255,255,255,0.7)',marginBottom:4},value:{fontSize:25,color:SNAKE_COLORS.SCORE_TEXT,fontWeight:'bold'}});
1
+ "use strict";import e from"react";import{View as r,Text as o,StyleSheet as t}from"react-native";import{useScore as n,useTimeLeft as s}from"../SnakeStore.js";import{SNAKE_COLORS as i}from"../SnakeConstants.js";import{ScoreBoardContainer as c}from"../../../helpers/index.js";import{jsx as l,jsxs as a}from"react/jsx-runtime";export const ScoreBoard=e.memo(({offset:t=0})=>{const d=n(),f=s(),h=e.useMemo(()=>`${Math.floor(f/60)}:${(""+f%60).padStart(2,"0")}`,[f]);return l(c,{offset:t,backgroundColor:i.SCORE_BACKGROUND,borderColor:i.SCORE_BACKGROUND,children:a(r,{style:m.scoreContainer,children:[a(r,{style:m.scoreItem,children:[l(o,{style:m.label,children:"Score"}),l(o,{style:m.value,children:d})]}),a(r,{style:m.scoreItem,children:[l(o,{style:m.label,children:"Time"}),l(o,{style:m.value,children:h})]})]})})});ScoreBoard.displayName="ScoreBoard";const m=t.create({scoreContainer:{flex:1,flexDirection:"row",justifyContent:"space-around",alignItems:"center"},scoreItem:{alignItems:"center"},label:{fontSize:16,color:"rgba(255, 255, 255, 0.7)",marginBottom:4},value:{fontSize:25,color:i.SCORE_TEXT,fontWeight:"bold"}});
@@ -1 +1 @@
1
- "use strict";export{GameBackground}from "./GameBackground.js";export{GameGrid}from "./GameGrid.js";export{ScoreBoard}from "./ScoreBoard.js";
1
+ "use strict";export{GameBackground}from"./GameBackground.js";export{GameGrid}from"./GameGrid.js";export{ScoreBoard}from"./ScoreBoard.js";
@@ -1 +1 @@
1
- "use strict";export{Snake}from "./Snake.js";export{useSnakeStore}from "./SnakeStore.js";export * from "./SnakeConstants.js";
1
+ "use strict";export{Snake}from"./Snake.js";export{useSnakeStore}from"./SnakeStore.js";export*from"./SnakeConstants.js";
@@ -1 +1 @@
1
- "use strict";import React,{useEffect,useCallback,useMemo}from 'react';import{View,StyleSheet,Dimensions}from 'react-native';import{GestureHandlerRootView}from 'react-native-gesture-handler';import{useSpaceFighterStore}from "./SpaceFighterStore.js";import{createSpaceFighterService}from "./SpaceFighterService.js";import{getDifficultySettings}from "./SpaceFighterConstants.js";import{ScoreBoard,GameArea,GameBackground}from "./components/index.js";import{GameControlButton,GameOverModal,useGameErrorHandler,GameErrorType}from "../../helpers/index.js";import{GameSettingsModal}from "../../helpers/index.js";import{GAME_IDS,DEFAULT_GAME_SETTINGS}from "../../services/UtilsService.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const{width,height}= Dimensions.get('window');export const SpaceFighter =({settings = DEFAULT_GAME_SETTINGS,onSettingsChange,onEndGame})=>{const{difficulty}= useMemo(()=> settings || DEFAULT_GAME_SETTINGS,[settings]);const isPlaying = useSpaceFighterStore(state => state.isPlaying);const gameOver = useSpaceFighterStore(state => state.gameOver);const score = useSpaceFighterStore(state => state.score);const gameReportedRef = React.useRef(false);const reportGameEnd = useCallback(status =>{if(onEndGame && !gameReportedRef.current){const currentState = useSpaceFighterStore.getState();onEndGame({status,score:currentState.score.toString()});gameReportedRef.current = true;}},[onEndGame]);const setOffset = useSpaceFighterStore(state => state.setOffset);const startGame = useSpaceFighterStore(state => state.startGame);const stopGame = useSpaceFighterStore(state => state.stopGame);const resetGame = useSpaceFighterStore(state => state.resetGame);const decrementTime = useSpaceFighterStore(state => state.decrementTime);const addAsteroids = useSpaceFighterStore(state => state.addAsteroids);const removeAsteroid = useSpaceFighterStore(state => state.removeAsteroid);const updateAsteroid = useSpaceFighterStore(state => state.updateAsteroid);const updateSpacecraft = useSpaceFighterStore(state => state.updateSpacecraft);const offset = settings?.offset ?? 0;useEffect(()=>{setOffset(offset);},[offset,setOffset]);const{wrapFunction,safeExecute}= useGameErrorHandler(GAME_IDS.SPACE_FIGHTER);const gameService = useMemo(()=> safeExecute(()=> createSpaceFighterService(width,height),createSpaceFighterService(width,height),GameErrorType.INITIALIZATION_ERROR),[safeExecute]);useEffect(()=>{return()=>{handleResetGame();if(gameService){gameService.cleanup();}};},[gameService]);useEffect(()=>{if(gameOver && !gameReportedRef.current){const currentState = useSpaceFighterStore.getState();const status = currentState.lives <= 0 ? 'lose':'win';reportGameEnd(status);}if(!gameOver){gameReportedRef.current = false;}},[gameOver,reportGameEnd]);useEffect(()=>{if(gameOver && gameService){gameService.cleanup();}},[gameOver,gameService]);const difficultySettings = useMemo(()=> getDifficultySettings(difficulty),[difficulty]);const handleStartGame = useCallback(()=> wrapFunction(()=>{gameReportedRef.current = false;startGame();if(gameService){gameService.updateDifficultySettings(difficultySettings.asteroidSpeed,difficultySettings.asteroidSpawnInterval);gameService.startGameTimer(decrementTime);gameService.startAsteroidSpawning(addAsteroids);gameService.startPhysicsLoop(updateSpacecraft,updateAsteroid,removeAsteroid,()=> useSpaceFighterStore.getState().spacecraft,()=> useSpaceFighterStore.getState().asteroids);}},GameErrorType.INITIALIZATION_ERROR)(),[wrapFunction,startGame,gameService,difficultySettings,updateSpacecraft,decrementTime,addAsteroids,updateAsteroid,removeAsteroid]);const handleStopGame = useCallback(()=> wrapFunction(()=>{reportGameEnd('cancel');stopGame();if(gameService){gameService.cleanup();}},GameErrorType.UNKNOWN_ERROR)(),[wrapFunction,stopGame,gameService,reportGameEnd]);const handleResetGame = useCallback(()=> wrapFunction(()=>{if(gameService){gameService.cleanup();}resetGame();if(gameService){const initialSpacecraft = gameService.getInitialSpacecraft();updateSpacecraft(initialSpacecraft);}},GameErrorType.UNKNOWN_ERROR)(),[wrapFunction,gameService,resetGame,updateSpacecraft]);const gameControlButtonProps = useMemo(()=>({isPlaying,gameOver,onStartGame:handleStartGame,onStopGame:handleStopGame,startButtonText:"START FLYING",stopButtonText:"STOP GAME",startButtonSubtext:"Navigate through space!",stopButtonSubtext:"End current game",startButtonColor:"#9370db",stopButtonColor:"#dc2626",startButtonBorderColor:"#ba68c8",stopButtonBorderColor:"#f87171"}),[isPlaying,gameOver,handleStartGame,handleStopGame]);const gameOverModalProps = useMemo(()=>({isVisible:gameOver,score,onPlayAgain:handleResetGame,buttonText:"Fly Again!",primaryColor:"rgba(147,112,219,0.5)",borderColor:"rgba(147,112,219,0.5)",buttonColor:"#ffffff",buttonBorderColor:"#ffffff",buttonTextColor:"#9370db"}),[gameOver,score,handleResetGame]);const gameSettingsModalProps = useMemo(()=>({gameId:GAME_IDS.SPACE_FIGHTER,settings:settings || DEFAULT_GAME_SETTINGS,onSettingsChange}),[settings,onSettingsChange]);return _jsx(View,{style:styles.container,children:_jsx(GestureHandlerRootView,{children:_jsxs(GameBackground,{children:[_jsx(GameArea,{}),_jsx(ScoreBoard,{offset:offset}),_jsx(GameControlButton,{...gameControlButtonProps}),_jsx(GameOverModal,{...gameOverModalProps}),_jsx(GameSettingsModal,{...gameSettingsModalProps})]})})});};const styles = StyleSheet.create({container:{flex:1}});
1
+ "use strict";import t,{useEffect as o,useCallback as r,useMemo as e}from"react";import{View as n,StyleSheet as s,Dimensions as i}from"react-native";import{GestureHandlerRootView as a}from"react-native-gesture-handler";import{useSpaceFighterStore as c}from"./SpaceFighterStore.js";import{createSpaceFighterService as f}from"./SpaceFighterService.js";import{getDifficultySettings as m}from"./SpaceFighterConstants.js";import{ScoreBoard as p,GameArea as l,GameBackground as u}from"./components/index.js";import{GameControlButton as g,GameOverModal as d,useGameErrorHandler as h,GameErrorType as x}from"../../helpers/index.js";import{GameSettingsModal as S}from"../../helpers/index.js";import{GAME_IDS as b,DEFAULT_GAME_SETTINGS as C}from"../../services/UtilsService.js";import{jsx as B,jsxs as j}from"react/jsx-runtime";const{width:v,height:F}=i.get("window");export const SpaceFighter=({settings:s=C,onSettingsChange:i,onEndGame:y})=>{const{difficulty:w}=e(()=>s||C,[s]),G=c(t=>t.isPlaying),A=c(t=>t.gameOver),E=c(t=>t.score),P=t.useRef(!1),I=r(t=>{if(y&&!P.current){const o=c.getState();y({status:t,score:o.score.toString()}),P.current=!0}},[y]),N=c(t=>t.setOffset),O=c(t=>t.startGame),L=c(t=>t.stopGame),M=c(t=>t.resetGame),R=c(t=>t.decrementTime),U=c(t=>t.addAsteroids),V=c(t=>t.removeAsteroid),Y=c(t=>t.updateAsteroid),k=c(t=>t.updateSpacecraft),q=s?.offset??0;o(()=>{N(q)},[q,N]);const{wrapFunction:z,safeExecute:D}=h(b.SPACE_FIGHTER),H=e(()=>D(()=>f(v,F),f(v,F),x.INITIALIZATION_ERROR),[D]);o(()=>()=>{W(),H&&H.cleanup()},[H]),o(()=>{if(A&&!P.current){const t=c.getState().lives>0?"win":"lose";I(t)}A||(P.current=!1)},[A,I]),o(()=>{A&&H&&H.cleanup()},[A,H]);const J=e(()=>m(w),[w]),K=r(()=>z(()=>{P.current=!1,O(),H&&(H.updateDifficultySettings(J.asteroidSpeed,J.asteroidSpawnInterval),H.startGameTimer(R),H.startAsteroidSpawning(U),H.startPhysicsLoop(k,Y,V,()=>c.getState().spacecraft,()=>c.getState().asteroids))},x.INITIALIZATION_ERROR)(),[z,O,H,J,k,R,U,Y,V]),Q=r(()=>z(()=>{I("cancel"),L(),H&&H.cleanup()},x.UNKNOWN_ERROR)(),[z,L,H,I]),W=r(()=>z(()=>{if(H&&H.cleanup(),M(),H){const t=H.getInitialSpacecraft();k(t)}},x.UNKNOWN_ERROR)(),[z,H,M,k]),X=e(()=>({isPlaying:G,gameOver:A,onStartGame:K,onStopGame:Q,startButtonText:"START FLYING",stopButtonText:"STOP GAME",startButtonSubtext:"Navigate through space!",stopButtonSubtext:"End current game",startButtonColor:"#9370db",stopButtonColor:"#dc2626",startButtonBorderColor:"#ba68c8",stopButtonBorderColor:"#f87171"}),[G,A,K,Q]),Z=e(()=>({isVisible:A,score:E,onPlayAgain:W,buttonText:"Fly Again!",primaryColor:"rgba(147, 112, 219, 0.5)",borderColor:"rgba(147, 112, 219, 0.5)",buttonColor:"#ffffff",buttonBorderColor:"#ffffff",buttonTextColor:"#9370db"}),[A,E,W]),$=e(()=>({gameId:b.SPACE_FIGHTER,settings:s||C,onSettingsChange:i}),[s,i]);return B(n,{style:T.container,children:B(a,{children:j(u,{children:[B(l,{}),B(p,{offset:q}),B(g,{...X}),B(d,{...Z}),B(S,{...$})]})})})};const T=s.create({container:{flex:1}});
@@ -1 +1 @@
1
- "use strict";export const SPACE_FIGHTER_DIFFICULTY_CONFIG ={easy:{gameDuration:60,asteroidSpawnInterval:4000,asteroidSpeed:1.5},medium:{gameDuration:60,asteroidSpawnInterval:3000,asteroidSpeed:2},hard:{gameDuration:60,asteroidSpawnInterval:2000,asteroidSpeed:3}};export const getDifficultySettings = difficulty =>{return SPACE_FIGHTER_DIFFICULTY_CONFIG[difficulty];};export const DEFAULT_SPACE_FIGHTER_SETTINGS ={difficulty:'medium',soundEnabled:true,hapticEnabled:true,gameDuration:SPACE_FIGHTER_DIFFICULTY_CONFIG.medium.gameDuration,asteroidSpawnInterval:SPACE_FIGHTER_DIFFICULTY_CONFIG.medium.asteroidSpawnInterval,asteroidSpeed:SPACE_FIGHTER_DIFFICULTY_CONFIG.medium.asteroidSpeed};export const SPACE_FIGHTER_GAME_CONFIG ={GAME_DURATION:120,SPACECRAFT_SIZE:28,SPACECRAFT_SPEED:3,ASTEROID_WIDTH:80,ASTEROID_GAP:120,ASTEROID_SPAWN_INTERVAL:3000,ASTEROID_SPEED:2,GRAVITY:0.3,BOUNCE_DAMPING:0.7,FRICTION:0.98,MAX_VELOCITY:8,COLLISION_TOLERANCE:2,COLLISION_COOLDOWN:2000};export const SPACE_FIGHTER_COLORS ={BACKGROUND:'#0a0a1a',SPACECRAFT:'#00d4ff',ASTEROID:'#8b4513',ASTEROID_SHADOW:'#654321',SCORE:'#ffd700',UI:'#9370db',STARS:'#ffffff'};export const SPACE_FIGHTER_THEME ={backgroundColor:'rgba(0,0,0,0.7)',headerBackgroundColor:'#9370db',headerTextColor:'#ffffff',sectionBackgroundColor:'rgba(147,112,219,0.15)',sectionTitleColor:'#9370db',buttonSelectedColor:'#9370db',buttonUnselectedColor:'rgba(255,255,255,0.2)',buttonSelectedTextColor:'#ffffff',buttonUnselectedTextColor:'rgba(255,255,255,0.7)',switchTrackColorFalse:'rgba(147,112,219,0.3)',switchTrackColorTrue:'#9370db',switchThumbColor:'#ffffff',infoTextColor:'rgba(255,255,255,0.9)'};export const SPACE_FIGHTER_DIFFICULTY_DESCRIPTIONS ={easy:'Slow asteroids,relaxed gameplay',medium:'Normal asteroid speed,balanced challenge',hard:'Fast asteroids,quick reflexes needed!'};
1
+ "use strict";export const SPACE_FIGHTER_DIFFICULTY_CONFIG={easy:{gameDuration:60,asteroidSpawnInterval:4e3,asteroidSpeed:1.5},medium:{gameDuration:60,asteroidSpawnInterval:3e3,asteroidSpeed:2},hard:{gameDuration:60,asteroidSpawnInterval:2e3,asteroidSpeed:3}};export const getDifficultySettings=e=>SPACE_FIGHTER_DIFFICULTY_CONFIG[e];export const DEFAULT_SPACE_FIGHTER_SETTINGS={difficulty:"medium",soundEnabled:!0,hapticEnabled:!0,gameDuration:SPACE_FIGHTER_DIFFICULTY_CONFIG.medium.gameDuration,asteroidSpawnInterval:SPACE_FIGHTER_DIFFICULTY_CONFIG.medium.asteroidSpawnInterval,asteroidSpeed:SPACE_FIGHTER_DIFFICULTY_CONFIG.medium.asteroidSpeed};export const SPACE_FIGHTER_GAME_CONFIG={GAME_DURATION:120,SPACECRAFT_SIZE:28,SPACECRAFT_SPEED:3,ASTEROID_WIDTH:80,ASTEROID_GAP:120,ASTEROID_SPAWN_INTERVAL:3e3,ASTEROID_SPEED:2,GRAVITY:.3,BOUNCE_DAMPING:.7,FRICTION:.98,MAX_VELOCITY:8,COLLISION_TOLERANCE:2,COLLISION_COOLDOWN:2e3};export const SPACE_FIGHTER_COLORS={BACKGROUND:"#0a0a1a",SPACECRAFT:"#00d4ff",ASTEROID:"#8b4513",ASTEROID_SHADOW:"#654321",SCORE:"#ffd700",UI:"#9370db",STARS:"#ffffff"};export const SPACE_FIGHTER_THEME={backgroundColor:"rgba(0, 0, 0, 0.7)",headerBackgroundColor:"#9370db",headerTextColor:"#ffffff",sectionBackgroundColor:"rgba(147, 112, 219, 0.15)",sectionTitleColor:"#9370db",buttonSelectedColor:"#9370db",buttonUnselectedColor:"rgba(255, 255, 255, 0.2)",buttonSelectedTextColor:"#ffffff",buttonUnselectedTextColor:"rgba(255, 255, 255, 0.7)",switchTrackColorFalse:"rgba(147, 112, 219, 0.3)",switchTrackColorTrue:"#9370db",switchThumbColor:"#ffffff",infoTextColor:"rgba(255, 255, 255, 0.9)"};export const SPACE_FIGHTER_DIFFICULTY_DESCRIPTIONS={easy:"Slow asteroids, relaxed gameplay",medium:"Normal asteroid speed, balanced challenge",hard:"Fast asteroids, quick reflexes needed!"};
@@ -1 +1 @@
1
- "use strict";import{SPACE_FIGHTER_GAME_CONFIG as GAME_CONFIG,SPACE_FIGHTER_COLORS as COLORS}from "./SpaceFighterConstants.js";export{GAME_CONFIG,COLORS};class SpaceFighterGameService{gameTimer = null;asteroidSpawnTimer = null;physicsTimer = null;particles = [];lastCollisionTime = 0;currentAsteroidSpeed = GAME_CONFIG.ASTEROID_SPEED;currentSpawnInterval = GAME_CONFIG.ASTEROID_SPAWN_INTERVAL;constructor(screenWidth,screenHeight){this.width = screenWidth;this.height = screenHeight;}startGame(){this.cleanup();}cleanup(){if(this.gameTimer){clearInterval(this.gameTimer);this.gameTimer = null;}if(this.asteroidSpawnTimer){clearInterval(this.asteroidSpawnTimer);this.asteroidSpawnTimer = null;}if(this.physicsTimer){clearInterval(this.physicsTimer);this.physicsTimer = null;}this.particles = [];this.lastCollisionTime = 0;}startGameTimer(decrementTime){this.gameTimer = setInterval(decrementTime,1000);}updateDifficultySettings(asteroidSpeed,spawnInterval){this.currentAsteroidSpeed = asteroidSpeed;this.currentSpawnInterval = spawnInterval;}startAsteroidSpawning(spawnAsteroid){const initialAsteroids = this.generateAsteroidPair();spawnAsteroid(initialAsteroids);this.asteroidSpawnTimer = setInterval(()=>{const asteroids = this.generateAsteroidPair();spawnAsteroid(asteroids);},this.currentSpawnInterval);}startPhysicsLoop(updateSpacecraft,updateAsteroid,removeAsteroid,getSpacecraft,getAsteroids){this.physicsTimer = setInterval(()=>{this.updatePhysics(updateSpacecraft,updateAsteroid,removeAsteroid,getSpacecraft,getAsteroids);},16);}generateAsteroidPair(){const pairId = `asteroid_pair_${Date.now()}_${Math.random()}`;const minGapX = this.width * 0.1;const maxGapX = this.width * 0.9 - GAME_CONFIG.ASTEROID_GAP;const gapX = Math.random()*(maxGapX - minGapX)+ minGapX;const leftAsteroid ={id:`${pairId}_left`,x:0,y:-250,width:gapX,height:GAME_CONFIG.ASTEROID_WIDTH,type:'left',pairId,passed:false,spawnTime:Date.now()};const rightAsteroid ={id:`${pairId}_right`,x:gapX + GAME_CONFIG.ASTEROID_GAP,y:-250,width:this.width -(gapX + GAME_CONFIG.ASTEROID_GAP),height:GAME_CONFIG.ASTEROID_WIDTH,type:'right',pairId,passed:false,spawnTime:Date.now()};return [leftAsteroid,rightAsteroid];}updatePhysics(updateSpacecraft,updateAsteroid,removeAsteroid,getSpacecraft,getAsteroids){const spacecraft = getSpacecraft();const asteroids = getAsteroids();let newSpacecraft ={...spacecraft};newSpacecraft.y = spacecraft.y;newSpacecraft.velocityY = 0;if(!newSpacecraft.isControlled){newSpacecraft.velocityX = 0;}if(newSpacecraft.x <= newSpacecraft.size / 2){newSpacecraft.x = newSpacecraft.size / 2;newSpacecraft.velocityX = 0;}if(newSpacecraft.x >= this.width - newSpacecraft.size / 2){newSpacecraft.x = this.width - newSpacecraft.size / 2;newSpacecraft.velocityX = 0;}updateSpacecraft(newSpacecraft);for(let i = 0;i < asteroids.length;i++){const asteroid = asteroids[i];if(!asteroid)continue;const newY = asteroid.y + this.currentAsteroidSpeed;if(newY > this.height){removeAsteroid(asteroid.id);}else{updateAsteroid(asteroid.id,{y:newY});}}this.updateParticles();}controlSpacecraft(spacecraft,targetX,_targetY){const minX = spacecraft.size / 2;const maxX = this.width - spacecraft.size / 2;const constrainedX = Math.max(minX,Math.min(maxX,targetX));return{...spacecraft,x:constrainedX,y:spacecraft.y,velocityX:0,velocityY:0,isControlled:true};}releaseSpacecraft(spacecraft){return{...spacecraft,isControlled:false};}checkCollision(spacecraft,asteroids){const currentTime = Date.now();if(currentTime - this.lastCollisionTime < GAME_CONFIG.COLLISION_COOLDOWN){return{collision:false};}for(const asteroid of asteroids){if(this.spacecraftAsteroidCollision(spacecraft,asteroid)){this.lastCollisionTime = currentTime;return{collision:true,asteroidId:asteroid.id};}}return{collision:false};}spacecraftAsteroidCollision(spacecraft,asteroid){const spacecraftLeft = spacecraft.x - spacecraft.size / 2;const spacecraftRight = spacecraft.x + spacecraft.size / 2;const spacecraftTop = spacecraft.y - spacecraft.size / 2;const spacecraftBottom = spacecraft.y + spacecraft.size / 2;const asteroidLeft = asteroid.x;const asteroidRight = asteroid.x + asteroid.width;const asteroidTop = asteroid.y;const asteroidBottom = asteroid.y + asteroid.height;return spacecraftRight > asteroidLeft && spacecraftLeft < asteroidRight && spacecraftBottom > asteroidTop && spacecraftTop < asteroidBottom;}checkAsteroidPass(spacecraft,asteroids){const passedAsteroids = [];for(let i = 0;i < asteroids.length;i++){const asteroid = asteroids[i];if(!asteroid)continue;if(!asteroid.passed && spacecraft.y > asteroid.y + asteroid.height){passedAsteroids.push(asteroid.pairId);}}return passedAsteroids;}createCollisionParticles(x,y){for(let i = 0;i < 15;i++){const particle ={id:`particle_${Date.now()}_${i}`,x,y,velocityX:(Math.random()- 0.5)* 15,velocityY:(Math.random()- 0.5)* 15,color:i < 5 ? '#ff6600':i < 10 ? COLORS.SPACECRAFT:'#ffff00',life:1200,maxLife:1200,size:8 + Math.random()* 12};this.particles.push(particle);}for(let i = 0;i < 5;i++){const bigParticle ={id:`big_particle_${Date.now()}_${i}`,x:x +(Math.random()- 0.5)* 20,y:y +(Math.random()- 0.5)* 20,velocityX:(Math.random()- 0.5)* 8,velocityY:(Math.random()- 0.5)* 8,color:'#ff0000',life:1500,maxLife:1500,size:15 + Math.random()* 10};this.particles.push(bigParticle);}}updateParticles(){const activeParticles = [];for(let i = 0;i < this.particles.length;i++){const particle = this.particles[i];if(!particle)continue;particle.x += particle.velocityX;particle.y += particle.velocityY;particle.velocityY += 0.2;particle.life -= 16;if(particle.life > 0){activeParticles.push(particle);}}this.particles = activeParticles;}getParticles(){return this.particles;}isInCollisionImmunity(){const currentTime = Date.now();return currentTime - this.lastCollisionTime < GAME_CONFIG.COLLISION_COOLDOWN;}getRemainingImmunityTime(){const currentTime = Date.now();const elapsed = currentTime - this.lastCollisionTime;return Math.max(0,GAME_CONFIG.COLLISION_COOLDOWN - elapsed);}getInitialSpacecraft(){return{x:this.width / 2,y:this.height * 0.77,velocityX:0,velocityY:0,size:GAME_CONFIG.SPACECRAFT_SIZE,isControlled:false};}}export const createSpaceFighterService =(width,height)=>{return new SpaceFighterGameService(width,height);};
1
+ "use strict";import{SPACE_FIGHTER_GAME_CONFIG as t,SPACE_FIGHTER_COLORS as i}from"./SpaceFighterConstants.js";export{t as GAME_CONFIG,i as COLORS};class e{gameTimer=null;asteroidSpawnTimer=null;physicsTimer=null;particles=[];lastCollisionTime=0;currentAsteroidSpeed=t.ASTEROID_SPEED;currentSpawnInterval=t.ASTEROID_SPAWN_INTERVAL;constructor(t,i){this.width=t,this.height=i}startGame(){this.cleanup()}cleanup(){this.gameTimer&&(clearInterval(this.gameTimer),this.gameTimer=null),this.asteroidSpawnTimer&&(clearInterval(this.asteroidSpawnTimer),this.asteroidSpawnTimer=null),this.physicsTimer&&(clearInterval(this.physicsTimer),this.physicsTimer=null),this.particles=[],this.lastCollisionTime=0}startGameTimer(t){this.gameTimer=setInterval(t,1e3)}updateDifficultySettings(t,i){this.currentAsteroidSpeed=t,this.currentSpawnInterval=i}startAsteroidSpawning(t){const i=this.generateAsteroidPair();t(i),this.asteroidSpawnTimer=setInterval(()=>{const i=this.generateAsteroidPair();t(i)},this.currentSpawnInterval)}startPhysicsLoop(t,i,e,s,r){this.physicsTimer=setInterval(()=>{this.updatePhysics(t,i,e,s,r)},16)}generateAsteroidPair(){const i=`asteroid_pair_${Date.now()}_${Math.random()}`,e=.1*this.width,s=.9*this.width-t.ASTEROID_GAP,r=Math.random()*(s-e)+e;return[{id:i+"_left",x:0,y:-250,width:r,height:t.ASTEROID_WIDTH,type:"left",pairId:i,passed:!1,spawnTime:Date.now()},{id:i+"_right",x:r+t.ASTEROID_GAP,y:-250,width:this.width-(r+t.ASTEROID_GAP),height:t.ASTEROID_WIDTH,type:"right",pairId:i,passed:!1,spawnTime:Date.now()}]}updatePhysics(t,i,e,s,r){const a=s(),o=r();let n={...a};n.y=a.y,n.velocityY=0,n.isControlled||(n.velocityX=0),n.x>n.size/2||(n.x=n.size/2,n.velocityX=0),n.x<this.width-n.size/2||(n.x=this.width-n.size/2,n.velocityX=0),t(n);for(let t=0;t<o.length;t++){const s=o[t];if(!s)continue;const r=s.y+this.currentAsteroidSpeed;r>this.height?e(s.id):i(s.id,{y:r})}this.updateParticles()}controlSpacecraft(t,i,e){const s=t.size/2,r=this.width-t.size/2,a=Math.max(s,Math.min(r,i));return{...t,x:a,y:t.y,velocityX:0,velocityY:0,isControlled:!0}}releaseSpacecraft(t){return{...t,isControlled:!1}}checkCollision(i,e){const s=Date.now();if(s-this.lastCollisionTime<t.COLLISION_COOLDOWN)return{collision:!1};for(const t of e)if(this.spacecraftAsteroidCollision(i,t))return this.lastCollisionTime=s,{collision:!0,asteroidId:t.id};return{collision:!1}}spacecraftAsteroidCollision(t,i){const e=t.x-t.size/2,s=t.x+t.size/2,r=t.y-t.size/2,a=t.y+t.size/2,o=i.x,n=i.x+i.width,l=i.y,h=i.y+i.height;return s>o&&n>e&&a>l&&h>r}checkAsteroidPass(t,i){const e=[];for(let s=0;s<i.length;s++){const r=i[s];r&&!r.passed&&t.y>r.y+r.height&&e.push(r.pairId)}return e}createCollisionParticles(t,e){for(let s=0;15>s;s++){const r={id:`particle_${Date.now()}_${s}`,x:t,y:e,velocityX:15*(Math.random()-.5),velocityY:15*(Math.random()-.5),color:5>s?"#ff6600":10>s?i.SPACECRAFT:"#ffff00",life:1200,maxLife:1200,size:8+12*Math.random()};this.particles.push(r)}for(let i=0;5>i;i++){const s={id:`big_particle_${Date.now()}_${i}`,x:t+20*(Math.random()-.5),y:e+20*(Math.random()-.5),velocityX:8*(Math.random()-.5),velocityY:8*(Math.random()-.5),color:"#ff0000",life:1500,maxLife:1500,size:15+10*Math.random()};this.particles.push(s)}}updateParticles(){const t=[];for(let i=0;i<this.particles.length;i++){const e=this.particles[i];e&&(e.x+=e.velocityX,e.y+=e.velocityY,e.velocityY+=.2,e.life-=16,e.life>0&&t.push(e))}this.particles=t}getParticles(){return this.particles}isInCollisionImmunity(){return Date.now()-this.lastCollisionTime<t.COLLISION_COOLDOWN}getRemainingImmunityTime(){const i=Date.now()-this.lastCollisionTime;return Math.max(0,t.COLLISION_COOLDOWN-i)}getInitialSpacecraft(){return{x:this.width/2,y:.77*this.height,velocityX:0,velocityY:0,size:t.SPACECRAFT_SIZE,isControlled:!1}}}export const createSpaceFighterService=(t,i)=>new e(t,i);
@@ -1 +1 @@
1
- "use strict";import{create}from 'zustand';import{subscribeWithSelector}from 'zustand/middleware';import{Dimensions}from 'react-native';import{GAME_CONFIG}from "./SpaceFighterService.js";import{immerMiddleware}from "../../services/UtilsService.js";const{width,height}= Dimensions.get('window');const getSpacecraftYPosition =(offset = 0)=>{const yPercent = offset > 0 ? 0.77:0.68;return height * yPercent;};export const useSpaceFighterStore = create()(subscribeWithSelector(immerMiddleware((set,_get)=>({score:0,timeLeft:60,isPlaying:false,gameOver:false,lives:3,asteroids:[],spacecraft:{x:width / 2,y:getSpacecraftYPosition(0),velocityX:0,velocityY:0,size:GAME_CONFIG.SPACECRAFT_SIZE,isControlled:false},spacecraftPath:[],isControllingSpacecraft:false,offset:0,setOffset:offset =>{set(draft =>{draft.offset = offset;draft.spacecraft.y = getSpacecraftYPosition(offset);});},startGame:_gameDuration =>{const duration = 60;set(draft =>{draft.score = 0;draft.timeLeft = duration;draft.isPlaying = true;draft.gameOver = false;draft.lives = 3;draft.asteroids = [];draft.spacecraftPath = [];draft.isControllingSpacecraft = false;draft.spacecraft ={x:width / 2,y:getSpacecraftYPosition(draft.offset),velocityX:0,velocityY:0,size:GAME_CONFIG.SPACECRAFT_SIZE,isControlled:false};});},stopGame:()=>{set(draft =>{draft.score = 0;draft.timeLeft = 60;draft.isPlaying = false;draft.gameOver = false;draft.lives = 3;draft.asteroids = [];draft.spacecraftPath = [];draft.isControllingSpacecraft = false;draft.spacecraft ={x:width / 2,y:getSpacecraftYPosition(draft.offset),velocityX:0,velocityY:0,size:GAME_CONFIG.SPACECRAFT_SIZE,isControlled:false};});},resetGame:()=>{set(draft =>{draft.score = 0;draft.timeLeft = 60;draft.isPlaying = false;draft.gameOver = false;draft.lives = 3;draft.asteroids = [];draft.spacecraftPath = [];draft.isControllingSpacecraft = false;draft.spacecraft ={x:width / 2,y:getSpacecraftYPosition(draft.offset),velocityX:0,velocityY:0,size:GAME_CONFIG.SPACECRAFT_SIZE,isControlled:false};});},updateScore:points =>{set(draft =>{draft.score = draft.score + points;});},decrementTime:()=>{set(draft =>{const newTimeLeft = draft.timeLeft - 1;if(newTimeLeft <= 0){draft.timeLeft = 0;draft.isPlaying = false;draft.gameOver = true;}else{draft.timeLeft = newTimeLeft;}});},addAsteroids:asteroids =>{set(draft =>{draft.asteroids.push(...asteroids);});},removeAsteroid:asteroidId =>{set(draft =>{draft.asteroids = draft.asteroids.filter(a => a.id !== asteroidId);});},updateAsteroid:(asteroidId,updates)=>{set(draft =>{for(let i = 0;i < draft.asteroids.length;i++){if(draft.asteroids[i].id === asteroidId){Object.assign(draft.asteroids[i],updates);break;}}});},updateSpacecraft:spacecraft =>{set(draft =>{draft.spacecraft = spacecraft;});},setSpacecraftPath:path =>{set(draft =>{draft.spacecraftPath = path;});},setIsControllingSpacecraft:isControlling =>{set(draft =>{draft.isControllingSpacecraft = isControlling;});},markAsteroidPassed:pairId =>{set(draft =>{for(let i = 0;i < draft.asteroids.length;i++){if(draft.asteroids[i].pairId === pairId){draft.asteroids[i].passed = true;}}draft.score = draft.score + 10;});},loseLife:()=>{set(draft =>{const newLives = draft.lives - 1;draft.spacecraft ={x:width / 2,y:getSpacecraftYPosition(draft.offset),velocityX:0,velocityY:0,size:GAME_CONFIG.SPACECRAFT_SIZE,isControlled:false};if(newLives <= 0){draft.lives = 0;draft.isPlaying = false;draft.gameOver = true;}else{draft.lives = newLives;}});}}))));
1
+ "use strict";import{create as e}from"zustand";import{subscribeWithSelector as t}from"zustand/middleware";import{Dimensions as o}from"react-native";import{GAME_CONFIG as i}from"./SpaceFighterService.js";import{immerMiddleware as r}from"../../services/UtilsService.js";const{width:s,height:c}=o.get("window"),a=(e=0)=>c*(e>0?.77:.68);export const useSpaceFighterStore=e()(t(r((e,t)=>({score:0,timeLeft:60,isPlaying:!1,gameOver:!1,lives:3,asteroids:[],spacecraft:{x:s/2,y:a(0),velocityX:0,velocityY:0,size:i.SPACECRAFT_SIZE,isControlled:!1},spacecraftPath:[],isControllingSpacecraft:!1,offset:0,setOffset:t=>{e(e=>{e.offset=t,e.spacecraft.y=a(t)})},startGame:t=>{e(e=>{e.score=0,e.timeLeft=60,e.isPlaying=!0,e.gameOver=!1,e.lives=3,e.asteroids=[],e.spacecraftPath=[],e.isControllingSpacecraft=!1,e.spacecraft={x:s/2,y:a(e.offset),velocityX:0,velocityY:0,size:i.SPACECRAFT_SIZE,isControlled:!1}})},stopGame:()=>{e(e=>{e.score=0,e.timeLeft=60,e.isPlaying=!1,e.gameOver=!1,e.lives=3,e.asteroids=[],e.spacecraftPath=[],e.isControllingSpacecraft=!1,e.spacecraft={x:s/2,y:a(e.offset),velocityX:0,velocityY:0,size:i.SPACECRAFT_SIZE,isControlled:!1}})},resetGame:()=>{e(e=>{e.score=0,e.timeLeft=60,e.isPlaying=!1,e.gameOver=!1,e.lives=3,e.asteroids=[],e.spacecraftPath=[],e.isControllingSpacecraft=!1,e.spacecraft={x:s/2,y:a(e.offset),velocityX:0,velocityY:0,size:i.SPACECRAFT_SIZE,isControlled:!1}})},updateScore:t=>{e(e=>{e.score=e.score+t})},decrementTime:()=>{e(e=>{const t=e.timeLeft-1;t>0?e.timeLeft=t:(e.timeLeft=0,e.isPlaying=!1,e.gameOver=!0)})},addAsteroids:t=>{e(e=>{e.asteroids.push(...t)})},removeAsteroid:t=>{e(e=>{e.asteroids=e.asteroids.filter(e=>e.id!==t)})},updateAsteroid:(t,o)=>{e(e=>{for(let i=0;i<e.asteroids.length;i++)if(e.asteroids[i].id===t){Object.assign(e.asteroids[i],o);break}})},updateSpacecraft:t=>{e(e=>{e.spacecraft=t})},setSpacecraftPath:t=>{e(e=>{e.spacecraftPath=t})},setIsControllingSpacecraft:t=>{e(e=>{e.isControllingSpacecraft=t})},markAsteroidPassed:t=>{e(e=>{for(let o=0;o<e.asteroids.length;o++)e.asteroids[o].pairId===t&&(e.asteroids[o].passed=!0);e.score=e.score+10})},loseLife:()=>{e(e=>{const t=e.lives-1;e.spacecraft={x:s/2,y:a(e.offset),velocityX:0,velocityY:0,size:i.SPACECRAFT_SIZE,isControlled:!1},t>0?e.lives=t:(e.lives=0,e.isPlaying=!1,e.gameOver=!0)})}}))));