@nayan-ui/games 1.20.0 → 1.21.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.
- package/lib/module/games/arrows/Arrows.js +1 -1
- package/lib/module/games/arrows/ArrowsScene.js +1 -1
- package/lib/module/games/balloon-blaster/BalloonBlaster.js +1 -1
- package/lib/module/games/balloon-blaster/BalloonBlasterScene.js +1 -1
- package/lib/module/games/bike-racing/BikeMeshes.js +1 -1
- package/lib/module/games/bike-racing/BikeRacing.js +1 -1
- package/lib/module/games/bike-racing/BikeRacingConstants.js +1 -1
- package/lib/module/games/bike-racing/BikeRacingScene.js +1 -1
- package/lib/module/games/block-blast/BlockBlast.js +1 -1
- package/lib/module/games/block-blast/BlockBlastScene.js +1 -1
- package/lib/module/games/block-breaker/BlockBreaker.js +1 -1
- package/lib/module/games/block-breaker/BlockBreakerScene.js +1 -1
- package/lib/module/games/bluk/Bluk.js +1 -1
- package/lib/module/games/bluk/BlukScene.js +1 -1
- package/lib/module/games/bubble-shooter/BubbleShooter.js +1 -1
- package/lib/module/games/bubble-shooter/BubbleShooterScene.js +1 -1
- package/lib/module/games/candy-crush/CandyCrush.js +1 -1
- package/lib/module/games/candy-crush/CandyCrushScene.js +1 -1
- package/lib/module/games/car-racing/CarMeshes.js +1 -1
- package/lib/module/games/car-racing/CarRacing.js +1 -1
- package/lib/module/games/car-racing/CarRacingScene.js +1 -1
- package/lib/module/games/chameleon-run/ChameleonRun.js +1 -1
- package/lib/module/games/chameleon-run/ChameleonRunBackdrop.js +1 -1
- package/lib/module/games/chameleon-run/ChameleonRunMeshes.js +1 -1
- package/lib/module/games/checkers/Checkers.js +1 -1
- package/lib/module/games/checkers/CheckersScene.js +1 -1
- package/lib/module/games/color-switch/ColorSwitch.js +1 -1
- package/lib/module/games/color-switch/ColorSwitchScene.js +1 -1
- package/lib/module/games/colors-sort/ColorsSort.js +1 -1
- package/lib/module/games/colors-sort/ColorsSortScene.js +1 -1
- package/lib/module/games/connect-em-all/ConnectEmAll.js +1 -1
- package/lib/module/games/connect-em-all/ConnectEmAllScene.js +1 -1
- package/lib/module/games/crossy-road/CrossyRoad.js +1 -1
- package/lib/module/games/crossy-road/CrossyRoadMeshes.js +1 -1
- package/lib/module/games/crossy-road/CrossyRoadPerson.js +1 -1
- package/lib/module/games/dino-jump/DinoJump.js +1 -1
- package/lib/module/games/dino-jump/DinoJumpScene.js +1 -1
- package/lib/module/games/dots-and-boxes/DotsAndBoxes.js +1 -1
- package/lib/module/games/dots-and-boxes/DotsAndBoxesScene.js +1 -1
- package/lib/module/games/falling-sticks/FallingSticks.js +1 -1
- package/lib/module/games/falling-sticks/FallingSticksScene.js +1 -1
- package/lib/module/games/find-different-number/FindDifferentNumber.js +1 -1
- package/lib/module/games/find-different-number/FindDifferentNumberScene.js +1 -1
- package/lib/module/games/flappy-bird/FlappyBird.js +1 -1
- package/lib/module/games/flappy-bird/FlappyBirdScene.js +1 -1
- package/lib/module/games/fruit-merger/FruitMerger.js +1 -1
- package/lib/module/games/fruit-merger/FruitMergerScene.js +1 -1
- package/lib/module/games/fruit-ninja/FruitNinja.js +1 -1
- package/lib/module/games/fruit-ninja/FruitNinjaScene.js +1 -1
- package/lib/module/games/fruit-ninja/FruitNinjaWorld.js +1 -1
- package/lib/module/games/game-2048/Game2048.js +1 -1
- package/lib/module/games/game-2048/Game2048Scene.js +1 -1
- package/lib/module/games/knife-hit/KnifeHit.js +1 -1
- package/lib/module/games/knife-hit/KnifeHitScene.js +1 -1
- package/lib/module/games/ludo-king/LudoKing.js +1 -1
- package/lib/module/games/ludo-king/LudoKingScene.js +1 -1
- package/lib/module/games/maze-runner/MazeRunner.js +1 -1
- package/lib/module/games/maze-runner/MazeRunnerScene.js +1 -1
- package/lib/module/games/memory-match/MemoryMatch.js +1 -1
- package/lib/module/games/memory-match/MemoryMatchScene.js +1 -1
- package/lib/module/games/mine-sweeper/MineSweeper.js +1 -1
- package/lib/module/games/mine-sweeper/MineSweeperScene.js +1 -1
- package/lib/module/games/mob-control/MobControl.js +1 -1
- package/lib/module/games/mob-control/MobControlGame.js +1 -1
- package/lib/module/games/mob-control/MobControlMeshes.js +1 -1
- package/lib/module/games/mob-control/MobControlRenderer.js +1 -1
- package/lib/module/games/mob-control/MobControlRules.js +1 -1
- package/lib/module/games/mob-control/MobControlScene.js +1 -1
- package/lib/module/games/nuts-and-bolts/NutsAndBolts.js +1 -1
- package/lib/module/games/nuts-and-bolts/NutsAndBoltsScene.js +1 -1
- package/lib/module/games/pac-man/PacMan.js +1 -1
- package/lib/module/games/pac-man/PacManGame.js +1 -1
- package/lib/module/games/pac-man/PacManScene.js +1 -1
- package/lib/module/games/paper-io/PaperIo.js +1 -1
- package/lib/module/games/paper-io/PaperIoMeshes.js +1 -1
- package/lib/module/games/paper-io/PaperIoScene.js +1 -1
- package/lib/module/games/penguin-run/PenguinMeshes.js +1 -1
- package/lib/module/games/penguin-run/PenguinRun.js +1 -1
- package/lib/module/games/penguin-run/PenguinRunScene.js +1 -1
- package/lib/module/games/perfect-circle/PerfectCircle.js +1 -1
- package/lib/module/games/perfect-circle/PerfectCircleScene.js +1 -1
- package/lib/module/games/pipe-connect/PipeConnect.js +1 -1
- package/lib/module/games/pipe-connect/PipeConnectScene.js +1 -1
- package/lib/module/games/play-with-tua/PlayWithTua.js +1 -1
- package/lib/module/games/popit-fidget/PopitFidget.js +1 -1
- package/lib/module/games/popit-fidget/PopitFidgetScene.js +1 -1
- package/lib/module/games/reversi/Reversi.js +1 -1
- package/lib/module/games/reversi/ReversiScene.js +1 -1
- package/lib/module/games/search-grid/SearchGrid.js +1 -1
- package/lib/module/games/search-grid/SearchGridScene.js +1 -1
- package/lib/module/games/shared/BackdropCard.js +1 -1
- package/lib/module/games/shared/EngineBackdrop.js +1 -1
- package/lib/module/games/shared/EngineBlasts.js +1 -1
- package/lib/module/games/shared/EngineBlocks.js +1 -1
- package/lib/module/games/shared/EngineDice.js +1 -1
- package/lib/module/games/shared/EngineFruits.js +1 -1
- package/lib/module/games/shared/EngineGameLoader.js +1 -1
- package/lib/module/games/shared/EngineGameScreen.js +1 -1
- package/lib/module/games/shared/EngineGlyphs.js +1 -1
- package/lib/module/games/shared/EngineHud.js +1 -1
- package/lib/module/games/shared/EngineMeadow.js +1 -1
- package/lib/module/games/shared/EngineTouchSurface.js +1 -1
- package/lib/module/games/shared/GameActivity.js +1 -1
- package/lib/module/games/shared/GameLoading.js +1 -1
- package/lib/module/games/shared/GameLoadingTransition.js +1 -1
- package/lib/module/games/sliding-numbers/SlidingNumbers.js +1 -1
- package/lib/module/games/sliding-numbers/SlidingNumbersScene.js +1 -1
- package/lib/module/games/snake-3d/Snake3D.js +1 -1
- package/lib/module/games/snake-3d/Snake3DScene.js +1 -1
- package/lib/module/games/snakes-and-ladders/SnakesAndLadders.js +1 -1
- package/lib/module/games/snakes-and-ladders/SnakesAndLaddersScene.js +1 -1
- package/lib/module/games/space-fighter/SpaceFighter.js +1 -1
- package/lib/module/games/space-fighter/SpaceFighterScene.js +1 -1
- package/lib/module/games/spider-solitaire/SpiderSolitaire.js +1 -1
- package/lib/module/games/spider-solitaire/SpiderSolitaireScene.js +1 -1
- package/lib/module/games/stack-tower/StackTower.js +1 -1
- package/lib/module/games/stack-tower/StackTowerScene.js +1 -1
- package/lib/module/games/sudoku/Sudoku.js +1 -1
- package/lib/module/games/sudoku/SudokuScene.js +1 -1
- package/lib/module/games/tangram/Tangram.js +1 -1
- package/lib/module/games/tangram/TangramScene.js +1 -1
- package/lib/module/games/tank-1990/Tank1990.js +1 -1
- package/lib/module/games/tank-1990/Tank1990Scene.js +1 -1
- package/lib/module/games/tic-tac-toe/TicTacToe.js +1 -1
- package/lib/module/games/tic-tac-toe/TicTacToeScene.js +1 -1
- package/lib/module/games/tile-home/TileHome.js +1 -1
- package/lib/module/games/tile-home/TileHomeScene.js +1 -1
- package/lib/module/games/treasure-hunt/TreasureHunt.js +1 -1
- package/lib/module/games/treasure-hunt/TreasureHuntConstants.js +1 -1
- package/lib/module/games/treasure-hunt/TreasureHuntModel.js +1 -1
- package/lib/module/games/treasure-hunt/TreasureHuntScene.js +1 -1
- package/lib/module/games/whack-a-mole/WhackAMole.js +1 -1
- package/lib/module/games/whack-a-mole/WhackAMoleScene.js +1 -1
- package/lib/module/helpers/GameSettingsModal.js +1 -1
- package/lib/module/helpers/GameTheme.js +1 -1
- package/package.json +1 -2
- package/src/assets/fonts/Inter-SemiBold.ttf +0 -0
- package/src/assets/models/explorer/explorer.glb +0 -0
- package/src/assets/models/explorer/params.json +0 -51
- package/src/assets/models/explorer/source.json +0 -222
- package/src/assets/models/play-with-tua/play-with-tua.glb +0 -0
- package/src/assets/models/play-with-tua/sources.json +0 -141
- package/src/assets/sfx/balloon_pop.wav +0 -0
- package/src/assets/sfx/blocked.wav +0 -0
- package/src/assets/sfx/boing.wav +0 -0
- package/src/assets/sfx/bonus.wav +0 -0
- package/src/assets/sfx/boom.wav +0 -0
- package/src/assets/sfx/bump.wav +0 -0
- package/src/assets/sfx/cannon.wav +0 -0
- package/src/assets/sfx/capture.wav +0 -0
- package/src/assets/sfx/card_flip.wav +0 -0
- package/src/assets/sfx/card_place.wav +0 -0
- package/src/assets/sfx/catch.wav +0 -0
- package/src/assets/sfx/chime.wav +0 -0
- package/src/assets/sfx/chomp.wav +0 -0
- package/src/assets/sfx/chomp_alt.wav +0 -0
- package/src/assets/sfx/clack.wav +0 -0
- package/src/assets/sfx/clang.wav +0 -0
- package/src/assets/sfx/clash.wav +0 -0
- package/src/assets/sfx/click.wav +0 -0
- package/src/assets/sfx/coin.wav +0 -0
- package/src/assets/sfx/combo.wav +0 -0
- package/src/assets/sfx/crash.wav +0 -0
- package/src/assets/sfx/crunch.wav +0 -0
- package/src/assets/sfx/dance.wav +0 -0
- package/src/assets/sfx/deselect.wav +0 -0
- package/src/assets/sfx/dice_roll.wav +0 -0
- package/src/assets/sfx/die.wav +0 -0
- package/src/assets/sfx/draw.wav +0 -0
- package/src/assets/sfx/drop.wav +0 -0
- package/src/assets/sfx/eat.wav +0 -0
- package/src/assets/sfx/engine.wav +0 -0
- package/src/assets/sfx/error.wav +0 -0
- package/src/assets/sfx/flag.wav +0 -0
- package/src/assets/sfx/flap.wav +0 -0
- package/src/assets/sfx/flip_disc.wav +0 -0
- package/src/assets/sfx/footstep_run.wav +0 -0
- package/src/assets/sfx/footstep_walk.wav +0 -0
- package/src/assets/sfx/game_over.wav +0 -0
- package/src/assets/sfx/gate.wav +0 -0
- package/src/assets/sfx/hit.wav +0 -0
- package/src/assets/sfx/hop.wav +0 -0
- package/src/assets/sfx/hurt.wav +0 -0
- package/src/assets/sfx/invalid.wav +0 -0
- package/src/assets/sfx/jump.wav +0 -0
- package/src/assets/sfx/ladder.wav +0 -0
- package/src/assets/sfx/land.wav +0 -0
- package/src/assets/sfx/laugh.wav +0 -0
- package/src/assets/sfx/lose.wav +0 -0
- package/src/assets/sfx/merge.wav +0 -0
- package/src/assets/sfx/miss.wav +0 -0
- package/src/assets/sfx/nitro.wav +0 -0
- package/src/assets/sfx/pew.wav +0 -0
- package/src/assets/sfx/place.wav +0 -0
- package/src/assets/sfx/pling.wav +0 -0
- package/src/assets/sfx/point.wav +0 -0
- package/src/assets/sfx/pop.wav +0 -0
- package/src/assets/sfx/pop_soft.wav +0 -0
- package/src/assets/sfx/power_up.wav +0 -0
- package/src/assets/sfx/reveal.wav +0 -0
- package/src/assets/sfx/robot.wav +0 -0
- package/src/assets/sfx/rotate.wav +0 -0
- package/src/assets/sfx/run_steps.wav +0 -0
- package/src/assets/sfx/select.wav +0 -0
- package/src/assets/sfx/shuffle.wav +0 -0
- package/src/assets/sfx/six.wav +0 -0
- package/src/assets/sfx/slice.wav +0 -0
- package/src/assets/sfx/slide.wav +0 -0
- package/src/assets/sfx/snake.wav +0 -0
- package/src/assets/sfx/snap.wav +0 -0
- package/src/assets/sfx/splash.wav +0 -0
- package/src/assets/sfx/star.wav +0 -0
- package/src/assets/sfx/start.wav +0 -0
- package/src/assets/sfx/swoosh.wav +0 -0
- package/src/assets/sfx/tap.wav +0 -0
- package/src/assets/sfx/thud.wav +0 -0
- package/src/assets/sfx/thunk.wav +0 -0
- package/src/assets/sfx/tick.wav +0 -0
- package/src/assets/sfx/time_up.wav +0 -0
- package/src/assets/sfx/tock.wav +0 -0
- package/src/assets/sfx/walk_steps.wav +0 -0
- package/src/assets/sfx/warning.wav +0 -0
- package/src/assets/sfx/whack.wav +0 -0
- package/src/assets/sfx/whoosh.wav +0 -0
- package/src/assets/sfx/win.wav +0 -0
- package/src/assets/sfx/zap.wav +0 -0
- package/src/assets/textures/ad_bolt.png +0 -0
- package/src/assets/textures/ad_bubbles.png +0 -0
- package/src/assets/textures/ad_chevrons.png +0 -0
- package/src/assets/textures/asphalt.png +0 -0
- package/src/assets/textures/bark.jpg +0 -0
- package/src/assets/textures/explorer-atlas.png +0 -0
- package/src/assets/textures/facade.png +0 -0
- package/src/assets/textures/grass.png +0 -0
- package/src/assets/textures/leaves.png +0 -0
- package/src/assets/textures/limestone.jpg +0 -0
- package/src/assets/textures/meadow.jpg +0 -0
- package/src/assets/textures/nature-sources.json +0 -87
- package/src/assets/textures/pine.png +0 -0
- package/src/assets/textures/river.jpg +0 -0
- package/src/assets/textures/soft_glow.png +0 -0
- package/src/games/arrows/Arrows.tsx +0 -69
- package/src/games/arrows/ArrowsConstants.ts +0 -107
- package/src/games/arrows/ArrowsGame.ts +0 -735
- package/src/games/arrows/ArrowsScene.ts +0 -395
- package/src/games/balloon-blaster/BalloonBlaster.tsx +0 -75
- package/src/games/balloon-blaster/BalloonBlasterConstants.ts +0 -79
- package/src/games/balloon-blaster/BalloonBlasterGame.ts +0 -287
- package/src/games/balloon-blaster/BalloonBlasterPhysics.ts +0 -95
- package/src/games/balloon-blaster/BalloonBlasterScene.ts +0 -225
- package/src/games/bike-racing/BikeMeshes.ts +0 -273
- package/src/games/bike-racing/BikeRacing.tsx +0 -211
- package/src/games/bike-racing/BikeRacingConstants.ts +0 -103
- package/src/games/bike-racing/BikeRacingGame.ts +0 -557
- package/src/games/bike-racing/BikeRacingScene.ts +0 -119
- package/src/games/block-blast/BlockBlast.tsx +0 -73
- package/src/games/block-blast/BlockBlastConstants.ts +0 -179
- package/src/games/block-blast/BlockBlastGame.ts +0 -471
- package/src/games/block-blast/BlockBlastScene.ts +0 -156
- package/src/games/block-breaker/BlockBreaker.tsx +0 -79
- package/src/games/block-breaker/BlockBreakerConstants.ts +0 -139
- package/src/games/block-breaker/BlockBreakerGame.ts +0 -459
- package/src/games/block-breaker/BlockBreakerScene.ts +0 -257
- package/src/games/bluk/Bluk.tsx +0 -82
- package/src/games/bluk/BlukConstants.ts +0 -121
- package/src/games/bluk/BlukGame.ts +0 -323
- package/src/games/bluk/BlukRules.ts +0 -154
- package/src/games/bluk/BlukScene.ts +0 -514
- package/src/games/bubble-shooter/BubbleShooter.tsx +0 -76
- package/src/games/bubble-shooter/BubbleShooterConstants.ts +0 -103
- package/src/games/bubble-shooter/BubbleShooterGame.ts +0 -459
- package/src/games/bubble-shooter/BubbleShooterScene.ts +0 -158
- package/src/games/candy-crush/CandyCrush.tsx +0 -77
- package/src/games/candy-crush/CandyCrushConstants.ts +0 -94
- package/src/games/candy-crush/CandyCrushGame.ts +0 -477
- package/src/games/candy-crush/CandyCrushScene.ts +0 -209
- package/src/games/car-racing/CarMeshes.ts +0 -440
- package/src/games/car-racing/CarRacing.tsx +0 -211
- package/src/games/car-racing/CarRacingConstants.ts +0 -167
- package/src/games/car-racing/CarRacingFacade.ts +0 -40
- package/src/games/car-racing/CarRacingGame.ts +0 -539
- package/src/games/car-racing/CarRacingScene.ts +0 -1026
- package/src/games/car-racing/CarRacingScenery.ts +0 -265
- package/src/games/chameleon-run/ChameleonRun.tsx +0 -96
- package/src/games/chameleon-run/ChameleonRunBackdrop.ts +0 -186
- package/src/games/chameleon-run/ChameleonRunConstants.ts +0 -47
- package/src/games/chameleon-run/ChameleonRunGame.ts +0 -309
- package/src/games/chameleon-run/ChameleonRunMeshes.ts +0 -305
- package/src/games/chameleon-run/ChameleonRunRules.ts +0 -252
- package/src/games/chameleon-run/ChameleonRunScene.ts +0 -310
- package/src/games/checkers/Checkers.tsx +0 -83
- package/src/games/checkers/CheckersAI.ts +0 -255
- package/src/games/checkers/CheckersConstants.ts +0 -68
- package/src/games/checkers/CheckersGame.ts +0 -322
- package/src/games/checkers/CheckersRules.ts +0 -167
- package/src/games/checkers/CheckersScene.ts +0 -308
- package/src/games/color-switch/ColorSwitch.tsx +0 -75
- package/src/games/color-switch/ColorSwitchConstants.ts +0 -61
- package/src/games/color-switch/ColorSwitchGame.ts +0 -329
- package/src/games/color-switch/ColorSwitchScene.ts +0 -220
- package/src/games/colors-sort/ColorsSort.tsx +0 -82
- package/src/games/colors-sort/ColorsSortConstants.ts +0 -119
- package/src/games/colors-sort/ColorsSortGame.ts +0 -246
- package/src/games/colors-sort/ColorsSortScene.ts +0 -315
- package/src/games/connect-em-all/ConnectEmAll.tsx +0 -70
- package/src/games/connect-em-all/ConnectEmAllConstants.ts +0 -77
- package/src/games/connect-em-all/ConnectEmAllGame.ts +0 -401
- package/src/games/connect-em-all/ConnectEmAllScene.ts +0 -235
- package/src/games/crossy-road/CrossyRoad.tsx +0 -74
- package/src/games/crossy-road/CrossyRoadCamera.ts +0 -59
- package/src/games/crossy-road/CrossyRoadConstants.ts +0 -132
- package/src/games/crossy-road/CrossyRoadGame.ts +0 -293
- package/src/games/crossy-road/CrossyRoadMeshes.ts +0 -195
- package/src/games/crossy-road/CrossyRoadPerson.ts +0 -198
- package/src/games/crossy-road/CrossyRoadRules.ts +0 -99
- package/src/games/crossy-road/CrossyRoadScene.ts +0 -355
- package/src/games/dino-jump/DinoJump.tsx +0 -77
- package/src/games/dino-jump/DinoJumpConstants.ts +0 -80
- package/src/games/dino-jump/DinoJumpGame.ts +0 -344
- package/src/games/dino-jump/DinoJumpScene.ts +0 -686
- package/src/games/dots-and-boxes/DotsAndBoxes.tsx +0 -83
- package/src/games/dots-and-boxes/DotsAndBoxesConstants.ts +0 -77
- package/src/games/dots-and-boxes/DotsAndBoxesGame.ts +0 -271
- package/src/games/dots-and-boxes/DotsAndBoxesScene.ts +0 -249
- package/src/games/falling-sticks/FallingSticks.tsx +0 -72
- package/src/games/falling-sticks/FallingSticksConstants.ts +0 -62
- package/src/games/falling-sticks/FallingSticksGame.ts +0 -220
- package/src/games/falling-sticks/FallingSticksRules.ts +0 -145
- package/src/games/falling-sticks/FallingSticksScene.ts +0 -245
- package/src/games/find-different-number/FindDifferentNumber.tsx +0 -74
- package/src/games/find-different-number/FindDifferentNumberConstants.ts +0 -74
- package/src/games/find-different-number/FindDifferentNumberGame.ts +0 -174
- package/src/games/find-different-number/FindDifferentNumberRules.ts +0 -43
- package/src/games/find-different-number/FindDifferentNumberScene.ts +0 -193
- package/src/games/flappy-bird/FlappyBird.tsx +0 -73
- package/src/games/flappy-bird/FlappyBirdConstants.ts +0 -95
- package/src/games/flappy-bird/FlappyBirdGame.ts +0 -375
- package/src/games/flappy-bird/FlappyBirdScene.ts +0 -630
- package/src/games/fruit-merger/FruitMerger.tsx +0 -73
- package/src/games/fruit-merger/FruitMergerConstants.ts +0 -102
- package/src/games/fruit-merger/FruitMergerGame.ts +0 -285
- package/src/games/fruit-merger/FruitMergerRules.ts +0 -286
- package/src/games/fruit-merger/FruitMergerScene.ts +0 -498
- package/src/games/fruit-ninja/FruitNinja.tsx +0 -129
- package/src/games/fruit-ninja/FruitNinjaConstants.ts +0 -107
- package/src/games/fruit-ninja/FruitNinjaGame.ts +0 -391
- package/src/games/fruit-ninja/FruitNinjaScene.ts +0 -319
- package/src/games/fruit-ninja/FruitNinjaWorld.ts +0 -17
- package/src/games/game-2048/Game2048.tsx +0 -84
- package/src/games/game-2048/Game2048Constants.ts +0 -68
- package/src/games/game-2048/Game2048Game.ts +0 -181
- package/src/games/game-2048/Game2048Rules.ts +0 -101
- package/src/games/game-2048/Game2048Scene.ts +0 -206
- package/src/games/knife-hit/KnifeHit.tsx +0 -78
- package/src/games/knife-hit/KnifeHitConstants.ts +0 -89
- package/src/games/knife-hit/KnifeHitGame.ts +0 -264
- package/src/games/knife-hit/KnifeHitScene.ts +0 -360
- package/src/games/ludo-king/LudoKing.tsx +0 -86
- package/src/games/ludo-king/LudoKingConstants.ts +0 -212
- package/src/games/ludo-king/LudoKingGame.ts +0 -326
- package/src/games/ludo-king/LudoKingRules.ts +0 -143
- package/src/games/ludo-king/LudoKingScene.ts +0 -397
- package/src/games/maze-runner/MazeRunner.tsx +0 -88
- package/src/games/maze-runner/MazeRunnerConstants.ts +0 -65
- package/src/games/maze-runner/MazeRunnerGame.ts +0 -176
- package/src/games/maze-runner/MazeRunnerRules.ts +0 -161
- package/src/games/maze-runner/MazeRunnerScene.ts +0 -245
- package/src/games/memory-match/MemoryMatch.tsx +0 -77
- package/src/games/memory-match/MemoryMatchConstants.ts +0 -105
- package/src/games/memory-match/MemoryMatchGame.ts +0 -222
- package/src/games/memory-match/MemoryMatchRules.ts +0 -38
- package/src/games/memory-match/MemoryMatchScene.ts +0 -268
- package/src/games/mine-sweeper/MineSweeper.tsx +0 -77
- package/src/games/mine-sweeper/MineSweeperConstants.ts +0 -94
- package/src/games/mine-sweeper/MineSweeperGame.ts +0 -234
- package/src/games/mine-sweeper/MineSweeperRules.ts +0 -67
- package/src/games/mine-sweeper/MineSweeperScene.ts +0 -262
- package/src/games/mob-control/MobControl.tsx +0 -73
- package/src/games/mob-control/MobControlConstants.ts +0 -106
- package/src/games/mob-control/MobControlGame.ts +0 -176
- package/src/games/mob-control/MobControlMeshes.ts +0 -85
- package/src/games/mob-control/MobControlRenderer.ts +0 -108
- package/src/games/mob-control/MobControlRules.ts +0 -522
- package/src/games/mob-control/MobControlScene.ts +0 -399
- package/src/games/number-search/NumberSearch.tsx +0 -18
- package/src/games/number-search/NumberSearchNumbers.ts +0 -623
- package/src/games/nuts-and-bolts/NutsAndBolts.tsx +0 -85
- package/src/games/nuts-and-bolts/NutsAndBoltsConstants.ts +0 -75
- package/src/games/nuts-and-bolts/NutsAndBoltsGame.ts +0 -298
- package/src/games/nuts-and-bolts/NutsAndBoltsRules.ts +0 -474
- package/src/games/nuts-and-bolts/NutsAndBoltsScene.ts +0 -498
- package/src/games/pac-man/PacMan.tsx +0 -86
- package/src/games/pac-man/PacManConstants.ts +0 -169
- package/src/games/pac-man/PacManGame.ts +0 -406
- package/src/games/pac-man/PacManRules.ts +0 -255
- package/src/games/pac-man/PacManScene.ts +0 -387
- package/src/games/paper-io/PaperIo.tsx +0 -91
- package/src/games/paper-io/PaperIoConstants.ts +0 -86
- package/src/games/paper-io/PaperIoGame.ts +0 -193
- package/src/games/paper-io/PaperIoMeshes.ts +0 -233
- package/src/games/paper-io/PaperIoRules.ts +0 -315
- package/src/games/paper-io/PaperIoScene.ts +0 -310
- package/src/games/penguin-run/PenguinMeshes.ts +0 -269
- package/src/games/penguin-run/PenguinModel.ts +0 -712
- package/src/games/penguin-run/PenguinRun.tsx +0 -77
- package/src/games/penguin-run/PenguinRunConstants.ts +0 -69
- package/src/games/penguin-run/PenguinRunGame.ts +0 -406
- package/src/games/penguin-run/PenguinRunScene.ts +0 -949
- package/src/games/perfect-circle/PerfectCircle.tsx +0 -74
- package/src/games/perfect-circle/PerfectCircleConstants.ts +0 -52
- package/src/games/perfect-circle/PerfectCircleGame.ts +0 -153
- package/src/games/perfect-circle/PerfectCircleRules.ts +0 -111
- package/src/games/perfect-circle/PerfectCircleScene.ts +0 -176
- package/src/games/pipe-connect/PipeConnect.tsx +0 -69
- package/src/games/pipe-connect/PipeConnectConstants.ts +0 -101
- package/src/games/pipe-connect/PipeConnectGame.ts +0 -179
- package/src/games/pipe-connect/PipeConnectRules.ts +0 -326
- package/src/games/pipe-connect/PipeConnectScene.ts +0 -271
- package/src/games/play-with-tua/PlayWithTua.tsx +0 -123
- package/src/games/play-with-tua/PlayWithTuaConstants.ts +0 -70
- package/src/games/play-with-tua/PlayWithTuaGame.ts +0 -167
- package/src/games/play-with-tua/PlayWithTuaModel.ts +0 -42
- package/src/games/play-with-tua/PlayWithTuaScene.ts +0 -36
- package/src/games/popit-fidget/PopitFidget.tsx +0 -72
- package/src/games/popit-fidget/PopitFidgetConstants.ts +0 -71
- package/src/games/popit-fidget/PopitFidgetGame.ts +0 -148
- package/src/games/popit-fidget/PopitFidgetScene.ts +0 -242
- package/src/games/reversi/Reversi.tsx +0 -94
- package/src/games/reversi/ReversiConstants.ts +0 -67
- package/src/games/reversi/ReversiGame.ts +0 -266
- package/src/games/reversi/ReversiRules.ts +0 -176
- package/src/games/reversi/ReversiScene.ts +0 -240
- package/src/games/search-grid/SearchGrid.tsx +0 -92
- package/src/games/search-grid/SearchGridConstants.ts +0 -178
- package/src/games/search-grid/SearchGridGame.ts +0 -209
- package/src/games/search-grid/SearchGridScene.ts +0 -301
- package/src/games/shared/BackdropCard.tsx +0 -54
- package/src/games/shared/EngineBackdrop.ts +0 -300
- package/src/games/shared/EngineBlasts.ts +0 -136
- package/src/games/shared/EngineBlocks.ts +0 -91
- package/src/games/shared/EngineControlText.ts +0 -17
- package/src/games/shared/EngineDice.ts +0 -250
- package/src/games/shared/EngineFootprint.ts +0 -35
- package/src/games/shared/EngineFruits.ts +0 -1435
- package/src/games/shared/EngineGameLoader.tsx +0 -88
- package/src/games/shared/EngineGameScreen.tsx +0 -325
- package/src/games/shared/EngineGameWidth.ts +0 -3
- package/src/games/shared/EngineGameWidth.web.ts +0 -3
- package/src/games/shared/EngineGlyphs.ts +0 -178
- package/src/games/shared/EngineHud.ts +0 -815
- package/src/games/shared/EngineLoading.ts +0 -477
- package/src/games/shared/EngineMeadow.ts +0 -725
- package/src/games/shared/EngineMeshCache.ts +0 -15
- package/src/games/shared/EngineMeshes.ts +0 -10
- package/src/games/shared/EngineRacingChase.ts +0 -21
- package/src/games/shared/EngineRacingHandling.ts +0 -48
- package/src/games/shared/EngineReadyContext.ts +0 -4
- package/src/games/shared/EngineTilt.ts +0 -44
- package/src/games/shared/EngineTouchSurface.tsx +0 -103
- package/src/games/shared/EngineUtils.ts +0 -52
- package/src/games/shared/GameActivity.ts +0 -13
- package/src/games/shared/GameLoading.tsx +0 -202
- package/src/games/shared/GameLoadingTransition.tsx +0 -61
- package/src/games/shared/ProceduralCacheVersion.ts +0 -2
- package/src/games/shared/useEngineGame.ts +0 -240
- package/src/games/shared/useGameResource.ts +0 -21
- package/src/games/shared/useLiveState.ts +0 -58
- package/src/games/sliding-numbers/SlidingNumbers.tsx +0 -71
- package/src/games/sliding-numbers/SlidingNumbersConstants.ts +0 -65
- package/src/games/sliding-numbers/SlidingNumbersGame.ts +0 -192
- package/src/games/sliding-numbers/SlidingNumbersRules.ts +0 -79
- package/src/games/sliding-numbers/SlidingNumbersScene.ts +0 -193
- package/src/games/snake-3d/Snake3D.tsx +0 -76
- package/src/games/snake-3d/Snake3DConstants.ts +0 -61
- package/src/games/snake-3d/Snake3DGame.ts +0 -192
- package/src/games/snake-3d/Snake3DMotion.ts +0 -22
- package/src/games/snake-3d/Snake3DRules.ts +0 -116
- package/src/games/snake-3d/Snake3DScene.ts +0 -322
- package/src/games/snakes-and-ladders/SnakesAndLadders.tsx +0 -87
- package/src/games/snakes-and-ladders/SnakesAndLaddersConstants.ts +0 -136
- package/src/games/snakes-and-ladders/SnakesAndLaddersGame.ts +0 -310
- package/src/games/snakes-and-ladders/SnakesAndLaddersScene.ts +0 -515
- package/src/games/space-fighter/SpaceFighter.tsx +0 -74
- package/src/games/space-fighter/SpaceFighterConstants.ts +0 -86
- package/src/games/space-fighter/SpaceFighterGame.ts +0 -292
- package/src/games/space-fighter/SpaceFighterScene.ts +0 -550
- package/src/games/spider-solitaire/SpiderSolitaire.tsx +0 -80
- package/src/games/spider-solitaire/SpiderSolitaireConstants.ts +0 -107
- package/src/games/spider-solitaire/SpiderSolitaireGame.ts +0 -387
- package/src/games/spider-solitaire/SpiderSolitaireRules.ts +0 -123
- package/src/games/spider-solitaire/SpiderSolitaireScene.ts +0 -352
- package/src/games/stack-tower/StackTower.tsx +0 -76
- package/src/games/stack-tower/StackTowerConstants.ts +0 -74
- package/src/games/stack-tower/StackTowerGame.ts +0 -236
- package/src/games/stack-tower/StackTowerScene.ts +0 -186
- package/src/games/sudoku/Sudoku.tsx +0 -73
- package/src/games/sudoku/SudokuConstants.ts +0 -127
- package/src/games/sudoku/SudokuGame.ts +0 -250
- package/src/games/sudoku/SudokuRules.ts +0 -125
- package/src/games/sudoku/SudokuScene.ts +0 -304
- package/src/games/tangram/Tangram.tsx +0 -79
- package/src/games/tangram/TangramConstants.ts +0 -64
- package/src/games/tangram/TangramGame.ts +0 -356
- package/src/games/tangram/TangramRules.ts +0 -409
- package/src/games/tangram/TangramScene.ts +0 -203
- package/src/games/tank-1990/Tank1990.tsx +0 -78
- package/src/games/tank-1990/Tank1990Constants.ts +0 -81
- package/src/games/tank-1990/Tank1990Game.ts +0 -331
- package/src/games/tank-1990/Tank1990Rules.ts +0 -390
- package/src/games/tank-1990/Tank1990Scene.ts +0 -434
- package/src/games/tic-tac-toe/TicTacToe.tsx +0 -98
- package/src/games/tic-tac-toe/TicTacToeConstants.ts +0 -69
- package/src/games/tic-tac-toe/TicTacToeGame.ts +0 -200
- package/src/games/tic-tac-toe/TicTacToeRules.ts +0 -103
- package/src/games/tic-tac-toe/TicTacToeScene.ts +0 -245
- package/src/games/tile-home/TileHome.tsx +0 -92
- package/src/games/tile-home/TileHomeConstants.ts +0 -139
- package/src/games/tile-home/TileHomeGame.ts +0 -435
- package/src/games/tile-home/TileHomeScene.ts +0 -154
- package/src/games/treasure-hunt/TreasureHunt.tsx +0 -206
- package/src/games/treasure-hunt/TreasureHuntConstants.ts +0 -266
- package/src/games/treasure-hunt/TreasureHuntControls.ts +0 -280
- package/src/games/treasure-hunt/TreasureHuntGame.ts +0 -402
- package/src/games/treasure-hunt/TreasureHuntMeshes.ts +0 -422
- package/src/games/treasure-hunt/TreasureHuntModel.ts +0 -506
- package/src/games/treasure-hunt/TreasureHuntRules.ts +0 -1210
- package/src/games/treasure-hunt/TreasureHuntScene.ts +0 -586
- package/src/games/whack-a-mole/WhackAMole.tsx +0 -79
- package/src/games/whack-a-mole/WhackAMoleConstants.ts +0 -94
- package/src/games/whack-a-mole/WhackAMoleGame.ts +0 -233
- package/src/games/whack-a-mole/WhackAMoleScene.ts +0 -349
- package/src/games/word-search/WordSearch.tsx +0 -18
- package/src/games/word-search/WordSearchWords.ts +0 -1642
- package/src/helpers/GamePatternData.ts +0 -120
- package/src/helpers/GameSettingsModal.tsx +0 -384
- package/src/helpers/GameTheme.tsx +0 -37
- package/src/helpers/IconPaths.ts +0 -6
- package/src/helpers/index.ts +0 -2
- package/src/index.tsx +0 -60
- package/src/services/GameAssetLoader.ts +0 -111
- package/src/services/GameAssets.ts +0 -149
- package/src/services/GameLoaders.ts +0 -93
- package/src/services/GamesConstants.ts +0 -193
- package/src/services/GamesMetadata.ts +0 -711
- package/src/services/GamesService.ts +0 -116
- package/src/services/HapticsService.ts +0 -48
- package/src/services/ScoringService.ts +0 -19
- package/src/services/SfxSources.ts +0 -81
- package/src/services/SoundsService.ts +0 -582
- package/src/services/ThemeService.ts +0 -281
- package/src/services/UtilsService.ts +0 -134
- package/src/services/index.ts +0 -6
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
// Pipe Connect's rules, pieces, timings and layout. Rules, puzzles and scoring match the original version
|
|
2
|
-
// (games/pipe-connect), so scores carry over.
|
|
3
|
-
import { getScoreBoardTop } from '../shared/EngineHud';
|
|
4
|
-
import type { TiltView } from '../shared/EngineTilt';
|
|
5
|
-
import { frontLight, gameWidthFor } from '../shared/EngineUtils';
|
|
6
|
-
|
|
7
|
-
// ── Rules ─────────────────────────────────────────────────────────────
|
|
8
|
-
|
|
9
|
-
export const TIME_LIMIT = 120; // seconds
|
|
10
|
-
export const POINTS_PER_PUZZLE = 5000; // the base calculateTimeBasedScore adds the time bonus to
|
|
11
|
-
|
|
12
|
-
/** The grid is 7 x 7 on a phone, 8 x 8 on a tablet (as the original game decided it). */
|
|
13
|
-
export const gridSizeFor = (tablet: boolean) => (tablet ? 8 : 7);
|
|
14
|
-
|
|
15
|
-
export enum PipeType {
|
|
16
|
-
EMPTY = 'empty',
|
|
17
|
-
STRAIGHT = 'straight',
|
|
18
|
-
CORNER = 'corner',
|
|
19
|
-
CROSS = 'cross',
|
|
20
|
-
T_SHAPE = 't-shape',
|
|
21
|
-
SOURCE = 'source',
|
|
22
|
-
SINK = 'sink'
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** Open sides, as a bitmask. */
|
|
26
|
-
export const DIRECTION = { TOP: 1, RIGHT: 2, BOTTOM: 4, LEFT: 8 } as const;
|
|
27
|
-
const ALL = DIRECTION.TOP | DIRECTION.RIGHT | DIRECTION.BOTTOM | DIRECTION.LEFT;
|
|
28
|
-
|
|
29
|
-
/** Each piece's open sides at rotations 0..3 (a quarter turn clockwise each). */
|
|
30
|
-
export const PIPE_OPENINGS: Record<PipeType, number[]> = {
|
|
31
|
-
[PipeType.EMPTY]: [0, 0, 0, 0],
|
|
32
|
-
[PipeType.STRAIGHT]: [
|
|
33
|
-
DIRECTION.TOP | DIRECTION.BOTTOM,
|
|
34
|
-
DIRECTION.LEFT | DIRECTION.RIGHT,
|
|
35
|
-
DIRECTION.TOP | DIRECTION.BOTTOM,
|
|
36
|
-
DIRECTION.LEFT | DIRECTION.RIGHT
|
|
37
|
-
],
|
|
38
|
-
[PipeType.CORNER]: [
|
|
39
|
-
DIRECTION.TOP | DIRECTION.RIGHT,
|
|
40
|
-
DIRECTION.RIGHT | DIRECTION.BOTTOM,
|
|
41
|
-
DIRECTION.BOTTOM | DIRECTION.LEFT,
|
|
42
|
-
DIRECTION.LEFT | DIRECTION.TOP
|
|
43
|
-
],
|
|
44
|
-
[PipeType.CROSS]: [ALL, ALL, ALL, ALL],
|
|
45
|
-
[PipeType.T_SHAPE]: [
|
|
46
|
-
DIRECTION.TOP | DIRECTION.RIGHT | DIRECTION.BOTTOM,
|
|
47
|
-
DIRECTION.RIGHT | DIRECTION.BOTTOM | DIRECTION.LEFT,
|
|
48
|
-
DIRECTION.BOTTOM | DIRECTION.LEFT | DIRECTION.TOP,
|
|
49
|
-
DIRECTION.LEFT | DIRECTION.TOP | DIRECTION.RIGHT
|
|
50
|
-
],
|
|
51
|
-
[PipeType.SOURCE]: [ALL, ALL, ALL, ALL],
|
|
52
|
-
[PipeType.SINK]: [ALL, ALL, ALL, ALL]
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
// ── Feel (seconds) ────────────────────────────────────────────────────
|
|
56
|
-
|
|
57
|
-
export const TURN_TIME = 0.2; // a pipe swings round a quarter turn, overshooting a touch
|
|
58
|
-
export const FLOW_STEP = 0.045; // water reaches each newly joined pipe this long after the last
|
|
59
|
-
export const WIN_STEP = 0.03; // the win pulse runs out from the source
|
|
60
|
-
|
|
61
|
-
// ── The scene ─────────────────────────────────────────────────────────
|
|
62
|
-
|
|
63
|
-
/** The camera leans back from straight down, so the pipes stand up off their tiles. */
|
|
64
|
-
export const TILT = 0.32;
|
|
65
|
-
/** Lit from the upper left; surfaces facing up off the board (+z) keep their exact color. */
|
|
66
|
-
export const LIGHT = frontLight([-0.4, 0.5, 1], 0.45);
|
|
67
|
-
export const COLORS = {
|
|
68
|
-
PIPE: '#8B9DC3',
|
|
69
|
-
WATER: '#4FC3F7',
|
|
70
|
-
SOURCE: '#4CAF50',
|
|
71
|
-
SINK: '#FF7043',
|
|
72
|
-
CLASSIC_BOARD: 'rgba(255, 255, 255, 0.30)',
|
|
73
|
-
CLASSIC_CELL: 'rgba(255, 255, 255, 0.10)'
|
|
74
|
-
} as const;
|
|
75
|
-
|
|
76
|
-
// ── Layout (view points) ──────────────────────────────────────────────
|
|
77
|
-
|
|
78
|
-
const SCOREBOARD_HEIGHT = 82;
|
|
79
|
-
const CONTROLS_HEIGHT = 110;
|
|
80
|
-
const TABLET_WIDTH = 640;
|
|
81
|
-
|
|
82
|
-
export type Layout = TiltView & { size: number; cell: number; padding: number; board: number };
|
|
83
|
-
|
|
84
|
-
/** Whole-point cells as wide as the game area allows (as the original board), smaller if the screen is short. */
|
|
85
|
-
export function getLayout(width: number, height: number, offset: number, size: number): Layout {
|
|
86
|
-
const tablet = width > TABLET_WIDTH;
|
|
87
|
-
const padding = tablet ? 6 : 4;
|
|
88
|
-
const gameWidth = gameWidthFor(width);
|
|
89
|
-
const top = getScoreBoardTop(offset) + SCOREBOARD_HEIGHT;
|
|
90
|
-
const room = Math.max(200, height - top - CONTROLS_HEIGHT);
|
|
91
|
-
const cell = Math.max(10, Math.floor(Math.min((gameWidth - padding * 2) / size, (room / Math.cos(TILT) - padding * 2) / size)));
|
|
92
|
-
return { width, height, tilt: TILT, centerY: top + room / 2, size, cell, padding, board: cell * size + padding * 2 };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export const isTabletWidth = (width: number) => width > TABLET_WIDTH;
|
|
96
|
-
|
|
97
|
-
/** A cell's centre on the board (row 0 along the top), in world units. */
|
|
98
|
-
export function cellCentre(l: Layout, row: number, col: number): [number, number] {
|
|
99
|
-
const half = l.board / 2;
|
|
100
|
-
return [-half + l.padding + (col + 0.5) * l.cell, half - l.padding - (row + 0.5) * l.cell];
|
|
101
|
-
}
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
// Pipe Connect's play, with no React: build a game with `createGame` (it deals a puzzle), give it a
|
|
2
|
-
// layout, `start` it and `tick` it every frame. Tap a pipe to turn it a quarter turn clockwise; the
|
|
3
|
-
// water runs from the source through every pipe joined up to it. Reach the sink inside five minutes to
|
|
4
|
-
// win (scored by the time it took, as the original version did). The store's rules, ported as they were.
|
|
5
|
-
import { type Camera, World } from '@nayan-ui/engine';
|
|
6
|
-
import { calculateTimeBasedScore } from '../../services/ScoringService';
|
|
7
|
-
import type { GameTheme } from '../../services/ThemeService';
|
|
8
|
-
import { aimTilted, unproject } from '../shared/EngineTilt';
|
|
9
|
-
import { type Layout, PipeType, POINTS_PER_PUZZLE, TIME_LIMIT } from './PipeConnectConstants';
|
|
10
|
-
import { findConnectedPath, generatePuzzle, isPuzzleCompleteFromSet, type PipeCell, rotatePipe } from './PipeConnectRules';
|
|
11
|
-
import { buildScene, flow, removeScene, type Scene, setSceneTheme, splash, turnPiece, winPulse } from './PipeConnectScene';
|
|
12
|
-
|
|
13
|
-
export type Phase = 'ready' | 'playing' | 'over';
|
|
14
|
-
|
|
15
|
-
const WORLD_CAPACITY = 300; // 64 tiles and pipes at most, the board, and the splash
|
|
16
|
-
|
|
17
|
-
/** The cells the water reaches, each with its place along the way out from the source (1 = the source). */
|
|
18
|
-
const reach = (grid: PipeCell[][], connected = findConnectedPath(grid)) => {
|
|
19
|
-
const order = new Map<string, number>();
|
|
20
|
-
for (const id of connected) order.set(id, order.size + 1);
|
|
21
|
-
return order;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export function createGame(theme: GameTheme, size: number) {
|
|
25
|
-
const world = new World(WORLD_CAPACITY);
|
|
26
|
-
const camera: Camera = { eye: [0, 0, 240], target: [0, 0, 0], fov: 1, ortho: 1 };
|
|
27
|
-
const grid = generatePuzzle(size);
|
|
28
|
-
const g = {
|
|
29
|
-
world,
|
|
30
|
-
camera,
|
|
31
|
-
theme,
|
|
32
|
-
size,
|
|
33
|
-
layout: null as Layout | null,
|
|
34
|
-
scene: null as Scene | null,
|
|
35
|
-
phase: 'ready' as Phase,
|
|
36
|
-
grid,
|
|
37
|
-
wet: reach(grid),
|
|
38
|
-
/** Score and the countdown live in the engine (Rust), advanced by world.update. */
|
|
39
|
-
get score() {
|
|
40
|
-
return world.scoreboard.score;
|
|
41
|
-
},
|
|
42
|
-
get time() {
|
|
43
|
-
return world.scoreboard.shownSeconds;
|
|
44
|
-
},
|
|
45
|
-
won: false,
|
|
46
|
-
// UI hooks, set by the screen.
|
|
47
|
-
onPhase: (_p: Phase) => {},
|
|
48
|
-
onTime: (_t: number) => {},
|
|
49
|
-
/** A pipe turned; `solved` when that turn reached the sink. */
|
|
50
|
-
onTurn: (_solved: boolean) => {}
|
|
51
|
-
};
|
|
52
|
-
world.scoreboard.reset(0, TIME_LIMIT);
|
|
53
|
-
world.scoreboard.handlers = {
|
|
54
|
-
onSecond: (t) => g.onTime(t),
|
|
55
|
-
onTimeUp: () => {
|
|
56
|
-
if (g.phase !== 'playing') return;
|
|
57
|
-
g.won = false;
|
|
58
|
-
g.phase = 'over';
|
|
59
|
-
g.onPhase('over');
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
return g;
|
|
63
|
-
}
|
|
64
|
-
export type Game = ReturnType<typeof createGame>;
|
|
65
|
-
|
|
66
|
-
const index = (g: Game, row: number, col: number) => row * g.size + col;
|
|
67
|
-
|
|
68
|
-
// ── Scene ─────────────────────────────────────────────────────────────
|
|
69
|
-
|
|
70
|
-
/** Lays the board out for the view (first layout, header height, rotation): rebuilt from the puzzle's state. */
|
|
71
|
-
export function setLayout(g: Game, l: Layout) {
|
|
72
|
-
if (g.scene) removeScene(g.world, g.scene);
|
|
73
|
-
g.layout = l;
|
|
74
|
-
aimTilted(g.camera, l);
|
|
75
|
-
g.scene = buildScene(g.world, l, g.theme, g.grid, new Set(g.wet.keys()));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function setTheme(g: Game, theme: GameTheme) {
|
|
79
|
-
g.theme = theme;
|
|
80
|
-
if (g.layout && g.scene) setSceneTheme(g.world, g.layout, g.scene, theme);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// ── Run ───────────────────────────────────────────────────────────────
|
|
84
|
-
|
|
85
|
-
/** Plays the puzzle that's showing. */
|
|
86
|
-
export function start(g: Game) {
|
|
87
|
-
if (g.phase !== 'ready') return;
|
|
88
|
-
g.phase = 'playing';
|
|
89
|
-
g.world.scoreboard.startCountdown(TIME_LIMIT);
|
|
90
|
-
g.onPhase('playing');
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** A fresh puzzle, not started (Stop, Play Again). The world, and the GPU view drawing it, are kept. */
|
|
94
|
-
export function reset(g: Game) {
|
|
95
|
-
g.grid = generatePuzzle(g.size);
|
|
96
|
-
g.wet = reach(g.grid);
|
|
97
|
-
g.world.scoreboard.reset(0, TIME_LIMIT);
|
|
98
|
-
g.won = false;
|
|
99
|
-
g.phase = 'ready';
|
|
100
|
-
if (g.layout) setLayout(g, g.layout);
|
|
101
|
-
g.onTime(TIME_LIMIT);
|
|
102
|
-
g.onPhase('ready');
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export function dispose(g: Game) {
|
|
106
|
-
g.world.dispose();
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export function tick(g: Game, dt: number) {
|
|
110
|
-
if (g.world.isDisposed) return;
|
|
111
|
-
g.world.update(dt);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// ── Taps ──────────────────────────────────────────────────────────────
|
|
115
|
-
|
|
116
|
-
/** The cell under a view point (as seen on the pipes, a little above the tiles). */
|
|
117
|
-
export function cellAt(g: Game, x: number, y: number): [number, number] | null {
|
|
118
|
-
const l = g.layout;
|
|
119
|
-
if (!l) return null;
|
|
120
|
-
const [wx, wy] = unproject(l, x, y, l.cell * 0.15);
|
|
121
|
-
const half = l.board / 2;
|
|
122
|
-
const col = Math.floor((wx + half - l.padding) / l.cell);
|
|
123
|
-
const row = Math.floor((half - l.padding - wy) / l.cell);
|
|
124
|
-
return row >= 0 && row < g.size && col >= 0 && col < g.size ? [row, col] : null;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export function tap(g: Game, x: number, y: number) {
|
|
128
|
-
const at = cellAt(g, x, y);
|
|
129
|
-
if (at) rotateAt(g, at[0], at[1]);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** Turns the pipe in a cell (not the source, sink or an empty one), then lets the water find its way. */
|
|
133
|
-
export function rotateAt(g: Game, row: number, col: number) {
|
|
134
|
-
if (g.phase !== 'playing') return;
|
|
135
|
-
const cell = g.grid[row]?.[col];
|
|
136
|
-
if (!cell || cell.fixed || cell.type === PipeType.EMPTY) return;
|
|
137
|
-
const rotated = rotatePipe(cell);
|
|
138
|
-
g.grid[row]![col] = rotated;
|
|
139
|
-
const before = g.wet;
|
|
140
|
-
const connected = findConnectedPath(g.grid);
|
|
141
|
-
const complete = isPuzzleCompleteFromSet(g.grid, connected);
|
|
142
|
-
g.wet = reach(g.grid, connected);
|
|
143
|
-
|
|
144
|
-
const { layout: l, scene: s } = g;
|
|
145
|
-
if (l && s) {
|
|
146
|
-
const e = s.pieces[index(g, row, col)];
|
|
147
|
-
if (e != null) turnPiece(g.world, e, rotated.rotation);
|
|
148
|
-
// The first newly reached pipe's place along the path: the rest follow on from it
|
|
149
|
-
let base = 0;
|
|
150
|
-
for (const [id, order] of g.wet) if (!before.has(id) && (!base || order < base)) base = order;
|
|
151
|
-
for (const r of g.grid)
|
|
152
|
-
for (const c of r) {
|
|
153
|
-
const piece = s.pieces[index(g, c.row, c.col)];
|
|
154
|
-
if (piece == null) continue;
|
|
155
|
-
const now = g.wet.get(c.id);
|
|
156
|
-
if (!!now !== before.has(c.id)) flow(g.world, l, piece, c, !!now, now ?? 0, base);
|
|
157
|
-
}
|
|
158
|
-
if (complete) {
|
|
159
|
-
for (const [id, order] of g.wet) {
|
|
160
|
-
const [r, c] = id.split('-').map(Number) as [number, number];
|
|
161
|
-
const piece = s.pieces[index(g, r, c)];
|
|
162
|
-
if (piece != null) winPulse(g.world, l, piece, order);
|
|
163
|
-
}
|
|
164
|
-
const sink = g.grid.flat().find((c) => c.type === PipeType.SINK);
|
|
165
|
-
if (sink) splash(g.world, l, sink.row, sink.col);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (complete) {
|
|
170
|
-
g.world.scoreboard.set(calculateTimeBasedScore(TIME_LIMIT - g.time, TIME_LIMIT, g.score + POINTS_PER_PUZZLE));
|
|
171
|
-
g.world.scoreboard.stop();
|
|
172
|
-
g.won = true;
|
|
173
|
-
g.phase = 'over';
|
|
174
|
-
g.onTurn(true);
|
|
175
|
-
g.onPhase('over');
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
g.onTurn(false);
|
|
179
|
-
}
|
|
@@ -1,326 +0,0 @@
|
|
|
1
|
-
// Pipe Connect's puzzles and moves, as plain functions: a winding path of pipes from a source on the
|
|
2
|
-
// left edge to a sink on the right, every other cell filled with a decoy, all scrambled; turning a pipe;
|
|
3
|
-
// and which pipes the water reaches. The original version's service (games/pipe-connect), unchanged but for
|
|
4
|
-
// taking the grid size as an argument, so every puzzle plays the same.
|
|
5
|
-
import { DIRECTION, PIPE_OPENINGS, PipeType } from './PipeConnectConstants';
|
|
6
|
-
|
|
7
|
-
export interface PipeCell {
|
|
8
|
-
id: string;
|
|
9
|
-
row: number;
|
|
10
|
-
col: number;
|
|
11
|
-
type: PipeType;
|
|
12
|
-
rotation: number; // 0-3 (0°, 90°, 180°, 270°)
|
|
13
|
-
filled: boolean; // water flowing through
|
|
14
|
-
fixed: boolean; // source/sink — cannot rotate
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Get the open directions for a pipe cell
|
|
18
|
-
export const getOpenDirections = (cell: PipeCell): number => {
|
|
19
|
-
const openings = PIPE_OPENINGS[cell.type];
|
|
20
|
-
if (!openings) return 0;
|
|
21
|
-
return openings[cell.rotation % 4] ?? 0;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
// Check if two adjacent cells are connected
|
|
25
|
-
const areConnected = (cellA: PipeCell, cellB: PipeCell): boolean => {
|
|
26
|
-
const openA = getOpenDirections(cellA);
|
|
27
|
-
const openB = getOpenDirections(cellB);
|
|
28
|
-
|
|
29
|
-
const dRow = cellB.row - cellA.row;
|
|
30
|
-
const dCol = cellB.col - cellA.col;
|
|
31
|
-
|
|
32
|
-
if (dRow === -1 && dCol === 0) {
|
|
33
|
-
return (openA & DIRECTION.TOP) !== 0 && (openB & DIRECTION.BOTTOM) !== 0;
|
|
34
|
-
}
|
|
35
|
-
if (dRow === 1 && dCol === 0) {
|
|
36
|
-
return (openA & DIRECTION.BOTTOM) !== 0 && (openB & DIRECTION.TOP) !== 0;
|
|
37
|
-
}
|
|
38
|
-
if (dRow === 0 && dCol === -1) {
|
|
39
|
-
return (openA & DIRECTION.LEFT) !== 0 && (openB & DIRECTION.RIGHT) !== 0;
|
|
40
|
-
}
|
|
41
|
-
if (dRow === 0 && dCol === 1) {
|
|
42
|
-
return (openA & DIRECTION.RIGHT) !== 0 && (openB & DIRECTION.LEFT) !== 0;
|
|
43
|
-
}
|
|
44
|
-
return false;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
// BFS to find all cells connected to the source via properly connected pipes
|
|
48
|
-
export const findConnectedPath = (grid: PipeCell[][]): Set<string> => {
|
|
49
|
-
const connected = new Set<string>();
|
|
50
|
-
const queue: PipeCell[] = [];
|
|
51
|
-
|
|
52
|
-
for (const row of grid) {
|
|
53
|
-
for (const cell of row) {
|
|
54
|
-
if (cell.type === PipeType.SOURCE) {
|
|
55
|
-
queue.push(cell);
|
|
56
|
-
connected.add(cell.id);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
for (let head = 0; head < queue.length; head++) {
|
|
62
|
-
const current = queue[head]!;
|
|
63
|
-
const neighbors = getNeighbors(grid, current.row, current.col);
|
|
64
|
-
|
|
65
|
-
for (const neighbor of neighbors) {
|
|
66
|
-
if (connected.has(neighbor.id)) continue;
|
|
67
|
-
if (neighbor.type === PipeType.EMPTY) continue;
|
|
68
|
-
|
|
69
|
-
if (areConnected(current, neighbor)) {
|
|
70
|
-
connected.add(neighbor.id);
|
|
71
|
-
queue.push(neighbor);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return connected;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// Check completion using pre-computed connected set (avoids double BFS)
|
|
80
|
-
export const isPuzzleCompleteFromSet = (grid: PipeCell[][], connected: Set<string>): boolean => {
|
|
81
|
-
for (const row of grid) {
|
|
82
|
-
for (const cell of row) {
|
|
83
|
-
if (cell.type === PipeType.SINK && connected.has(cell.id)) {
|
|
84
|
-
return true;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return false;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
// Check if source is connected to sink
|
|
92
|
-
export const isPuzzleComplete = (grid: PipeCell[][]): boolean => {
|
|
93
|
-
const connected = findConnectedPath(grid);
|
|
94
|
-
return isPuzzleCompleteFromSet(grid, connected);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// Get adjacent cells
|
|
98
|
-
const getNeighbors = (grid: PipeCell[][], row: number, col: number): PipeCell[] => {
|
|
99
|
-
const neighbors: PipeCell[] = [];
|
|
100
|
-
if (row > 0) neighbors.push(grid[row - 1]![col]!);
|
|
101
|
-
if (row < grid.length - 1) neighbors.push(grid[row + 1]![col]!);
|
|
102
|
-
if (col > 0) neighbors.push(grid[row]![col - 1]!);
|
|
103
|
-
if (col < (grid[0]?.length ?? 0) - 1) neighbors.push(grid[row]![col + 1]!);
|
|
104
|
-
return neighbors;
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
interface PathNode {
|
|
108
|
-
row: number;
|
|
109
|
-
col: number;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// A short, near-straight run from source to sink could be completed in a handful of taps, so the
|
|
113
|
-
// two ends are kept well apart in row and the walk between them is left free to wander.
|
|
114
|
-
const minEndRowGap = (rows: number) => Math.max(2, Math.floor(rows / 2));
|
|
115
|
-
|
|
116
|
-
// Generate a solvable puzzle — fills entire grid with pipes
|
|
117
|
-
export const generatePuzzle = (size: number): PipeCell[][] => {
|
|
118
|
-
const rows = size;
|
|
119
|
-
const cols = size;
|
|
120
|
-
|
|
121
|
-
// Create empty grid
|
|
122
|
-
const grid: PipeCell[][] = [];
|
|
123
|
-
for (let r = 0; r < rows; r++) {
|
|
124
|
-
const row: PipeCell[] = [];
|
|
125
|
-
for (let c = 0; c < cols; c++) {
|
|
126
|
-
row.push({
|
|
127
|
-
id: `${r}-${c}`,
|
|
128
|
-
row: r,
|
|
129
|
-
col: c,
|
|
130
|
-
type: PipeType.EMPTY,
|
|
131
|
-
rotation: 0,
|
|
132
|
-
filled: false,
|
|
133
|
-
fixed: false
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
grid.push(row);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Source and sink sit on opposite edges, and far enough apart in row that no straight shot exists
|
|
140
|
-
const sourceRow = Math.floor(Math.random() * rows);
|
|
141
|
-
let sinkRow = Math.floor(Math.random() * rows);
|
|
142
|
-
const gap = minEndRowGap(rows);
|
|
143
|
-
if (Math.abs(sinkRow - sourceRow) < gap) {
|
|
144
|
-
sinkRow = sourceRow < rows / 2 ? Math.min(rows - 1, sourceRow + gap) : Math.max(0, sourceRow - gap);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
grid[sourceRow]![0] = {
|
|
148
|
-
...grid[sourceRow]![0]!,
|
|
149
|
-
type: PipeType.SOURCE,
|
|
150
|
-
fixed: true
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
grid[sinkRow]![cols - 1] = {
|
|
154
|
-
...grid[sinkRow]![cols - 1]!,
|
|
155
|
-
type: PipeType.SINK,
|
|
156
|
-
fixed: true
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
// Build a random winding path from source to sink
|
|
160
|
-
const path = buildRandomPath(sourceRow, 0, sinkRow, cols - 1, rows, cols);
|
|
161
|
-
|
|
162
|
-
// Place pipes along the main path
|
|
163
|
-
placePipesAlongPath(grid, path);
|
|
164
|
-
|
|
165
|
-
// Fill ALL remaining empty cells with pipes to make the grid dense
|
|
166
|
-
fillEmptyCells(grid, rows, cols);
|
|
167
|
-
|
|
168
|
-
// Scramble all non-fixed pipe rotations
|
|
169
|
-
scrambleRotations(grid);
|
|
170
|
-
|
|
171
|
-
return grid;
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
// Build a random winding path from source to sink
|
|
175
|
-
const buildRandomPath = (startRow: number, startCol: number, endRow: number, endCol: number, rows: number, cols: number): PathNode[] => {
|
|
176
|
-
const visited = new Set<string>();
|
|
177
|
-
const path: PathNode[] = [];
|
|
178
|
-
|
|
179
|
-
const dfs = (r: number, c: number): boolean => {
|
|
180
|
-
if (r === endRow && c === endCol) {
|
|
181
|
-
path.push({ row: r, col: c });
|
|
182
|
-
return true;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
visited.add(`${r}-${c}`);
|
|
186
|
-
path.push({ row: r, col: c });
|
|
187
|
-
|
|
188
|
-
// Unsorted on purpose: the walk used to be pulled straight at the sink, which produced a short,
|
|
189
|
-
// obvious run. Picking freely at random lets it wander far off the direct line, and the
|
|
190
|
-
// backtracking below still guarantees it reaches the sink.
|
|
191
|
-
const dirs = shuffleArray([
|
|
192
|
-
{ dr: -1, dc: 0 },
|
|
193
|
-
{ dr: 1, dc: 0 },
|
|
194
|
-
{ dr: 0, dc: -1 },
|
|
195
|
-
{ dr: 0, dc: 1 }
|
|
196
|
-
]);
|
|
197
|
-
|
|
198
|
-
for (const dir of dirs) {
|
|
199
|
-
const nr = r + dir.dr;
|
|
200
|
-
const nc = c + dir.dc;
|
|
201
|
-
|
|
202
|
-
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols && !visited.has(`${nr}-${nc}`)) {
|
|
203
|
-
if (dfs(nr, nc)) return true;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
path.pop();
|
|
208
|
-
return false;
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
dfs(startRow, startCol);
|
|
212
|
-
return path;
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
// Determine pipe type and correct rotation for each cell in the path
|
|
216
|
-
const placePipesAlongPath = (grid: PipeCell[][], path: PathNode[]) => {
|
|
217
|
-
for (let i = 0; i < path.length; i++) {
|
|
218
|
-
const node = path[i]!;
|
|
219
|
-
const cell = grid[node.row]![node.col]!;
|
|
220
|
-
|
|
221
|
-
if (cell.type === PipeType.SOURCE || cell.type === PipeType.SINK) continue;
|
|
222
|
-
|
|
223
|
-
let openDirs = 0;
|
|
224
|
-
if (i > 0) {
|
|
225
|
-
const prev = path[i - 1]!;
|
|
226
|
-
const dr = prev.row - node.row;
|
|
227
|
-
const dc = prev.col - node.col;
|
|
228
|
-
if (dr === -1) openDirs |= DIRECTION.TOP;
|
|
229
|
-
if (dr === 1) openDirs |= DIRECTION.BOTTOM;
|
|
230
|
-
if (dc === -1) openDirs |= DIRECTION.LEFT;
|
|
231
|
-
if (dc === 1) openDirs |= DIRECTION.RIGHT;
|
|
232
|
-
}
|
|
233
|
-
if (i < path.length - 1) {
|
|
234
|
-
const next = path[i + 1]!;
|
|
235
|
-
const dr = next.row - node.row;
|
|
236
|
-
const dc = next.col - node.col;
|
|
237
|
-
if (dr === -1) openDirs |= DIRECTION.TOP;
|
|
238
|
-
if (dr === 1) openDirs |= DIRECTION.BOTTOM;
|
|
239
|
-
if (dc === -1) openDirs |= DIRECTION.LEFT;
|
|
240
|
-
if (dc === 1) openDirs |= DIRECTION.RIGHT;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const match = findPipeForOpenings(openDirs);
|
|
244
|
-
cell.type = match.type;
|
|
245
|
-
cell.rotation = match.rotation;
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
// Find which pipe type + rotation produces the given openings bitmask
|
|
250
|
-
const findPipeForOpenings = (openDirs: number): { type: PipeType; rotation: number } => {
|
|
251
|
-
const types = [PipeType.STRAIGHT, PipeType.CORNER, PipeType.T_SHAPE, PipeType.CROSS];
|
|
252
|
-
for (const type of types) {
|
|
253
|
-
const openings = PIPE_OPENINGS[type]!;
|
|
254
|
-
for (let rot = 0; rot < 4; rot++) {
|
|
255
|
-
if (openings[rot] === openDirs) {
|
|
256
|
-
return { type, rotation: rot };
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return { type: PipeType.STRAIGHT, rotation: 0 };
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
// Fill all remaining empty cells with random pipe types
|
|
264
|
-
const fillEmptyCells = (grid: PipeCell[][], rows: number, cols: number) => {
|
|
265
|
-
// No CROSS filler: it is open on all four sides at every rotation, so each one was a free joint
|
|
266
|
-
// that let water find the sink by accident. T pieces are kept rare for the same reason.
|
|
267
|
-
const pipeTypes = [PipeType.STRAIGHT, PipeType.CORNER, PipeType.T_SHAPE];
|
|
268
|
-
const weights = [0.45, 0.45, 0.1];
|
|
269
|
-
|
|
270
|
-
for (let r = 0; r < rows; r++) {
|
|
271
|
-
for (let c = 0; c < cols; c++) {
|
|
272
|
-
const cell = grid[r]![c]!;
|
|
273
|
-
if (cell.type !== PipeType.EMPTY) continue;
|
|
274
|
-
|
|
275
|
-
// Pick weighted random type
|
|
276
|
-
const rand = Math.random();
|
|
277
|
-
let cumulative = 0;
|
|
278
|
-
let chosenType = PipeType.STRAIGHT;
|
|
279
|
-
for (let i = 0; i < pipeTypes.length; i++) {
|
|
280
|
-
cumulative += weights[i]!;
|
|
281
|
-
if (rand < cumulative) {
|
|
282
|
-
chosenType = pipeTypes[i]!;
|
|
283
|
-
break;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
cell.type = chosenType;
|
|
288
|
-
cell.rotation = Math.floor(Math.random() * 4);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
// Scramble all non-fixed pipe rotations
|
|
294
|
-
const scrambleRotations = (grid: PipeCell[][]) => {
|
|
295
|
-
for (const row of grid) {
|
|
296
|
-
for (const cell of row) {
|
|
297
|
-
if (cell.fixed || cell.type === PipeType.EMPTY) continue;
|
|
298
|
-
if (cell.type === PipeType.CROSS) continue;
|
|
299
|
-
const originalRotation = cell.rotation;
|
|
300
|
-
let newRotation = originalRotation;
|
|
301
|
-
while (newRotation === originalRotation) {
|
|
302
|
-
newRotation = Math.floor(Math.random() * 4);
|
|
303
|
-
}
|
|
304
|
-
cell.rotation = newRotation;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
// Rotate a pipe 90° clockwise
|
|
310
|
-
export const rotatePipe = (cell: PipeCell): PipeCell => {
|
|
311
|
-
if (cell.fixed || cell.type === PipeType.EMPTY) return cell;
|
|
312
|
-
return {
|
|
313
|
-
...cell,
|
|
314
|
-
rotation: (cell.rotation + 1) % 4
|
|
315
|
-
};
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
// Utility: shuffle array (Fisher-Yates)
|
|
319
|
-
const shuffleArray = <T>(arr: T[]): T[] => {
|
|
320
|
-
const result = [...arr];
|
|
321
|
-
for (let i = result.length - 1; i > 0; i--) {
|
|
322
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
323
|
-
[result[i], result[j]] = [result[j]!, result[i]!];
|
|
324
|
-
}
|
|
325
|
-
return result;
|
|
326
|
-
};
|