Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Gravity Flip Gauntlet</title>
- <style>
- * {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- }
- body {
- background: linear-gradient(135deg, #0a0e27 0%, #1a0b2e 50%, #2e1760 100%);
- font-family: 'Courier New', monospace;
- overflow: hidden;
- position: relative;
- height: 100vh;
- touch-action: none;
- }
- #gameCanvas {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- border: 2px solid #ff00ff;
- box-shadow: 0 0 30px #ff00ff, inset 0 0 30px rgba(255, 0, 255, 0.2);
- transition: transform 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
- }
- .flipped {
- transform: translate(-50%, -50%) rotate(180deg) !important;
- }
- #ui {
- position: absolute;
- top: 20px;
- left: 20px;
- color: #fff;
- font-size: 18px;
- text-shadow: 0 0 10px #ff00ff;
- z-index: 100;
- background: rgba(0, 0, 0, 0.5);
- padding: 10px;
- border-radius: 10px;
- }
- #score {
- font-size: 24px;
- color: #00ffff;
- margin-bottom: 10px;
- }
- #multiplier {
- color: #ffff00;
- font-size: 20px;
- animation: pulse 0.5s infinite;
- }
- #level {
- color: #ff00ff;
- margin-top: 10px;
- }
- #startScreen, #gameOverScreen {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- text-align: center;
- color: #fff;
- z-index: 200;
- background: rgba(10, 14, 39, 0.9);
- padding: 30px;
- border-radius: 20px;
- border: 2px solid #ff00ff;
- box-shadow: 0 0 40px rgba(255, 0, 255, 0.5);
- width: 90%;
- max-width: 600px;
- }
- h1 {
- font-size: 48px;
- background: linear-gradient(45deg, #ff00ff, #00ffff, #ffff00);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
- margin-bottom: 20px;
- animation: glow 2s ease-in-out infinite;
- }
- .instructions {
- font-size: 16px;
- color: #fff;
- margin: 20px 0;
- line-height: 1.6;
- }
- .startBtn {
- background: linear-gradient(135deg, #ff00ff, #00ffff);
- border: none;
- padding: 15px 40px;
- font-size: 20px;
- color: #000;
- font-weight: bold;
- cursor: pointer;
- border-radius: 30px;
- transition: all 0.3s;
- margin-top: 20px;
- }
- .startBtn:hover {
- transform: scale(1.1);
- box-shadow: 0 0 30px #ff00ff;
- }
- #particles {
- position: absolute;
- width: 100%;
- height: 100%;
- pointer-events: none;
- }
- .particle {
- position: absolute;
- width: 4px;
- height: 4px;
- background: #fff;
- border-radius: 50%;
- opacity: 0;
- animation: particleFade 1s ease-out;
- }
- @keyframes pulse {
- 0%, 100% { transform: scale(1); }
- 50% { transform: scale(1.2); }
- }
- @keyframes glow {
- 0%, 100% { filter: drop-shadow(0 0 20px rgba(255, 0, 255, 0.8)); }
- 50% { filter: drop-shadow(0 0 40px rgba(0, 255, 255, 1)); }
- }
- @keyframes particleFade {
- 0% {
- opacity: 1;
- transform: translate(0, 0) scale(1);
- }
- 100% {
- opacity: 0;
- transform: translate(var(--dx), var(--dy)) scale(0);
- }
- }
- .combo-text {
- position: absolute;
- color: #ffff00;
- font-size: 36px;
- font-weight: bold;
- pointer-events: none;
- animation: comboFloat 1s ease-out forwards;
- text-shadow: 0 0 20px #ffff00;
- }
- @keyframes comboFloat {
- 0% {
- opacity: 1;
- transform: translate(-50%, -50%) scale(0.5);
- }
- 50% {
- transform: translate(-50%, -100%) scale(1.5);
- }
- 100% {
- opacity: 0;
- transform: translate(-50%, -150%) scale(1);
- }
- }
- .touch-controls {
- position: absolute;
- bottom: 30px;
- width: 100%;
- display: flex;
- justify-content: space-between;
- padding: 0 30px;
- z-index: 150;
- }
- .touch-btn {
- width: 70px;
- height: 70px;
- background: rgba(255, 255, 255, 0.2);
- border-radius: 50%;
- display: flex;
- justify-content: center;
- align-items: center;
- color: white;
- font-size: 24px;
- user-select: none;
- border: 2px solid #ff00ff;
- }
- .touch-center {
- display: flex;
- gap: 30px;
- }
- #flipBtn {
- background: rgba(255, 0, 255, 0.3);
- }
- #jumpBtn {
- background: rgba(0, 255, 255, 0.3);
- }
- #mobileControls {
- display: none;
- }
- @media (max-width: 768px) {
- #mobileControls {
- display: flex;
- }
- #gameCanvas {
- width: 95%;
- height: auto;
- }
- h1 {
- font-size: 36px;
- }
- .instructions {
- font-size: 14px;
- }
- }
- </style>
- </head>
- <body>
- <div id="particles"></div>
- <div id="ui">
- <div id="score">SCORE: 0</div>
- <div id="multiplier">MULTIPLIER: x1</div>
- <div id="level">LEVEL: 1</div>
- <div id="flipCount" style="color: #00ff00; margin-top: 10px;">FLIPS: 0</div>
- <div id="aiStatus" style="color: #00ff00; margin-top: 10px; display: none;">
- AI MODE: <span id="aiGeneration">GEN 1</span> |
- <span id="aiFitness">FITNESS: 0</span> |
- <span id="aiHop">HOP: 0</span> |
- <span id="aiReward">REWARD: 0</span>
- </div>
- </div>
- <div id="startScreen">
- <h1>GRAVITY FLIP GAUNTLET</h1>
- <div class="instructions">
- ↑ Jump (when on ground)<br>
- ↓ Toggle Gravity (Normal ↔ Reversed)<br>
- ← → Move Left/Right<br><br>
- Chain perfect flips for MULTIPLIERS!<br>
- Master the inversion to survive!
- </div>
- <button class="startBtn" onclick="startGame()">START GAUNTLET</button>
- <button class="startBtn" style="background: linear-gradient(135deg, #00ff00, #ff00ff); margin-left: 10px;" onclick="startAutoPlay()">AUTO-PLAY (AI)</button>
- </div>
- <div id="gameOverScreen" style="display: none;">
- <h1>GAUNTLET COMPLETE</h1>
- <div style="font-size: 32px; color: #00ffff; margin: 20px 0;">
- FINAL SCORE: <span id="finalScore">0</span>
- </div>
- <div style="font-size: 24px; color: #ffff00; margin: 10px 0;">
- MAX COMBO: <span id="maxCombo">0</span>
- </div>
- <button class="startBtn" onclick="restartGame()">RETRY GAUNTLET</button>
- <button class="startBtn" style="background: linear-gradient(135deg, #00ff00, #ff00ff); margin-left: 10px;" onclick="startAutoPlayFromGameOver()">AUTO-PLAY</button>
- </div>
- <canvas id="gameCanvas"></canvas>
- <div id="mobileControls" class="touch-controls">
- <div class="touch-btn" id="leftBtn">←</div>
- <div class="touch-center">
- <div class="touch-btn" id="flipBtn">↕</div>
- <div class="touch-btn" id="jumpBtn">↑</div>
- </div>
- <div class="touch-btn" id="rightBtn">→</div>
- </div>
- <script>
- const canvas = document.getElementById('gameCanvas');
- const ctx = canvas.getContext('2d');
- canvas.width = 800;
- canvas.height = 400;
- let gameState = 'menu';
- let score = 0;
- let multiplier = 1;
- let targetMultiplier = 1;
- let multiplierDecayTimer = 0;
- let level = 1;
- let flipCount = 0;
- let maxCombo = 0;
- let comboTimer = 0;
- let perfectFlipStreak = 0;
- let autoPlayActive = false;
- // Neural Network for AI
- class NeuralNetwork {
- constructor() {
- this.generation = 1;
- this.fitness = 0;
- this.bestFitness = 0;
- this.learningRate = 0.1;
- this.exploration = 0.3;
- this.currentReward = 0;
- this.totalRewardHistory = 0;
- this.maxXReached = 0;
- this.allTimeMaxX = 0;
- this.rewardMultiplier = 1;
- this.hopConfidence = 0;
- // Simplified network architecture
- this.weights1 = this.randomMatrix(8, 12);
- this.weights2 = this.randomMatrix(12, 6);
- this.weights3 = this.randomMatrix(6, 4);
- this.bias1 = this.randomMatrix(1, 12)[0];
- this.bias2 = this.randomMatrix(1, 6)[0];
- this.bias3 = this.randomMatrix(1, 4)[0];
- this.memory = [];
- this.maxMemory = 500;
- this.batchSize = 16;
- this.loadNetwork();
- }
- randomMatrix(rows, cols) {
- const matrix = [];
- for (let i = 0; i < rows; i++) {
- matrix[i] = [];
- for (let j = 0; j < cols; j++) {
- matrix[i][j] = (Math.random() - 0.5) * 2;
- }
- }
- return matrix;
- }
- sigmoid(x) {
- return 1 / (1 + Math.exp(-x));
- }
- relu(x) {
- return Math.max(0, x);
- }
- forward(inputs) {
- if (inputs.length !== 8) {
- return [0.5, 0.5, 0.5, 0.5];
- }
- // First layer
- const hidden1 = [];
- for (let i = 0; i < this.weights1[0].length; i++) {
- let sum = this.bias1[i];
- for (let j = 0; j < inputs.length; j++) {
- sum += inputs[j] * this.weights1[j][i];
- }
- hidden1[i] = this.relu(sum);
- }
- // Second layer
- const hidden2 = [];
- for (let i = 0; i < this.weights2[0].length; i++) {
- let sum = this.bias2[i];
- for (let j = 0; j < hidden1.length; j++) {
- sum += hidden1[j] * this.weights2[j][i];
- }
- hidden2[i] = this.relu(sum);
- }
- // Output layer
- const outputs = [];
- for (let i = 0; i < this.weights3[0].length; i++) {
- let sum = this.bias3[i];
- for (let j = 0; j < hidden2.length; j++) {
- sum += hidden2[j] * this.weights3[j][i];
- }
- outputs[i] = this.sigmoid(sum);
- }
- return outputs;
- }
- getState() {
- const nearestPlatform = this.findNearestPlatform();
- const nearestObstacle = this.findNearestObstacle();
- return [
- player.x / 1000,
- player.y / 400,
- player.vx / 10,
- player.vy / 15,
- player.gravityDirection,
- player.onGround ? 1 : 0,
- nearestPlatform ? (nearestPlatform.x - player.x) / 200 : 1,
- nearestObstacle ? (nearestObstacle.x - player.x) / 200 : 1
- ];
- }
- findNearestPlatform() {
- let nearest = null;
- let minDist = Infinity;
- for (const platform of platforms) {
- if (platform.x > player.x - 50) {
- const dist = Math.sqrt(
- Math.pow(platform.x - player.x, 2) +
- Math.pow(platform.y - player.y, 2)
- );
- if (dist < minDist) {
- minDist = dist;
- nearest = platform;
- }
- }
- }
- return nearest;
- }
- findNearestObstacle() {
- let nearest = null;
- let minDist = Infinity;
- for (const obstacle of obstacles) {
- if (obstacle.x > player.x - 50) {
- const dist = Math.sqrt(
- Math.pow(obstacle.x - player.x, 2) +
- Math.pow(obstacle.y - player.y, 2)
- );
- if (dist < minDist) {
- minDist = dist;
- nearest = obstacle;
- }
- }
- }
- return nearest;
- }
- calculateHopConfidence() {
- let landBelow = false;
- let landAhead = false;
- let nearestPlatformDistance = 1000;
- const checkX = player.x + player.width + 30;
- const checkRange = 150;
- for (const platform of platforms) {
- if (player.gravityDirection === 1) {
- if (platform.x <= player.x + player.width &&
- platform.x + platform.width >= player.x &&
- platform.y >= player.y + player.height - 5 &&
- platform.y <= player.y + player.height + 50) {
- landBelow = true;
- }
- } else {
- if (platform.x <= player.x + player.width &&
- platform.x + platform.width >= player.x &&
- platform.y + platform.height >= player.y - 50 &&
- platform.y + platform.height <= player.y + 5) {
- landBelow = true;
- }
- }
- if (platform.x > player.x && platform.x < player.x + checkRange) {
- const yDiff = Math.abs(platform.y - player.y);
- if (yDiff < 150) {
- landAhead = true;
- const dist = platform.x - (player.x + player.width);
- nearestPlatformDistance = Math.min(nearestPlatformDistance, dist);
- }
- }
- }
- if (landBelow) {
- this.hopConfidence = 100;
- } else if (player.onGround) {
- this.hopConfidence = 50;
- } else if (landAhead && nearestPlatformDistance < 80) {
- this.hopConfidence = 40;
- } else if (landAhead && nearestPlatformDistance < 120) {
- this.hopConfidence = 20;
- } else {
- this.hopConfidence = 0;
- }
- return this.hopConfidence;
- }
- decide() {
- const state = this.getState();
- const outputs = this.forward(state);
- this.calculateHopConfidence();
- if (Math.random() < this.exploration) {
- const randomAction = Math.floor(Math.random() * 4);
- outputs[randomAction] += 0.5;
- }
- if (this.hopConfidence < 30) {
- outputs[1] = 0.9;
- outputs[0] = 0;
- if (this.hopConfidence < 10 && player.onGround) {
- outputs[2] = 0.9;
- }
- } else if (this.hopConfidence < 70) {
- outputs[1] = Math.max(outputs[1], 0.8);
- if (player.onGround) {
- outputs[2] = Math.max(outputs[2], 0.8);
- }
- }
- outputs[1] = Math.max(outputs[1], 0.6);
- if (player.x > 200) {
- outputs[0] *= 0.1;
- }
- if (player.gravityDirection === -1) {
- outputs[3] *= 0.3;
- }
- const shouldFlip = outputs[3] > 0.8 && player.gravityDirection === 1 ||
- (outputs[3] > 0.4 && player.gravityDirection === -1);
- this.memory.push({
- state: state,
- outputs: outputs,
- reward: 0,
- hopConfidence: this.hopConfidence
- });
- if (this.memory.length > this.maxMemory) {
- this.memory.shift();
- }
- const decision = {
- left: outputs[0] > 0.7,
- right: outputs[1] > 0.2,
- jump: outputs[2] > 0.7 && player.onGround,
- flip: shouldFlip && !keys.downPressed
- };
- return decision;
- }
- updateFitness() {
- this.maxXReached = Math.max(this.maxXReached, player.x);
- const gravityBonus = player.gravityDirection === 1 ? 10 : 0;
- this.fitness = Math.max(0,
- score +
- (player.x / 10) +
- (flipCount * 2) +
- (collectibles.filter(c => c.collected).length * 50) +
- gravityBonus +
- (player.onGround ? 5 : 0)
- );
- const xProgress = Math.max(0, player.x / 100);
- const baseReward = xProgress * xProgress;
- if (this.totalRewardHistory > 0) {
- this.rewardMultiplier = 1 + Math.log10(1 + this.totalRewardHistory / 1000);
- }
- this.currentReward = Math.floor(baseReward * this.rewardMultiplier * 10);
- if (player.x > this.allTimeMaxX) {
- this.allTimeMaxX = player.x;
- this.currentReward += 100 * this.rewardMultiplier;
- }
- const recentMemory = this.memory.slice(-10);
- recentMemory.forEach(mem => {
- mem.reward = (this.fitness + this.currentReward) / 1000;
- });
- }
- learn() {
- if (this.memory.length < this.batchSize) return;
- const batch = this.memory.slice(-this.batchSize);
- batch.forEach(experience => {
- const { state, outputs, reward } = experience;
- if (reward > 0) {
- this.adjustWeights(state, outputs, reward);
- }
- });
- this.exploration = Math.max(0.1, this.exploration * 0.995);
- }
- adjustWeights(state, targetOutputs, reward) {
- const currentOutputs = this.forward(state);
- const error = [];
- for (let i = 0; i < targetOutputs.length; i++) {
- error[i] = (targetOutputs[i] - currentOutputs[i]) * reward * this.learningRate;
- }
- for (let i = 0; i < this.weights3.length; i++) {
- for (let j = 0; j < this.weights3[i].length; j++) {
- this.weights3[i][j] += error[j] * 0.01;
- }
- }
- }
- evolve() {
- this.generation++;
- this.totalRewardHistory += this.currentReward;
- if (this.fitness > this.bestFitness) {
- this.bestFitness = this.fitness;
- this.saveNetwork();
- } else {
- this.mutate();
- }
- this.fitness = 0;
- this.currentReward = 0;
- this.maxXReached = 0;
- this.learn();
- }
- mutate() {
- const mutationRate = 0.1;
- const mutationStrength = 0.2;
- for (let i = 0; i < this.weights1.length; i++) {
- for (let j = 0; j < this.weights1[i].length; j++) {
- if (Math.random() < mutationRate) {
- this.weights1[i][j] += (Math.random() - 0.5) * mutationStrength;
- }
- }
- }
- for (let i = 0; i < this.weights2.length; i++) {
- for (let j = 0; j < this.weights2[i].length; j++) {
- if (Math.random() < mutationRate) {
- this.weights2[i][j] += (Math.random() - 0.5) * mutationStrength;
- }
- }
- }
- for (let i = 0; i < this.weights3.length; i++) {
- for (let j = 0; j < this.weights3[i].length; j++) {
- if (Math.random() < mutationRate) {
- this.weights3[i][j] += (Math.random() - 0.5) * mutationStrength;
- }
- }
- }
- }
- saveNetwork() {
- const networkData = {
- weights1: this.weights1,
- weights2: this.weights2,
- weights3: this.weights3,
- bias1: this.bias1,
- bias2: this.bias2,
- bias3: this.bias3,
- generation: this.generation,
- bestFitness: this.bestFitness,
- totalRewardHistory: this.totalRewardHistory,
- allTimeMaxX: this.allTimeMaxX
- };
- localStorage.setItem('gravityFlipAI', JSON.stringify(networkData));
- }
- loadNetwork() {
- const saved = localStorage.getItem('gravityFlipAI');
- if (saved) {
- try {
- const networkData = JSON.parse(saved);
- if (networkData.weights1 &&
- networkData.weights1.length === 8) {
- this.weights1 = networkData.weights1;
- this.weights2 = networkData.weights2;
- this.weights3 = networkData.weights3;
- this.bias1 = networkData.bias1;
- this.bias2 = networkData.bias2;
- this.bias3 = networkData.bias3;
- this.generation = networkData.generation || 1;
- this.bestFitness = networkData.bestFitness || 0;
- this.totalRewardHistory = networkData.totalRewardHistory || 0;
- this.allTimeMaxX = networkData.allTimeMaxX || 0;
- } else {
- localStorage.removeItem('gravityFlipAI');
- }
- } catch (e) {
- localStorage.removeItem('gravityFlipAI');
- }
- }
- }
- }
- let aiPlayer = null;
- const player = {
- x: 100,
- y: 200,
- width: 30,
- height: 30,
- vx: 0,
- vy: 0,
- speed: 5,
- jumpPower: 12,
- gravity: 0.4,
- gravityDirection: 1,
- onGround: false,
- color: '#00ffff',
- trail: []
- };
- const camera = {
- x: 0,
- y: 0
- };
- let platforms = [];
- let obstacles = [];
- let collectibles = [];
- let particles = [];
- const keys = {
- downPressed: false
- };
- class Platform {
- constructor(x, y, width, height, type = 'normal') {
- this.x = x;
- this.y = y;
- this.width = width;
- this.height = height;
- this.type = type;
- this.color = type === 'moving' ? '#ffff00' : '#ff00ff';
- this.moveSpeed = 2;
- this.moveRange = 100;
- this.startY = y;
- }
- update() {
- if (this.type === 'moving') {
- this.y = this.startY + Math.sin(Date.now() * 0.002) * this.moveRange;
- }
- }
- draw() {
- ctx.save();
- ctx.fillStyle = this.color;
- ctx.shadowBlur = 10;
- ctx.shadowColor = this.color;
- ctx.fillRect(this.x - camera.x, this.y - camera.y, this.width, this.height);
- ctx.restore();
- }
- }
- class Obstacle {
- constructor(x, y, width, height, type = 'spike') {
- this.x = x;
- this.y = y;
- this.width = width;
- this.height = height;
- this.type = type;
- this.rotation = 0;
- }
- update() {
- if (this.type === 'rotating') {
- this.rotation += 0.05;
- }
- }
- draw() {
- ctx.save();
- ctx.translate(this.x - camera.x + this.width/2, this.y - camera.y + this.height/2);
- ctx.rotate(this.rotation);
- if (this.type === 'spike') {
- ctx.fillStyle = '#ff0000';
- ctx.beginPath();
- ctx.moveTo(-this.width/2, this.height/2);
- ctx.lineTo(0, -this.height/2);
- ctx.lineTo(this.width/2, this.height/2);
- ctx.closePath();
- ctx.fill();
- } else {
- ctx.fillStyle = '#ff6600';
- ctx.fillRect(-this.width/2, -this.height/2, this.width, this.height);
- }
- ctx.restore();
- }
- }
- class Collectible {
- constructor(x, y) {
- this.x = x;
- this.y = y;
- this.size = 20;
- this.collected = false;
- this.rotation = 0;
- this.value = 100;
- }
- update() {
- this.rotation += 0.1;
- }
- draw() {
- if (this.collected) return;
- ctx.save();
- ctx.translate(this.x - camera.x, this.y - camera.y);
- ctx.rotate(this.rotation);
- if (this.value > 100) {
- ctx.fillStyle = '#ff00ff';
- ctx.shadowBlur = 25;
- ctx.shadowColor = '#ff00ff';
- } else {
- ctx.fillStyle = '#ffff00';
- ctx.shadowBlur = 20;
- ctx.shadowColor = '#ffff00';
- }
- ctx.beginPath();
- for (let i = 0; i < 6; i++) {
- const angle = (Math.PI * 2 / 6) * i;
- const x = Math.cos(angle) * this.size/2;
- const y = Math.sin(angle) * this.size/2;
- if (i === 0) ctx.moveTo(x, y);
- else ctx.lineTo(x, y);
- }
- ctx.closePath();
- ctx.fill();
- ctx.restore();
- }
- }
- function generateLevel(levelNum) {
- platforms = [];
- obstacles = [];
- collectibles = [];
- platforms.push(new Platform(0, 350, 200, 50));
- platforms.push(new Platform(0, 0, 200, 50));
- const levelLength = 3000 + levelNum * 1000;
- let lastXBottom = 200;
- let lastYBottom = 350;
- let lastXTop = 200;
- let lastYTop = 50;
- const maxJumpDistance = 120;
- const minGap = 40;
- for (let i = 0; i < levelLength; i += 150) {
- const gapBottom = minGap + Math.random() * (maxJumpDistance - minGap);
- const widthBottom = 80 + Math.random() * 120;
- const heightBottom = 20 + Math.random() * 30;
- let yBottom = lastYBottom + (Math.random() - 0.5) * 80;
- yBottom = Math.max(250, Math.min(370, yBottom));
- const platformTypeBottom = Math.random() < 0.2 ? 'moving' : 'normal';
- const platformBottom = new Platform(lastXBottom + gapBottom, yBottom, widthBottom, heightBottom, platformTypeBottom);
- if (platformTypeBottom === 'moving') {
- platformBottom.moveRange = 40;
- }
- platforms.push(platformBottom);
- const gapTop = minGap + Math.random() * (maxJumpDistance - minGap);
- const widthTop = 80 + Math.random() * 120;
- const heightTop = 20 + Math.random() * 30;
- let yTop = lastYTop + (Math.random() - 0.5) * 80;
- yTop = Math.max(30, Math.min(150, yTop));
- const platformTypeTop = Math.random() < 0.2 ? 'moving' : 'normal';
- const platformTop = new Platform(lastXTop + gapTop, yTop, widthTop, heightTop, platformTypeTop);
- platformTop.color = '#00ffff';
- if (platformTypeTop === 'moving') {
- platformTop.moveRange = 40;
- }
- platforms.push(platformTop);
- if (Math.random() < 0.3 && i > 500) {
- if (Math.random() < 0.5) {
- lastXBottom += 200;
- } else {
- lastXTop += 200;
- }
- }
- if (Math.random() < (0.2 + levelNum * 0.05)) {
- obstacles.push(new Obstacle(
- lastXBottom + gapBottom + widthBottom/2 - 15,
- yBottom - 30,
- 30,
- 30,
- Math.random() < 0.5 ? 'spike' : 'rotating'
- ));
- }
- if (Math.random() < (0.2 + levelNum * 0.05)) {
- const topObstacle = new Obstacle(
- lastXTop + gapTop + widthTop/2 - 15,
- yTop + heightTop + 5,
- 30,
- 30,
- Math.random() < 0.5 ? 'spike' : 'rotating'
- );
- topObstacle.color = '#ff00ff';
- obstacles.push(topObstacle);
- }
- if (Math.random() < 0.7) {
- collectibles.push(new Collectible(lastXBottom + gapBottom + widthBottom/2, yBottom - 60));
- }
- if (Math.random() < 0.5) {
- const topCollectible = new Collectible(lastXTop + gapTop + widthTop/2, yTop + heightTop + 60);
- topCollectible.value = 200;
- collectibles.push(topCollectible);
- }
- lastXBottom = lastXBottom + gapBottom + widthBottom;
- lastYBottom = yBottom;
- lastXTop = lastXTop + gapTop + widthTop;
- lastYTop = yTop;
- }
- const finalX = Math.max(lastXBottom, lastXTop);
- platforms.push(new Platform(finalX + 100, 350, 300, 50));
- platforms.push(new Platform(finalX + 100, 0, 300, 50));
- }
- function createParticle(x, y, color) {
- const particle = document.createElement('div');
- particle.className = 'particle';
- particle.style.left = x + 'px';
- particle.style.top = y + 'px';
- particle.style.background = color;
- particle.style.setProperty('--dx', (Math.random() - 0.5) * 100 + 'px');
- particle.style.setProperty('--dy', (Math.random() - 0.5) * 100 + 'px');
- document.getElementById('particles').appendChild(particle);
- setTimeout(() => particle.remove(), 1000);
- }
- function showComboText(x, y, text) {
- const comboEl = document.createElement('div');
- comboEl.className = 'combo-text';
- comboEl.textContent = text;
- comboEl.style.left = x + 'px';
- comboEl.style.top = y + 'px';
- document.body.appendChild(comboEl);
- setTimeout(() => comboEl.remove(), 1000);
- }
- function updatePlayer() {
- if (autoPlayActive && aiPlayer) {
- const decision = aiPlayer.decide();
- keys['ArrowLeft'] = false;
- keys['ArrowRight'] = false;
- keys['ArrowUp'] = false;
- keys['ArrowDown'] = false;
- if (decision.left) keys['ArrowLeft'] = true;
- if (decision.right) keys['ArrowRight'] = true;
- if (decision.jump) keys['ArrowUp'] = true;
- if (decision.flip) keys['ArrowDown'] = true;
- aiPlayer.updateFitness();
- document.getElementById('aiGeneration').textContent = `GEN ${aiPlayer.generation}`;
- document.getElementById('aiFitness').textContent = `FITNESS: ${Math.floor(aiPlayer.fitness)}`;
- document.getElementById('aiHop').textContent = `HOP: ${Math.floor(aiPlayer.hopConfidence)}`;
- document.getElementById('aiReward').textContent = `REWARD: ${aiPlayer.currentReward}`;
- }
- if (keys['ArrowLeft']) {
- player.vx = -player.speed;
- } else if (keys['ArrowRight']) {
- player.vx = player.speed;
- } else {
- player.vx *= 0.8;
- }
- if (keys['ArrowUp'] && player.onGround) {
- player.vy = -player.jumpPower * player.gravityDirection;
- player.onGround = false;
- }
- if (keys['ArrowDown'] && !keys.downPressed) {
- keys.downPressed = true;
- flipGravity(player.gravityDirection === 1 ? -1 : 1);
- }
- if (!keys['ArrowDown']) {
- keys.downPressed = false;
- }
- player.vy += player.gravity * player.gravityDirection;
- player.vy = Math.max(-15, Math.min(15, player.vy));
- player.x += player.vx;
- player.y += player.vy;
- player.trail.push({x: player.x, y: player.y, alpha: 1});
- if (player.trail.length > 10) {
- player.trail.shift();
- }
- player.onGround = false;
- for (const platform of platforms) {
- if (checkCollision(player, platform)) {
- if (player.gravityDirection === 1) {
- if (player.vy > 0) {
- player.y = platform.y - player.height;
- player.vy = 0;
- player.onGround = true;
- }
- } else {
- if (player.vy < 0) {
- player.y = platform.y + platform.height;
- player.vy = 0;
- player.onGround = true;
- }
- }
- }
- }
- for (const obstacle of obstacles) {
- if (checkCollision(player, obstacle)) {
- gameOver();
- }
- }
- for (const collectible of collectibles) {
- if (!collectible.collected && checkCollision(player, {
- x: collectible.x - collectible.size/2,
- y: collectible.y - collectible.size/2,
- width: collectible.size,
- height: collectible.size
- })) {
- collectible.collected = true;
- if (targetMultiplier < 10) {
- targetMultiplier = 10;
- } else if (targetMultiplier <= 10) {
- targetMultiplier = 13;
- } else if (targetMultiplier <= 12) {
- targetMultiplier = 15;
- } else if (targetMultiplier <= 13) {
- targetMultiplier = 18;
- } else if (targetMultiplier <= 15) {
- targetMultiplier = 20;
- } else {
- targetMultiplier = Math.min(targetMultiplier + 2, 25);
- }
- multiplier = targetMultiplier;
- multiplierDecayTimer = 120;
- score += collectible.value * multiplier;
- for (let i = 0; i < 10; i++) {
- createParticle(
- collectible.x - camera.x + canvas.offsetLeft,
- collectible.y - camera.y + canvas.offsetTop,
- collectible.value > 100 ? '#ff00ff' : '#ffff00'
- );
- }
- showComboText(
- canvas.offsetLeft + canvas.width/2,
- canvas.offsetTop + 100,
- `x${multiplier}!`
- );
- maxCombo = Math.max(maxCombo, multiplier);
- }
- }
- if (multiplierDecayTimer > 0) {
- multiplierDecayTimer--;
- } else if (multiplier > 1) {
- if (multiplier >= 20) {
- targetMultiplier = 15;
- } else if (multiplier >= 15) {
- targetMultiplier = 10;
- } else if (multiplier >= 10) {
- targetMultiplier = 8;
- } else if (multiplier >= 8) {
- targetMultiplier = 5;
- } else if (multiplier >= 5) {
- targetMultiplier = 3;
- } else {
- targetMultiplier = 1;
- }
- multiplier = targetMultiplier;
- multiplierDecayTimer = 60;
- }
- const lastPlatform = platforms[platforms.length - 1];
- if (player.x > lastPlatform.x + lastPlatform.width/2) {
- nextLevel();
- }
- if (player.y > 600 || player.y < -200) {
- gameOver();
- }
- }
- function flipGravity(direction) {
- if (player.gravityDirection !== direction) {
- player.gravityDirection = direction;
- canvas.classList.toggle('flipped');
- flipCount++;
- for (let i = 0; i < 20; i++) {
- createParticle(
- player.x - camera.x + canvas.offsetLeft,
- player.y - camera.y + canvas.offsetTop,
- '#ff00ff'
- );
- }
- document.getElementById('flipCount').textContent = `FLIPS: ${flipCount}`;
- }
- }
- function checkCollision(a, b) {
- return a.x < b.x + b.width &&
- a.x + a.width > b.x &&
- a.y < b.y + b.height &&
- a.y + a.height > b.y;
- }
- function updateCamera() {
- camera.x = player.x - canvas.width / 3;
- camera.y = player.y - canvas.height / 2;
- camera.y = Math.max(-100, Math.min(100, camera.y));
- }
- function render() {
- ctx.fillStyle = 'rgba(10, 14, 39, 0.3)';
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- ctx.strokeStyle = 'rgba(255, 0, 255, 0.1)';
- ctx.lineWidth = 1;
- for (let i = 0; i < canvas.width; i += 50) {
- ctx.beginPath();
- ctx.moveTo(i, 0);
- ctx.lineTo(i, canvas.height);
- ctx.stroke();
- }
- for (let i = 0; i < canvas.height; i += 50) {
- ctx.beginPath();
- ctx.moveTo(0, i);
- ctx.lineTo(canvas.width, i);
- ctx.stroke();
- }
- ctx.strokeStyle = 'rgba(0, 255, 255, 0.2)';
- ctx.lineWidth = 2;
- ctx.setLineDash([10, 10]);
- ctx.beginPath();
- ctx.moveTo(0 - camera.x, 200 - camera.y);
- ctx.lineTo(10000 - camera.x, 200 - camera.y);
- ctx.stroke();
- ctx.setLineDash([]);
- platforms.forEach(platform => platform.draw());
- obstacles.forEach(obstacle => obstacle.draw());
- collectibles.forEach(collectible => collectible.draw());
- player.trail.forEach((point, index) => {
- ctx.fillStyle = `rgba(0, 255, 255, ${point.alpha * (index / player.trail.length)})`;
- ctx.fillRect(point.x - camera.x, point.y - camera.y, player.width, player.height);
- });
- ctx.save();
- ctx.fillStyle = player.color;
- ctx.shadowBlur = 20;
- ctx.shadowColor = player.color;
- ctx.fillRect(player.x - camera.x, player.y - camera.y, player.width, player.height);
- ctx.restore();
- document.getElementById('score').textContent = `SCORE: ${score}`;
- document.getElementById('multiplier').textContent = `MULTIPLIER: x${multiplier}`;
- document.getElementById('level').textContent = `LEVEL: ${level}`;
- }
- function gameLoop() {
- if (gameState !== 'playing') return;
- platforms.forEach(platform => platform.update());
- obstacles.forEach(obstacle => obstacle.update());
- collectibles.forEach(collectible => collectible.update());
- updatePlayer();
- updateCamera();
- render();
- requestAnimationFrame(gameLoop);
- }
- function startGame() {
- document.getElementById('startScreen').style.display = 'none';
- gameState = 'playing';
- score = 0;
- level = 1;
- multiplier = 1;
- targetMultiplier = 1;
- multiplierDecayTimer = 0;
- flipCount = 0;
- maxCombo = 1;
- player.x = 100;
- player.y = 300;
- player.vx = 0;
- player.vy = 0;
- player.gravityDirection = 1;
- player.onGround = false;
- canvas.classList.remove('flipped');
- generateLevel(level);
- gameLoop();
- }
- function nextLevel() {
- level++;
- score += 1000 * level;
- showComboText(
- canvas.offsetLeft + canvas.width/2,
- canvas.offsetTop + canvas.height/2,
- `LEVEL ${level}!`
- );
- generateLevel(level);
- player.x = 100;
- player.y = 200;
- player.vx = 0;
- player.vy = 0;
- }
- function gameOver() {
- gameState = 'gameover';
- if (autoPlayActive && aiPlayer) {
- aiPlayer.evolve();
- setTimeout(() => {
- if (autoPlayActive) {
- document.getElementById('gameOverScreen').style.display = 'none';
- restartGame();
- }
- }, 1000);
- } else {
- document.getElementById('finalScore').textContent = score;
- document.getElementById('maxCombo').textContent = `x${maxCombo}`;
- document.getElementById('gameOverScreen').style.display = 'block';
- }
- }
- function restartGame() {
- if (!autoPlayActive) {
- document.getElementById('gameOverScreen').style.display = 'none';
- }
- startGame();
- }
- function startAutoPlay() {
- autoPlayActive = true;
- if (!aiPlayer) {
- aiPlayer = new NeuralNetwork();
- }
- document.getElementById('startScreen').style.display = 'none';
- document.getElementById('aiStatus').style.display = 'block';
- startGame();
- }
- document.addEventListener('keydown', (e) => {
- if (!autoPlayActive) {
- keys[e.key] = true;
- if (e.key.startsWith('Arrow')) {
- e.preventDefault();
- }
- }
- });
- document.addEventListener('keyup', (e) => {
- if (!autoPlayActive) {
- keys[e.key] = false;
- }
- });
- function startAutoPlayFromGameOver() {
- document.getElementById('gameOverScreen').style.display = 'none';
- startAutoPlay();
- }
- // Touch controls for mobile
- const leftBtn = document.getElementById('leftBtn');
- const rightBtn = document.getElementById('rightBtn');
- const flipBtn = document.getElementById('flipBtn');
- const jumpBtn = document.getElementById('jumpBtn');
- leftBtn.addEventListener('touchstart', () => keys['ArrowLeft'] = true);
- leftBtn.addEventListener('touchend', () => keys['ArrowLeft'] = false);
- rightBtn.addEventListener('touchstart', () => keys['ArrowRight'] = true);
- rightBtn.addEventListener('touchend', () => keys['ArrowRight'] = false);
- flipBtn.addEventListener('touchstart', () => {
- keys['ArrowDown'] = true;
- flipGravity(player.gravityDirection === 1 ? -1 : 1);
- });
- flipBtn.addEventListener('touchend', () => keys['ArrowDown'] = false);
- jumpBtn.addEventListener('touchstart', () => keys['ArrowUp'] = true);
- jumpBtn.addEventListener('touchend', () => keys['ArrowUp'] = false);
- // Initialize the canvas
- ctx.fillStyle = '#0a0e27';
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment