XTaylorSpenceX

Gravity Flip Gauntlet

Sep 15th, 2025
74
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
HTML 48.90 KB | None | 0 0
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>Gravity Flip Gauntlet</title>
  7.     <style>
  8.         * {
  9.             margin: 0;
  10.             padding: 0;
  11.             box-sizing: border-box;
  12.         }
  13.  
  14.         body {
  15.             background: linear-gradient(135deg, #0a0e27 0%, #1a0b2e 50%, #2e1760 100%);
  16.             font-family: 'Courier New', monospace;
  17.             overflow: hidden;
  18.             position: relative;
  19.             height: 100vh;
  20.             touch-action: none;
  21.         }
  22.  
  23.         #gameCanvas {
  24.             position: absolute;
  25.             top: 50%;
  26.             left: 50%;
  27.             transform: translate(-50%, -50%);
  28.             border: 2px solid #ff00ff;
  29.             box-shadow: 0 0 30px #ff00ff, inset 0 0 30px rgba(255, 0, 255, 0.2);
  30.             transition: transform 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
  31.         }
  32.  
  33.         .flipped {
  34.             transform: translate(-50%, -50%) rotate(180deg) !important;
  35.         }
  36.  
  37.         #ui {
  38.             position: absolute;
  39.             top: 20px;
  40.             left: 20px;
  41.             color: #fff;
  42.             font-size: 18px;
  43.             text-shadow: 0 0 10px #ff00ff;
  44.             z-index: 100;
  45.             background: rgba(0, 0, 0, 0.5);
  46.             padding: 10px;
  47.             border-radius: 10px;
  48.         }
  49.  
  50.         #score {
  51.             font-size: 24px;
  52.             color: #00ffff;
  53.             margin-bottom: 10px;
  54.         }
  55.  
  56.         #multiplier {
  57.             color: #ffff00;
  58.             font-size: 20px;
  59.             animation: pulse 0.5s infinite;
  60.         }
  61.  
  62.         #level {
  63.             color: #ff00ff;
  64.             margin-top: 10px;
  65.         }
  66.  
  67.         #startScreen, #gameOverScreen {
  68.             position: absolute;
  69.             top: 50%;
  70.             left: 50%;
  71.             transform: translate(-50%, -50%);
  72.             text-align: center;
  73.             color: #fff;
  74.             z-index: 200;
  75.             background: rgba(10, 14, 39, 0.9);
  76.             padding: 30px;
  77.             border-radius: 20px;
  78.             border: 2px solid #ff00ff;
  79.             box-shadow: 0 0 40px rgba(255, 0, 255, 0.5);
  80.             width: 90%;
  81.             max-width: 600px;
  82.         }
  83.  
  84.         h1 {
  85.             font-size: 48px;
  86.             background: linear-gradient(45deg, #ff00ff, #00ffff, #ffff00);
  87.             -webkit-background-clip: text;
  88.             -webkit-text-fill-color: transparent;
  89.             background-clip: text;
  90.             margin-bottom: 20px;
  91.             animation: glow 2s ease-in-out infinite;
  92.         }
  93.  
  94.         .instructions {
  95.             font-size: 16px;
  96.             color: #fff;
  97.             margin: 20px 0;
  98.             line-height: 1.6;
  99.         }
  100.  
  101.         .startBtn {
  102.             background: linear-gradient(135deg, #ff00ff, #00ffff);
  103.             border: none;
  104.             padding: 15px 40px;
  105.             font-size: 20px;
  106.             color: #000;
  107.             font-weight: bold;
  108.             cursor: pointer;
  109.             border-radius: 30px;
  110.             transition: all 0.3s;
  111.             margin-top: 20px;
  112.         }
  113.  
  114.         .startBtn:hover {
  115.             transform: scale(1.1);
  116.             box-shadow: 0 0 30px #ff00ff;
  117.         }
  118.  
  119.         #particles {
  120.             position: absolute;
  121.             width: 100%;
  122.             height: 100%;
  123.             pointer-events: none;
  124.         }
  125.  
  126.         .particle {
  127.             position: absolute;
  128.             width: 4px;
  129.             height: 4px;
  130.             background: #fff;
  131.             border-radius: 50%;
  132.             opacity: 0;
  133.             animation: particleFade 1s ease-out;
  134.         }
  135.  
  136.         @keyframes pulse {
  137.             0%, 100% { transform: scale(1); }
  138.             50% { transform: scale(1.2); }
  139.         }
  140.  
  141.         @keyframes glow {
  142.             0%, 100% { filter: drop-shadow(0 0 20px rgba(255, 0, 255, 0.8)); }
  143.             50% { filter: drop-shadow(0 0 40px rgba(0, 255, 255, 1)); }
  144.         }
  145.  
  146.         @keyframes particleFade {
  147.             0% {
  148.                 opacity: 1;
  149.                 transform: translate(0, 0) scale(1);
  150.             }
  151.             100% {
  152.                 opacity: 0;
  153.                 transform: translate(var(--dx), var(--dy)) scale(0);
  154.             }
  155.         }
  156.  
  157.         .combo-text {
  158.             position: absolute;
  159.             color: #ffff00;
  160.             font-size: 36px;
  161.             font-weight: bold;
  162.             pointer-events: none;
  163.             animation: comboFloat 1s ease-out forwards;
  164.             text-shadow: 0 0 20px #ffff00;
  165.         }
  166.  
  167.         @keyframes comboFloat {
  168.             0% {
  169.                 opacity: 1;
  170.                 transform: translate(-50%, -50%) scale(0.5);
  171.             }
  172.             50% {
  173.                 transform: translate(-50%, -100%) scale(1.5);
  174.             }
  175.             100% {
  176.                 opacity: 0;
  177.                 transform: translate(-50%, -150%) scale(1);
  178.             }
  179.         }
  180.  
  181.         .touch-controls {
  182.             position: absolute;
  183.             bottom: 30px;
  184.             width: 100%;
  185.             display: flex;
  186.             justify-content: space-between;
  187.             padding: 0 30px;
  188.             z-index: 150;
  189.         }
  190.  
  191.         .touch-btn {
  192.             width: 70px;
  193.             height: 70px;
  194.             background: rgba(255, 255, 255, 0.2);
  195.             border-radius: 50%;
  196.             display: flex;
  197.             justify-content: center;
  198.             align-items: center;
  199.             color: white;
  200.             font-size: 24px;
  201.             user-select: none;
  202.             border: 2px solid #ff00ff;
  203.         }
  204.  
  205.         .touch-center {
  206.             display: flex;
  207.             gap: 30px;
  208.         }
  209.  
  210.         #flipBtn {
  211.             background: rgba(255, 0, 255, 0.3);
  212.         }
  213.  
  214.         #jumpBtn {
  215.             background: rgba(0, 255, 255, 0.3);
  216.         }
  217.  
  218.         #mobileControls {
  219.             display: none;
  220.         }
  221.  
  222.         @media (max-width: 768px) {
  223.             #mobileControls {
  224.                 display: flex;
  225.             }
  226.            
  227.             #gameCanvas {
  228.                 width: 95%;
  229.                 height: auto;
  230.             }
  231.            
  232.             h1 {
  233.                 font-size: 36px;
  234.             }
  235.            
  236.             .instructions {
  237.                 font-size: 14px;
  238.             }
  239.         }
  240.     </style>
  241. </head>
  242. <body>
  243.     <div id="particles"></div>
  244.    
  245.     <div id="ui">
  246.         <div id="score">SCORE: 0</div>
  247.         <div id="multiplier">MULTIPLIER: x1</div>
  248.         <div id="level">LEVEL: 1</div>
  249.         <div id="flipCount" style="color: #00ff00; margin-top: 10px;">FLIPS: 0</div>
  250.         <div id="aiStatus" style="color: #00ff00; margin-top: 10px; display: none;">
  251.             AI MODE: <span id="aiGeneration">GEN 1</span> |
  252.             <span id="aiFitness">FITNESS: 0</span> |
  253.             <span id="aiHop">HOP: 0</span> |
  254.             <span id="aiReward">REWARD: 0</span>
  255.         </div>
  256.     </div>
  257.  
  258.     <div id="startScreen">
  259.         <h1>GRAVITY FLIP GAUNTLET</h1>
  260.         <div class="instructions">
  261.             ↑ Jump (when on ground)<br>
  262.             ↓ Toggle Gravity (Normal ↔ Reversed)<br>
  263.             ← → Move Left/Right<br><br>
  264.             Chain perfect flips for MULTIPLIERS!<br>
  265.             Master the inversion to survive!
  266.         </div>
  267.         <button class="startBtn" onclick="startGame()">START GAUNTLET</button>
  268.         <button class="startBtn" style="background: linear-gradient(135deg, #00ff00, #ff00ff); margin-left: 10px;" onclick="startAutoPlay()">AUTO-PLAY (AI)</button>
  269.     </div>
  270.  
  271.     <div id="gameOverScreen" style="display: none;">
  272.         <h1>GAUNTLET COMPLETE</h1>
  273.         <div style="font-size: 32px; color: #00ffff; margin: 20px 0;">
  274.             FINAL SCORE: <span id="finalScore">0</span>
  275.         </div>
  276.         <div style="font-size: 24px; color: #ffff00; margin: 10px 0;">
  277.             MAX COMBO: <span id="maxCombo">0</span>
  278.         </div>
  279.         <button class="startBtn" onclick="restartGame()">RETRY GAUNTLET</button>
  280.         <button class="startBtn" style="background: linear-gradient(135deg, #00ff00, #ff00ff); margin-left: 10px;" onclick="startAutoPlayFromGameOver()">AUTO-PLAY</button>
  281.     </div>
  282.  
  283.     <canvas id="gameCanvas"></canvas>
  284.    
  285.     <div id="mobileControls" class="touch-controls">
  286.         <div class="touch-btn" id="leftBtn"></div>
  287.         <div class="touch-center">
  288.             <div class="touch-btn" id="flipBtn"></div>
  289.             <div class="touch-btn" id="jumpBtn"></div>
  290.         </div>
  291.         <div class="touch-btn" id="rightBtn"></div>
  292.     </div>
  293.  
  294.     <script>
  295.         const canvas = document.getElementById('gameCanvas');
  296.         const ctx = canvas.getContext('2d');
  297.         canvas.width = 800;
  298.         canvas.height = 400;
  299.  
  300.         let gameState = 'menu';
  301.         let score = 0;
  302.         let multiplier = 1;
  303.         let targetMultiplier = 1;
  304.         let multiplierDecayTimer = 0;
  305.         let level = 1;
  306.         let flipCount = 0;
  307.         let maxCombo = 0;
  308.         let comboTimer = 0;
  309.         let perfectFlipStreak = 0;
  310.         let autoPlayActive = false;
  311.  
  312.         // Neural Network for AI
  313.         class NeuralNetwork {
  314.             constructor() {
  315.                 this.generation = 1;
  316.                 this.fitness = 0;
  317.                 this.bestFitness = 0;
  318.                 this.learningRate = 0.1;
  319.                 this.exploration = 0.3;
  320.                
  321.                 this.currentReward = 0;
  322.                 this.totalRewardHistory = 0;
  323.                 this.maxXReached = 0;
  324.                 this.allTimeMaxX = 0;
  325.                 this.rewardMultiplier = 1;
  326.                 this.hopConfidence = 0;
  327.                
  328.                 // Simplified network architecture
  329.                 this.weights1 = this.randomMatrix(8, 12);
  330.                 this.weights2 = this.randomMatrix(12, 6);
  331.                 this.weights3 = this.randomMatrix(6, 4);
  332.                
  333.                 this.bias1 = this.randomMatrix(1, 12)[0];
  334.                 this.bias2 = this.randomMatrix(1, 6)[0];
  335.                 this.bias3 = this.randomMatrix(1, 4)[0];
  336.                
  337.                 this.memory = [];
  338.                 this.maxMemory = 500;
  339.                 this.batchSize = 16;
  340.                
  341.                 this.loadNetwork();
  342.             }
  343.            
  344.             randomMatrix(rows, cols) {
  345.                 const matrix = [];
  346.                 for (let i = 0; i < rows; i++) {
  347.                    matrix[i] = [];
  348.                    for (let j = 0; j < cols; j++) {
  349.                        matrix[i][j] = (Math.random() - 0.5) * 2;
  350.                    }
  351.                }
  352.                return matrix;
  353.            }
  354.            
  355.            sigmoid(x) {
  356.                return 1 / (1 + Math.exp(-x));
  357.            }
  358.            
  359.            relu(x) {
  360.                return Math.max(0, x);
  361.            }
  362.            
  363.            forward(inputs) {
  364.                if (inputs.length !== 8) {
  365.                    return [0.5, 0.5, 0.5, 0.5];
  366.                }
  367.                
  368.                // First layer
  369.                const hidden1 = [];
  370.                for (let i = 0; i < this.weights1[0].length; i++) {
  371.                    let sum = this.bias1[i];
  372.                    for (let j = 0; j < inputs.length; j++) {
  373.                        sum += inputs[j] * this.weights1[j][i];
  374.                    }
  375.                    hidden1[i] = this.relu(sum);
  376.                }
  377.                
  378.                // Second layer
  379.                const hidden2 = [];
  380.                for (let i = 0; i < this.weights2[0].length; i++) {
  381.                    let sum = this.bias2[i];
  382.                    for (let j = 0; j < hidden1.length; j++) {
  383.                        sum += hidden1[j] * this.weights2[j][i];
  384.                    }
  385.                    hidden2[i] = this.relu(sum);
  386.                }
  387.                
  388.                // Output layer
  389.                const outputs = [];
  390.                for (let i = 0; i < this.weights3[0].length; i++) {
  391.                    let sum = this.bias3[i];
  392.                    for (let j = 0; j < hidden2.length; j++) {
  393.                        sum += hidden2[j] * this.weights3[j][i];
  394.                    }
  395.                    outputs[i] = this.sigmoid(sum);
  396.                }
  397.                
  398.                return outputs;
  399.            }
  400.            
  401.            getState() {
  402.                const nearestPlatform = this.findNearestPlatform();
  403.                const nearestObstacle = this.findNearestObstacle();
  404.                
  405.                return [
  406.                    player.x / 1000,
  407.                    player.y / 400,
  408.                    player.vx / 10,
  409.                    player.vy / 15,
  410.                    player.gravityDirection,
  411.                    player.onGround ? 1 : 0,
  412.                    nearestPlatform ? (nearestPlatform.x - player.x) / 200 : 1,
  413.                    nearestObstacle ? (nearestObstacle.x - player.x) / 200 : 1
  414.                ];
  415.            }
  416.            
  417.            findNearestPlatform() {
  418.                let nearest = null;
  419.                let minDist = Infinity;
  420.                
  421.                for (const platform of platforms) {
  422.                    if (platform.x > player.x - 50) {
  423.                         const dist = Math.sqrt(
  424.                             Math.pow(platform.x - player.x, 2) +
  425.                             Math.pow(platform.y - player.y, 2)
  426.                         );
  427.                         if (dist < minDist) {
  428.                            minDist = dist;
  429.                            nearest = platform;
  430.                        }
  431.                    }
  432.                }
  433.                return nearest;
  434.            }
  435.            
  436.            findNearestObstacle() {
  437.                let nearest = null;
  438.                let minDist = Infinity;
  439.                
  440.                for (const obstacle of obstacles) {
  441.                    if (obstacle.x > player.x - 50) {
  442.                         const dist = Math.sqrt(
  443.                             Math.pow(obstacle.x - player.x, 2) +
  444.                             Math.pow(obstacle.y - player.y, 2)
  445.                         );
  446.                         if (dist < minDist) {
  447.                            minDist = dist;
  448.                            nearest = obstacle;
  449.                        }
  450.                    }
  451.                }
  452.                return nearest;
  453.            }
  454.  
  455.            calculateHopConfidence() {
  456.                let landBelow = false;
  457.                let landAhead = false;
  458.                let nearestPlatformDistance = 1000;
  459.                
  460.                const checkX = player.x + player.width + 30;
  461.                const checkRange = 150;
  462.                
  463.                for (const platform of platforms) {
  464.                    if (player.gravityDirection === 1) {
  465.                        if (platform.x <= player.x + player.width &&
  466.                            platform.x + platform.width >= player.x &&
  467.                            platform.y >= player.y + player.height - 5 &&
  468.                            platform.y <= player.y + player.height + 50) {
  469.                            landBelow = true;
  470.                         }
  471.                     } else {
  472.                         if (platform.x <= player.x + player.width &&
  473.                            platform.x + platform.width >= player.x &&
  474.                            platform.y + platform.height >= player.y - 50 &&
  475.                            platform.y + platform.height <= player.y + 5) {
  476.                            landBelow = true;
  477.                         }
  478.                     }
  479.                    
  480.                     if (platform.x > player.x && platform.x < player.x + checkRange) {
  481.                        const yDiff = Math.abs(platform.y - player.y);
  482.                         if (yDiff < 150) {
  483.                            landAhead = true;
  484.                            const dist = platform.x - (player.x + player.width);
  485.                            nearestPlatformDistance = Math.min(nearestPlatformDistance, dist);
  486.                        }
  487.                    }
  488.                }
  489.                
  490.                if (landBelow) {
  491.                    this.hopConfidence = 100;
  492.                } else if (player.onGround) {
  493.                    this.hopConfidence = 50;
  494.                } else if (landAhead && nearestPlatformDistance < 80) {
  495.                    this.hopConfidence = 40;
  496.                } else if (landAhead && nearestPlatformDistance < 120) {
  497.                    this.hopConfidence = 20;
  498.                } else {
  499.                    this.hopConfidence = 0;
  500.                }
  501.                
  502.                return this.hopConfidence;
  503.            }
  504.            
  505.            decide() {
  506.                const state = this.getState();
  507.                const outputs = this.forward(state);
  508.                
  509.                this.calculateHopConfidence();
  510.                
  511.                if (Math.random() < this.exploration) {
  512.                    const randomAction = Math.floor(Math.random() * 4);
  513.                    outputs[randomAction] += 0.5;
  514.                }
  515.                
  516.                if (this.hopConfidence < 30) {
  517.                    outputs[1] = 0.9;
  518.                    outputs[0] = 0;
  519.                    if (this.hopConfidence < 10 && player.onGround) {
  520.                        outputs[2] = 0.9;
  521.                    }
  522.                } else if (this.hopConfidence < 70) {
  523.                    outputs[1] = Math.max(outputs[1], 0.8);
  524.                    if (player.onGround) {
  525.                        outputs[2] = Math.max(outputs[2], 0.8);
  526.                    }
  527.                }
  528.                
  529.                outputs[1] = Math.max(outputs[1], 0.6);
  530.                
  531.                if (player.x > 200) {
  532.                     outputs[0] *= 0.1;
  533.                 }
  534.                
  535.                 if (player.gravityDirection === -1) {
  536.                     outputs[3] *= 0.3;
  537.                 }
  538.                
  539.                 const shouldFlip = outputs[3] > 0.8 && player.gravityDirection === 1 ||
  540.                                 (outputs[3] > 0.4 && player.gravityDirection === -1);
  541.                
  542.                 this.memory.push({
  543.                     state: state,
  544.                     outputs: outputs,
  545.                     reward: 0,
  546.                     hopConfidence: this.hopConfidence
  547.                 });
  548.                
  549.                 if (this.memory.length > this.maxMemory) {
  550.                     this.memory.shift();
  551.                 }
  552.                
  553.                 const decision = {
  554.                     left: outputs[0] > 0.7,
  555.                     right: outputs[1] > 0.2,
  556.                     jump: outputs[2] > 0.7 && player.onGround,
  557.                    flip: shouldFlip && !keys.downPressed
  558.                };
  559.                
  560.                 return decision;
  561.             }
  562.            
  563.             updateFitness() {
  564.                 this.maxXReached = Math.max(this.maxXReached, player.x);
  565.                
  566.                 const gravityBonus = player.gravityDirection === 1 ? 10 : 0;
  567.                
  568.                 this.fitness = Math.max(0,
  569.                     score +
  570.                     (player.x / 10) +
  571.                     (flipCount * 2) +
  572.                     (collectibles.filter(c => c.collected).length * 50) +
  573.                     gravityBonus +
  574.                     (player.onGround ? 5 : 0)
  575.                 );
  576.                
  577.                 const xProgress = Math.max(0, player.x / 100);
  578.                 const baseReward = xProgress * xProgress;
  579.                
  580.                 if (this.totalRewardHistory > 0) {
  581.                     this.rewardMultiplier = 1 + Math.log10(1 + this.totalRewardHistory / 1000);
  582.                 }
  583.                
  584.                 this.currentReward = Math.floor(baseReward * this.rewardMultiplier * 10);
  585.                
  586.                 if (player.x > this.allTimeMaxX) {
  587.                     this.allTimeMaxX = player.x;
  588.                     this.currentReward += 100 * this.rewardMultiplier;
  589.                 }
  590.                
  591.                 const recentMemory = this.memory.slice(-10);
  592.                 recentMemory.forEach(mem => {
  593.                     mem.reward = (this.fitness + this.currentReward) / 1000;
  594.                 });
  595.             }
  596.            
  597.             learn() {
  598.                 if (this.memory.length < this.batchSize) return;
  599.                
  600.                const batch = this.memory.slice(-this.batchSize);
  601.                
  602.                batch.forEach(experience => {
  603.                     const { state, outputs, reward } = experience;
  604.                    
  605.                     if (reward > 0) {
  606.                         this.adjustWeights(state, outputs, reward);
  607.                     }
  608.                 });
  609.                
  610.                 this.exploration = Math.max(0.1, this.exploration * 0.995);
  611.             }
  612.            
  613.             adjustWeights(state, targetOutputs, reward) {
  614.                 const currentOutputs = this.forward(state);
  615.                 const error = [];
  616.                
  617.                 for (let i = 0; i < targetOutputs.length; i++) {
  618.                    error[i] = (targetOutputs[i] - currentOutputs[i]) * reward * this.learningRate;
  619.                }
  620.                
  621.                for (let i = 0; i < this.weights3.length; i++) {
  622.                    for (let j = 0; j < this.weights3[i].length; j++) {
  623.                        this.weights3[i][j] += error[j] * 0.01;
  624.                    }
  625.                }
  626.            }
  627.            
  628.            evolve() {
  629.                this.generation++;
  630.                
  631.                this.totalRewardHistory += this.currentReward;
  632.                
  633.                if (this.fitness > this.bestFitness) {
  634.                     this.bestFitness = this.fitness;
  635.                     this.saveNetwork();
  636.                 } else {
  637.                     this.mutate();
  638.                 }
  639.                
  640.                 this.fitness = 0;
  641.                 this.currentReward = 0;
  642.                 this.maxXReached = 0;
  643.                 this.learn();
  644.             }
  645.            
  646.             mutate() {
  647.                 const mutationRate = 0.1;
  648.                 const mutationStrength = 0.2;
  649.                
  650.                 for (let i = 0; i < this.weights1.length; i++) {
  651.                    for (let j = 0; j < this.weights1[i].length; j++) {
  652.                        if (Math.random() < mutationRate) {
  653.                            this.weights1[i][j] += (Math.random() - 0.5) * mutationStrength;
  654.                        }
  655.                    }
  656.                }
  657.                
  658.                for (let i = 0; i < this.weights2.length; i++) {
  659.                    for (let j = 0; j < this.weights2[i].length; j++) {
  660.                        if (Math.random() < mutationRate) {
  661.                            this.weights2[i][j] += (Math.random() - 0.5) * mutationStrength;
  662.                        }
  663.                    }
  664.                }
  665.                
  666.                for (let i = 0; i < this.weights3.length; i++) {
  667.                    for (let j = 0; j < this.weights3[i].length; j++) {
  668.                        if (Math.random() < mutationRate) {
  669.                            this.weights3[i][j] += (Math.random() - 0.5) * mutationStrength;
  670.                        }
  671.                    }
  672.                }
  673.            }
  674.            
  675.            saveNetwork() {
  676.                const networkData = {
  677.                    weights1: this.weights1,
  678.                    weights2: this.weights2,
  679.                    weights3: this.weights3,
  680.                    bias1: this.bias1,
  681.                    bias2: this.bias2,
  682.                    bias3: this.bias3,
  683.                    generation: this.generation,
  684.                    bestFitness: this.bestFitness,
  685.                    totalRewardHistory: this.totalRewardHistory,
  686.                    allTimeMaxX: this.allTimeMaxX
  687.                };
  688.                localStorage.setItem('gravityFlipAI', JSON.stringify(networkData));
  689.            }
  690.            
  691.            loadNetwork() {
  692.                const saved = localStorage.getItem('gravityFlipAI');
  693.                if (saved) {
  694.                    try {
  695.                        const networkData = JSON.parse(saved);
  696.                        if (networkData.weights1 &&
  697.                            networkData.weights1.length === 8) {
  698.                            this.weights1 = networkData.weights1;
  699.                            this.weights2 = networkData.weights2;
  700.                            this.weights3 = networkData.weights3;
  701.                            this.bias1 = networkData.bias1;
  702.                            this.bias2 = networkData.bias2;
  703.                            this.bias3 = networkData.bias3;
  704.                            this.generation = networkData.generation || 1;
  705.                            this.bestFitness = networkData.bestFitness || 0;
  706.                            this.totalRewardHistory = networkData.totalRewardHistory || 0;
  707.                            this.allTimeMaxX = networkData.allTimeMaxX || 0;
  708.                        } else {
  709.                            localStorage.removeItem('gravityFlipAI');
  710.                        }
  711.                    } catch (e) {
  712.                        localStorage.removeItem('gravityFlipAI');
  713.                    }
  714.                }
  715.            }
  716.        }
  717.  
  718.        let aiPlayer = null;
  719.  
  720.        const player = {
  721.            x: 100,
  722.            y: 200,
  723.            width: 30,
  724.            height: 30,
  725.            vx: 0,
  726.            vy: 0,
  727.            speed: 5,
  728.            jumpPower: 12,
  729.            gravity: 0.4,
  730.            gravityDirection: 1,
  731.            onGround: false,
  732.            color: '#00ffff',
  733.            trail: []
  734.        };
  735.  
  736.        const camera = {
  737.            x: 0,
  738.            y: 0
  739.        };
  740.  
  741.        let platforms = [];
  742.        let obstacles = [];
  743.        let collectibles = [];
  744.        let particles = [];
  745.  
  746.        const keys = {
  747.            downPressed: false
  748.        };
  749.  
  750.        class Platform {
  751.            constructor(x, y, width, height, type = 'normal') {
  752.                this.x = x;
  753.                this.y = y;
  754.                this.width = width;
  755.                this.height = height;
  756.                this.type = type;
  757.                this.color = type === 'moving' ? '#ffff00' : '#ff00ff';
  758.                this.moveSpeed = 2;
  759.                this.moveRange = 100;
  760.                this.startY = y;
  761.            }
  762.  
  763.            update() {
  764.                if (this.type === 'moving') {
  765.                    this.y = this.startY + Math.sin(Date.now() * 0.002) * this.moveRange;
  766.                }
  767.            }
  768.  
  769.            draw() {
  770.                ctx.save();
  771.                ctx.fillStyle = this.color;
  772.                ctx.shadowBlur = 10;
  773.                ctx.shadowColor = this.color;
  774.                ctx.fillRect(this.x - camera.x, this.y - camera.y, this.width, this.height);
  775.                ctx.restore();
  776.            }
  777.        }
  778.  
  779.        class Obstacle {
  780.            constructor(x, y, width, height, type = 'spike') {
  781.                this.x = x;
  782.                this.y = y;
  783.                this.width = width;
  784.                this.height = height;
  785.                this.type = type;
  786.                this.rotation = 0;
  787.            }
  788.  
  789.            update() {
  790.                if (this.type === 'rotating') {
  791.                    this.rotation += 0.05;
  792.                }
  793.            }
  794.  
  795.            draw() {
  796.                ctx.save();
  797.                ctx.translate(this.x - camera.x + this.width/2, this.y - camera.y + this.height/2);
  798.                ctx.rotate(this.rotation);
  799.                
  800.                if (this.type === 'spike') {
  801.                    ctx.fillStyle = '#ff0000';
  802.                    ctx.beginPath();
  803.                    ctx.moveTo(-this.width/2, this.height/2);
  804.                    ctx.lineTo(0, -this.height/2);
  805.                    ctx.lineTo(this.width/2, this.height/2);
  806.                    ctx.closePath();
  807.                    ctx.fill();
  808.                } else {
  809.                    ctx.fillStyle = '#ff6600';
  810.                    ctx.fillRect(-this.width/2, -this.height/2, this.width, this.height);
  811.                }
  812.                ctx.restore();
  813.            }
  814.        }
  815.  
  816.        class Collectible {
  817.            constructor(x, y) {
  818.                this.x = x;
  819.                this.y = y;
  820.                this.size = 20;
  821.                this.collected = false;
  822.                this.rotation = 0;
  823.                this.value = 100;
  824.            }
  825.  
  826.            update() {
  827.                this.rotation += 0.1;
  828.            }
  829.  
  830.            draw() {
  831.                if (this.collected) return;
  832.                
  833.                ctx.save();
  834.                ctx.translate(this.x - camera.x, this.y - camera.y);
  835.                ctx.rotate(this.rotation);
  836.                
  837.                if (this.value > 100) {
  838.                     ctx.fillStyle = '#ff00ff';
  839.                     ctx.shadowBlur = 25;
  840.                     ctx.shadowColor = '#ff00ff';
  841.                 } else {
  842.                     ctx.fillStyle = '#ffff00';
  843.                     ctx.shadowBlur = 20;
  844.                     ctx.shadowColor = '#ffff00';
  845.                 }
  846.                
  847.                 ctx.beginPath();
  848.                 for (let i = 0; i < 6; i++) {
  849.                    const angle = (Math.PI * 2 / 6) * i;
  850.                    const x = Math.cos(angle) * this.size/2;
  851.                    const y = Math.sin(angle) * this.size/2;
  852.                    if (i === 0) ctx.moveTo(x, y);
  853.                    else ctx.lineTo(x, y);
  854.                }
  855.                ctx.closePath();
  856.                ctx.fill();
  857.                ctx.restore();
  858.            }
  859.        }
  860.  
  861.        function generateLevel(levelNum) {
  862.            platforms = [];
  863.            obstacles = [];
  864.            collectibles = [];
  865.            
  866.            platforms.push(new Platform(0, 350, 200, 50));
  867.            platforms.push(new Platform(0, 0, 200, 50));
  868.            
  869.            const levelLength = 3000 + levelNum * 1000;
  870.            let lastXBottom = 200;
  871.            let lastYBottom = 350;
  872.            let lastXTop = 200;
  873.            let lastYTop = 50;
  874.            
  875.            const maxJumpDistance = 120;
  876.            const minGap = 40;
  877.            
  878.            for (let i = 0; i < levelLength; i += 150) {
  879.                const gapBottom = minGap + Math.random() * (maxJumpDistance - minGap);
  880.                const widthBottom = 80 + Math.random() * 120;
  881.                const heightBottom = 20 + Math.random() * 30;
  882.                
  883.                let yBottom = lastYBottom + (Math.random() - 0.5) * 80;
  884.                yBottom = Math.max(250, Math.min(370, yBottom));
  885.                
  886.                const platformTypeBottom = Math.random() < 0.2 ? 'moving' : 'normal';
  887.                const platformBottom = new Platform(lastXBottom + gapBottom, yBottom, widthBottom, heightBottom, platformTypeBottom);
  888.                
  889.                if (platformTypeBottom === 'moving') {
  890.                    platformBottom.moveRange = 40;
  891.                }
  892.                
  893.                platforms.push(platformBottom);
  894.                
  895.                const gapTop = minGap + Math.random() * (maxJumpDistance - minGap);
  896.                const widthTop = 80 + Math.random() * 120;
  897.                const heightTop = 20 + Math.random() * 30;
  898.                
  899.                let yTop = lastYTop + (Math.random() - 0.5) * 80;
  900.                yTop = Math.max(30, Math.min(150, yTop));
  901.                
  902.                const platformTypeTop = Math.random() < 0.2 ? 'moving' : 'normal';
  903.                const platformTop = new Platform(lastXTop + gapTop, yTop, widthTop, heightTop, platformTypeTop);
  904.                platformTop.color = '#00ffff';
  905.                
  906.                if (platformTypeTop === 'moving') {
  907.                    platformTop.moveRange = 40;
  908.                }
  909.                
  910.                platforms.push(platformTop);
  911.                
  912.                if (Math.random() < 0.3 && i > 500) {
  913.                     if (Math.random() < 0.5) {
  914.                        lastXBottom += 200;
  915.                    } else {
  916.                        lastXTop += 200;
  917.                    }
  918.                }
  919.                
  920.                if (Math.random() < (0.2 + levelNum * 0.05)) {
  921.                    obstacles.push(new Obstacle(
  922.                        lastXBottom + gapBottom + widthBottom/2 - 15,
  923.                        yBottom - 30,
  924.                        30,
  925.                        30,
  926.                        Math.random() < 0.5 ? 'spike' : 'rotating'
  927.                    ));
  928.                }
  929.                
  930.                if (Math.random() < (0.2 + levelNum * 0.05)) {
  931.                    const topObstacle = new Obstacle(
  932.                        lastXTop + gapTop + widthTop/2 - 15,
  933.                        yTop + heightTop + 5,
  934.                        30,
  935.                        30,
  936.                        Math.random() < 0.5 ? 'spike' : 'rotating'
  937.                    );
  938.                    topObstacle.color = '#ff00ff';
  939.                    obstacles.push(topObstacle);
  940.                }
  941.                
  942.                if (Math.random() < 0.7) {
  943.                    collectibles.push(new Collectible(lastXBottom + gapBottom + widthBottom/2, yBottom - 60));
  944.                }
  945.                
  946.                if (Math.random() < 0.5) {
  947.                    const topCollectible = new Collectible(lastXTop + gapTop + widthTop/2, yTop + heightTop + 60);
  948.                    topCollectible.value = 200;
  949.                    collectibles.push(topCollectible);
  950.                }
  951.                
  952.                lastXBottom = lastXBottom + gapBottom + widthBottom;
  953.                lastYBottom = yBottom;
  954.                lastXTop = lastXTop + gapTop + widthTop;
  955.                lastYTop = yTop;
  956.            }
  957.            
  958.            const finalX = Math.max(lastXBottom, lastXTop);
  959.            platforms.push(new Platform(finalX + 100, 350, 300, 50));
  960.            platforms.push(new Platform(finalX + 100, 0, 300, 50));
  961.        }
  962.  
  963.        function createParticle(x, y, color) {
  964.            const particle = document.createElement('div');
  965.            particle.className = 'particle';
  966.            particle.style.left = x + 'px';
  967.            particle.style.top = y + 'px';
  968.            particle.style.background = color;
  969.            particle.style.setProperty('--dx', (Math.random() - 0.5) * 100 + 'px');
  970.            particle.style.setProperty('--dy', (Math.random() - 0.5) * 100 + 'px');
  971.            document.getElementById('particles').appendChild(particle);
  972.            
  973.            setTimeout(() => particle.remove(), 1000);
  974.         }
  975.  
  976.         function showComboText(x, y, text) {
  977.             const comboEl = document.createElement('div');
  978.             comboEl.className = 'combo-text';
  979.             comboEl.textContent = text;
  980.             comboEl.style.left = x + 'px';
  981.             comboEl.style.top = y + 'px';
  982.             document.body.appendChild(comboEl);
  983.            
  984.             setTimeout(() => comboEl.remove(), 1000);
  985.         }
  986.  
  987.         function updatePlayer() {
  988.             if (autoPlayActive && aiPlayer) {
  989.                const decision = aiPlayer.decide();
  990.                
  991.                 keys['ArrowLeft'] = false;
  992.                 keys['ArrowRight'] = false;
  993.                 keys['ArrowUp'] = false;
  994.                 keys['ArrowDown'] = false;
  995.                
  996.                 if (decision.left) keys['ArrowLeft'] = true;
  997.                 if (decision.right) keys['ArrowRight'] = true;
  998.                 if (decision.jump) keys['ArrowUp'] = true;
  999.                 if (decision.flip) keys['ArrowDown'] = true;
  1000.                
  1001.                 aiPlayer.updateFitness();
  1002.                
  1003.                 document.getElementById('aiGeneration').textContent = `GEN ${aiPlayer.generation}`;
  1004.                 document.getElementById('aiFitness').textContent = `FITNESS: ${Math.floor(aiPlayer.fitness)}`;
  1005.                 document.getElementById('aiHop').textContent = `HOP: ${Math.floor(aiPlayer.hopConfidence)}`;
  1006.                 document.getElementById('aiReward').textContent = `REWARD: ${aiPlayer.currentReward}`;
  1007.             }
  1008.            
  1009.             if (keys['ArrowLeft']) {
  1010.                 player.vx = -player.speed;
  1011.             } else if (keys['ArrowRight']) {
  1012.                 player.vx = player.speed;
  1013.             } else {
  1014.                 player.vx *= 0.8;
  1015.             }
  1016.            
  1017.             if (keys['ArrowUp'] && player.onGround) {
  1018.                player.vy = -player.jumpPower * player.gravityDirection;
  1019.                 player.onGround = false;
  1020.             }
  1021.            
  1022.             if (keys['ArrowDown'] && !keys.downPressed) {
  1023.                keys.downPressed = true;
  1024.                 flipGravity(player.gravityDirection === 1 ? -1 : 1);
  1025.             }
  1026.             if (!keys['ArrowDown']) {
  1027.                 keys.downPressed = false;
  1028.             }
  1029.            
  1030.             player.vy += player.gravity * player.gravityDirection;
  1031.             player.vy = Math.max(-15, Math.min(15, player.vy));
  1032.            
  1033.             player.x += player.vx;
  1034.             player.y += player.vy;
  1035.            
  1036.             player.trail.push({x: player.x, y: player.y, alpha: 1});
  1037.             if (player.trail.length > 10) {
  1038.                 player.trail.shift();
  1039.             }
  1040.            
  1041.             player.onGround = false;
  1042.            
  1043.             for (const platform of platforms) {
  1044.                 if (checkCollision(player, platform)) {
  1045.                     if (player.gravityDirection === 1) {
  1046.                         if (player.vy > 0) {
  1047.                             player.y = platform.y - player.height;
  1048.                             player.vy = 0;
  1049.                             player.onGround = true;
  1050.                         }
  1051.                     } else {
  1052.                         if (player.vy < 0) {
  1053.                            player.y = platform.y + platform.height;
  1054.                            player.vy = 0;
  1055.                            player.onGround = true;
  1056.                        }
  1057.                    }
  1058.                }
  1059.            }
  1060.            
  1061.            for (const obstacle of obstacles) {
  1062.                if (checkCollision(player, obstacle)) {
  1063.                    gameOver();
  1064.                }
  1065.            }
  1066.            
  1067.            for (const collectible of collectibles) {
  1068.                if (!collectible.collected && checkCollision(player, {
  1069.                    x: collectible.x - collectible.size/2,
  1070.                    y: collectible.y - collectible.size/2,
  1071.                    width: collectible.size,
  1072.                    height: collectible.size
  1073.                })) {
  1074.                    collectible.collected = true;
  1075.                    
  1076.                    if (targetMultiplier < 10) {
  1077.                        targetMultiplier = 10;
  1078.                    } else if (targetMultiplier <= 10) {
  1079.                        targetMultiplier = 13;
  1080.                    } else if (targetMultiplier <= 12) {
  1081.                        targetMultiplier = 15;
  1082.                    } else if (targetMultiplier <= 13) {
  1083.                        targetMultiplier = 18;
  1084.                    } else if (targetMultiplier <= 15) {
  1085.                        targetMultiplier = 20;
  1086.                    } else {
  1087.                        targetMultiplier = Math.min(targetMultiplier + 2, 25);
  1088.                    }
  1089.                    
  1090.                    multiplier = targetMultiplier;
  1091.                    multiplierDecayTimer = 120;
  1092.                    
  1093.                    score += collectible.value * multiplier;
  1094.                    
  1095.                    for (let i = 0; i < 10; i++) {
  1096.                        createParticle(
  1097.                            collectible.x - camera.x + canvas.offsetLeft,
  1098.                            collectible.y - camera.y + canvas.offsetTop,
  1099.                            collectible.value > 100 ? '#ff00ff' : '#ffff00'
  1100.                         );
  1101.                     }
  1102.                    
  1103.                     showComboText(
  1104.                         canvas.offsetLeft + canvas.width/2,
  1105.                         canvas.offsetTop + 100,
  1106.                         `x${multiplier}!`
  1107.                     );
  1108.                     maxCombo = Math.max(maxCombo, multiplier);
  1109.                 }
  1110.             }
  1111.            
  1112.             if (multiplierDecayTimer > 0) {
  1113.                 multiplierDecayTimer--;
  1114.             } else if (multiplier > 1) {
  1115.                 if (multiplier >= 20) {
  1116.                     targetMultiplier = 15;
  1117.                 } else if (multiplier >= 15) {
  1118.                     targetMultiplier = 10;
  1119.                 } else if (multiplier >= 10) {
  1120.                     targetMultiplier = 8;
  1121.                 } else if (multiplier >= 8) {
  1122.                     targetMultiplier = 5;
  1123.                 } else if (multiplier >= 5) {
  1124.                     targetMultiplier = 3;
  1125.                 } else {
  1126.                     targetMultiplier = 1;
  1127.                 }
  1128.                 multiplier = targetMultiplier;
  1129.                 multiplierDecayTimer = 60;
  1130.             }
  1131.            
  1132.             const lastPlatform = platforms[platforms.length - 1];
  1133.             if (player.x > lastPlatform.x + lastPlatform.width/2) {
  1134.                 nextLevel();
  1135.             }
  1136.            
  1137.             if (player.y > 600 || player.y < -200) {
  1138.                gameOver();
  1139.            }
  1140.        }
  1141.  
  1142.        function flipGravity(direction) {
  1143.            if (player.gravityDirection !== direction) {
  1144.                player.gravityDirection = direction;
  1145.                canvas.classList.toggle('flipped');
  1146.                flipCount++;
  1147.                
  1148.                for (let i = 0; i < 20; i++) {
  1149.                    createParticle(
  1150.                        player.x - camera.x + canvas.offsetLeft,
  1151.                        player.y - camera.y + canvas.offsetTop,
  1152.                        '#ff00ff'
  1153.                    );
  1154.                }
  1155.                
  1156.                document.getElementById('flipCount').textContent = `FLIPS: ${flipCount}`;
  1157.            }
  1158.        }
  1159.  
  1160.        function checkCollision(a, b) {
  1161.            return a.x < b.x + b.width &&
  1162.                   a.x + a.width > b.x &&
  1163.                   a.y < b.y + b.height &&
  1164.                   a.y + a.height > b.y;
  1165.         }
  1166.  
  1167.         function updateCamera() {
  1168.             camera.x = player.x - canvas.width / 3;
  1169.             camera.y = player.y - canvas.height / 2;
  1170.             camera.y = Math.max(-100, Math.min(100, camera.y));
  1171.         }
  1172.  
  1173.         function render() {
  1174.             ctx.fillStyle = 'rgba(10, 14, 39, 0.3)';
  1175.             ctx.fillRect(0, 0, canvas.width, canvas.height);
  1176.            
  1177.             ctx.strokeStyle = 'rgba(255, 0, 255, 0.1)';
  1178.             ctx.lineWidth = 1;
  1179.             for (let i = 0; i < canvas.width; i += 50) {
  1180.                ctx.beginPath();
  1181.                ctx.moveTo(i, 0);
  1182.                ctx.lineTo(i, canvas.height);
  1183.                ctx.stroke();
  1184.            }
  1185.            for (let i = 0; i < canvas.height; i += 50) {
  1186.                ctx.beginPath();
  1187.                ctx.moveTo(0, i);
  1188.                ctx.lineTo(canvas.width, i);
  1189.                ctx.stroke();
  1190.            }
  1191.            
  1192.            ctx.strokeStyle = 'rgba(0, 255, 255, 0.2)';
  1193.            ctx.lineWidth = 2;
  1194.            ctx.setLineDash([10, 10]);
  1195.            ctx.beginPath();
  1196.            ctx.moveTo(0 - camera.x, 200 - camera.y);
  1197.            ctx.lineTo(10000 - camera.x, 200 - camera.y);
  1198.            ctx.stroke();
  1199.            ctx.setLineDash([]);
  1200.            
  1201.            platforms.forEach(platform => platform.draw());
  1202.             obstacles.forEach(obstacle => obstacle.draw());
  1203.             collectibles.forEach(collectible => collectible.draw());
  1204.            
  1205.             player.trail.forEach((point, index) => {
  1206.                 ctx.fillStyle = `rgba(0, 255, 255, ${point.alpha * (index / player.trail.length)})`;
  1207.                 ctx.fillRect(point.x - camera.x, point.y - camera.y, player.width, player.height);
  1208.             });
  1209.            
  1210.             ctx.save();
  1211.             ctx.fillStyle = player.color;
  1212.             ctx.shadowBlur = 20;
  1213.             ctx.shadowColor = player.color;
  1214.             ctx.fillRect(player.x - camera.x, player.y - camera.y, player.width, player.height);
  1215.             ctx.restore();
  1216.            
  1217.             document.getElementById('score').textContent = `SCORE: ${score}`;
  1218.             document.getElementById('multiplier').textContent = `MULTIPLIER: x${multiplier}`;
  1219.             document.getElementById('level').textContent = `LEVEL: ${level}`;
  1220.         }
  1221.  
  1222.         function gameLoop() {
  1223.             if (gameState !== 'playing') return;
  1224.            
  1225.             platforms.forEach(platform => platform.update());
  1226.             obstacles.forEach(obstacle => obstacle.update());
  1227.             collectibles.forEach(collectible => collectible.update());
  1228.            
  1229.             updatePlayer();
  1230.             updateCamera();
  1231.             render();
  1232.            
  1233.             requestAnimationFrame(gameLoop);
  1234.         }
  1235.  
  1236.         function startGame() {
  1237.             document.getElementById('startScreen').style.display = 'none';
  1238.             gameState = 'playing';
  1239.             score = 0;
  1240.             level = 1;
  1241.             multiplier = 1;
  1242.             targetMultiplier = 1;
  1243.             multiplierDecayTimer = 0;
  1244.             flipCount = 0;
  1245.             maxCombo = 1;
  1246.             player.x = 100;
  1247.             player.y = 300;
  1248.             player.vx = 0;
  1249.             player.vy = 0;
  1250.             player.gravityDirection = 1;
  1251.             player.onGround = false;
  1252.             canvas.classList.remove('flipped');
  1253.             generateLevel(level);
  1254.             gameLoop();
  1255.         }
  1256.  
  1257.         function nextLevel() {
  1258.             level++;
  1259.             score += 1000 * level;
  1260.             showComboText(
  1261.                 canvas.offsetLeft + canvas.width/2,
  1262.                 canvas.offsetTop + canvas.height/2,
  1263.                 `LEVEL ${level}!`
  1264.             );
  1265.             generateLevel(level);
  1266.             player.x = 100;
  1267.             player.y = 200;
  1268.             player.vx = 0;
  1269.             player.vy = 0;
  1270.         }
  1271.  
  1272.         function gameOver() {
  1273.             gameState = 'gameover';
  1274.            
  1275.             if (autoPlayActive && aiPlayer) {
  1276.                aiPlayer.evolve();
  1277.                
  1278.                 setTimeout(() => {
  1279.                     if (autoPlayActive) {
  1280.                         document.getElementById('gameOverScreen').style.display = 'none';
  1281.                         restartGame();
  1282.                     }
  1283.                 }, 1000);
  1284.             } else {
  1285.                 document.getElementById('finalScore').textContent = score;
  1286.                 document.getElementById('maxCombo').textContent = `x${maxCombo}`;
  1287.                 document.getElementById('gameOverScreen').style.display = 'block';
  1288.             }
  1289.         }
  1290.  
  1291.         function restartGame() {
  1292.             if (!autoPlayActive) {
  1293.                 document.getElementById('gameOverScreen').style.display = 'none';
  1294.             }
  1295.             startGame();
  1296.         }
  1297.  
  1298.         function startAutoPlay() {
  1299.             autoPlayActive = true;
  1300.             if (!aiPlayer) {
  1301.                 aiPlayer = new NeuralNetwork();
  1302.             }
  1303.             document.getElementById('startScreen').style.display = 'none';
  1304.             document.getElementById('aiStatus').style.display = 'block';
  1305.             startGame();
  1306.         }
  1307.  
  1308.         document.addEventListener('keydown', (e) => {
  1309.             if (!autoPlayActive) {
  1310.                 keys[e.key] = true;
  1311.                 if (e.key.startsWith('Arrow')) {
  1312.                     e.preventDefault();
  1313.                 }
  1314.             }
  1315.         });
  1316.  
  1317.         document.addEventListener('keyup', (e) => {
  1318.             if (!autoPlayActive) {
  1319.                 keys[e.key] = false;
  1320.             }
  1321.         });
  1322.  
  1323.         function startAutoPlayFromGameOver() {
  1324.             document.getElementById('gameOverScreen').style.display = 'none';
  1325.             startAutoPlay();
  1326.         }
  1327.  
  1328.         // Touch controls for mobile
  1329.         const leftBtn = document.getElementById('leftBtn');
  1330.         const rightBtn = document.getElementById('rightBtn');
  1331.         const flipBtn = document.getElementById('flipBtn');
  1332.         const jumpBtn = document.getElementById('jumpBtn');
  1333.  
  1334.         leftBtn.addEventListener('touchstart', () => keys['ArrowLeft'] = true);
  1335.         leftBtn.addEventListener('touchend', () => keys['ArrowLeft'] = false);
  1336.        
  1337.         rightBtn.addEventListener('touchstart', () => keys['ArrowRight'] = true);
  1338.         rightBtn.addEventListener('touchend', () => keys['ArrowRight'] = false);
  1339.        
  1340.         flipBtn.addEventListener('touchstart', () => {
  1341.             keys['ArrowDown'] = true;
  1342.             flipGravity(player.gravityDirection === 1 ? -1 : 1);
  1343.         });
  1344.         flipBtn.addEventListener('touchend', () => keys['ArrowDown'] = false);
  1345.        
  1346.         jumpBtn.addEventListener('touchstart', () => keys['ArrowUp'] = true);
  1347.         jumpBtn.addEventListener('touchend', () => keys['ArrowUp'] = false);
  1348.  
  1349.         // Initialize the canvas
  1350.         ctx.fillStyle = '#0a0e27';
  1351.         ctx.fillRect(0, 0, canvas.width, canvas.height);
  1352.     </script>
  1353. </body>
  1354. </html>
  1355.  
Advertisement
Add Comment
Please, Sign In to add comment