Gummy Arena v1

This commit is contained in:
Demetri Pirpiris 2025-03-15 00:49:30 -06:00
parent d867cd9065
commit 3cde484557
3 changed files with 1748 additions and 0 deletions

330
index.html Normal file
View File

@ -0,0 +1,330 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Neon Synthwave Snake Evolution Simulator</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<canvas id="simCanvas"></canvas>
<!-- Loading Overlay -->
<div id="loadingOverlay">
<div class="sinewave">Fast Forwarding...</div>
</div>
<!-- Small "?" Help Button -->
<button id="helpButton" class="help-button">?</button>
<!-- Help Modal (Hidden By Default) -->
<div id="helpModal" class="help-modal">
<div class="help-content">
<button id="helpClose" class="help-close">×</button>
<h2>How This Simulation Works</h2>
<p>
Welcome to the Neon Synthwave Snake Evolution Simulator! Your main goal is to help create
(and sometimes directly control) the environment so the snakes can grow as long as possible.
</p>
<p>
Each snake tries to eat food (and occasionally each other) to gain points, grow in length, and
eventually become the biggest snake. Behind the scenes, genetic algorithms evolve the snakes
“brains” to adapt their behavior over time.
</p>
<p>
Theres also a hidden twist: the size of your browser window subtly influences the snakes
learning process. Try making the canvas larger or smaller to see how it affects the outcome!
</p>
<p style="font-weight: bold;">
Objective: Achieve the highest possible snake length!
</p>
</div>
</div>
<!-- Control Panel (Bottom) -->
<div id="controlPanel" class="control-panel">
<div class="header">
Simulation Controls
<button class="minimize"></button>
</div>
<div class="content">
<!-- Top-level Tabs -->
<div class="tab-buttons top-level-tabs">
<button class="tabBtn active" data-tab="rewardTab">Reward</button>
<button class="tabBtn" data-tab="foodTab">Food</button>
<button class="tabBtn" data-tab="snakesTab">Snakes</button>
<button class="tabBtn" data-tab="evolutionTab">Evolution</button>
<button class="tabBtn" data-tab="environmentTab">Environment</button>
</div>
<!-- REWARD TAB (with 2 sub-tabs) -->
<div id="rewardTab" class="tab-content active">
<div class="tab-buttons reward-subtabs">
<button class="tabBtn active" data-subtab="rewardPointsSubTab">Points & Kills</button>
<button class="tabBtn" data-subtab="rewardGrowthSubTab">Survival & Growth</button>
</div>
<!-- Sub-tab 1: Points & Kills -->
<div id="rewardPointsSubTab" class="reward-sub-content active">
<div class="tab-description">
Adjust food points, dropped food, and kill bonus rewards here.
</div>
<div class="control-group">
<label for="foodPointsSlider">Food Points:</label>
<input type="range" id="foodPointsSlider" min="0" max="10" value="3" step="0.5">
<span id="foodPointsVal">3</span>
<div class="setting-description">
Higher values give snakes more points for eating standard food.
</div>
</div>
<div class="control-group">
<label for="droppedFoodPointsSlider">Dropped Food:</label>
<input type="range" id="droppedFoodPointsSlider" min="0" max="10" value="10" step="0.5">
<span id="droppedFoodPointsVal">10</span>
<div class="setting-description">
When a snake dies, it leaves behind 'dropped food' worth these extra points.
</div>
</div>
<div class="control-group">
<label for="killBonusSlider">Kill Bonus:</label>
<input type="range" id="killBonusSlider" min="0" max="10" value="0" step="1">
<span id="killBonusVal">0</span>
<div class="setting-description">
If a snake collides with your body, you gain these extra points.
</div>
</div>
</div>
<!-- Sub-tab 2: Survival & Growth -->
<div id="rewardGrowthSubTab" class="reward-sub-content">
<div class="tab-description">
Adjust survival bonus per frame and extra points for bigger snakes.
</div>
<div class="control-group">
<label for="survivalBonusSlider">Survival Bonus:</label>
<input type="range" id="survivalBonusSlider" min="0" max="10" value="2" step="0.5">
<span id="survivalBonusVal">2</span>
<div class="setting-description">
Each frame, snakes get a tiny fraction of this as extra points (×0.001).
</div>
</div>
<div class="control-group">
<label for="lengthBonusSlider">Length Bonus:</label>
<input type="range" id="lengthBonusSlider" min="0" max="10" value="1" step="0.5">
<span id="lengthBonusVal">1</span>
<div class="setting-description">
Extra points for each unit of snake length. Encourages bigger snakes!
</div>
</div>
</div>
</div>
<!-- FOOD TAB -->
<div id="foodTab" class="tab-content">
<div class="tab-description">
Control how food is generated and how big your snakes can get from it.
</div>
<div class="control-group">
<label for="maxFoodSlider">Max Food:</label>
<input type="range" id="maxFoodSlider" min="50" max="200" value="120" step="1">
<span id="maxFoodVal">120</span>
<div class="setting-description">
The map won't exceed this many food items at once.
</div>
</div>
<div class="control-group">
<label for="spawnChanceSlider">Food Spawn Chance:</label>
<input type="range" id="spawnChanceSlider" min="1" max="10" value="3" step="0.5">
<span id="spawnChanceVal">3</span>
<div class="setting-description">
Each frame, if below the max, there's a spawn chance out of 100.
</div>
</div>
<div class="control-group">
<label for="bodyLengthIncSlider">Length Increment:</label>
<input type="range" id="bodyLengthIncSlider" min="5" max="20" value="10" step="1">
<span id="bodyLengthIncVal">10</span>
<div class="setting-description">
Each time a snake eats, it grows by this many segments.
</div>
</div>
<div class="control-group">
<label for="thicknessIncSlider">Thickness Increment:</label>
<input type="range" id="thicknessIncSlider" min="0.01" max="0.1" value="0.05" step="0.01">
<span id="thicknessIncVal">0.05</span>
<div class="setting-description">
Controls how 'fat' the snake gets with each meal.
</div>
</div>
</div>
<!-- SNAKES TAB -->
<div id="snakesTab" class="tab-content">
<div class="tab-description">
Control the number of snakes and their intelligence or speed.
</div>
<div class="control-group">
<label for="snakeCountSlider">Snake Count:</label>
<input type="range" id="snakeCountSlider" min="1" max="100" value="30" step="1">
<span id="snakeCountVal">30</span>
<div class="setting-description">
Increase or decrease the total population of snakes.
</div>
</div>
<div class="control-group">
<label for="snakeSpeedSlider">Snake Speed:</label>
<input type="range" id="snakeSpeedSlider" min="1" max="50" step="0.1" value="3.5">
<span id="snakeSpeedVal">3.5</span>
<div class="setting-description">
Snakes turn at a rate up to 0.15 / (lengthFactor) * speed; higher is more agile.
</div>
</div>
<!-- New Slider: Base Turn Rate -->
<div class="control-group">
<label for="turnRateSlider">Base Turn Rate:</label>
<input type="range" id="turnRateSlider" min="0.01" max="0.3" step="0.01" value="0.15">
<span id="turnRateVal">0.15</span>
<div class="setting-description">
Controls how sharply snakes can turn (before factoring in size).
</div>
</div>
<!-- New Slider: Boost Cost (length/sec) -->
<div class="control-group">
<label for="boostCostSlider">Boost Cost (length/sec):</label>
<input type="range" id="boostCostSlider" min="0" max="5" step="0.1" value="1">
<span id="boostCostVal">1</span>
<div class="setting-description">
How many body length units are spent per second while boosting.
</div>
</div>
<!-- New Slider: Boost Speed Multiplier -->
<div class="control-group">
<label for="boostMultiplierSlider">Boost Speed Multiplier:</label>
<input type="range" id="boostMultiplierSlider" min="1" max="3" step="0.1" value="2">
<span id="boostMultiplierVal">2</span>
<div class="setting-description">
How much faster the snake goes when boosting.
</div>
</div>
<div class="control-group">
<label for="brainSizeSlider">Brain Size:</label>
<input type="range" id="brainSizeSlider" min="5" max="50" value="15" step="1">
<span id="brainSizeVal">15</span>
<div class="setting-description">
The bigger the brain, the more complex the snake's decision-making.
</div>
</div>
</div>
<!-- EVOLUTION TAB -->
<div id="evolutionTab" class="tab-content">
<div class="tab-description">
Adjust evolutionary parameters to influence genetic changes.
</div>
<div class="control-group">
<label for="mutationRateSlider">Mutation Rate:</label>
<input type="range" id="mutationRateSlider" min="0" max="0.2" value="0.1" step="0.01">
<span id="mutationRateVal">0.1</span>
<div class="setting-description">
Probability that a brain's bit will flip during mutation.
</div>
</div>
<div class="control-group">
<label for="crossoverBiasSlider">Crossover Bias:</label>
<input type="range" id="crossoverBiasSlider" min="0" max="1" value="0.5" step="0.05">
<span id="crossoverBiasVal">0.5</span>
<div class="setting-description">
Likelihood of choosing a gene from the first parent during crossover.
</div>
</div>
</div>
<!-- ENVIRONMENT TAB -->
<div id="environmentTab" class="tab-content">
<div class="tab-description">
Adjust environmental parameters that affect food and snake respawning.
</div>
<div class="control-group">
<label for="foodDecayTimeSlider">Food Decay Time:</label>
<input type="range" id="foodDecayTimeSlider" min="500" max="5000" value="2000" step="100">
<span id="foodDecayTimeVal">2000</span>
<div class="setting-description">
Time (ms) before dropped food disappears.
</div>
</div>
<div class="control-group">
<label for="respawnDelaySlider">Respawn Delay:</label>
<input type="range" id="respawnDelaySlider" min="0" max="5" value="0" step="0.5">
<span id="respawnDelayVal">0</span>
<div class="setting-description">
Delay (sec) before a new snake spawns after one dies.
</div>
</div>
</div>
<div class="control-buttons">
<button id="restoreDefaultsButton">Restore Defaults</button>
<button id="resetSimButton">Reset Simulation</button>
</div>
</div>
</div>
<!-- Combined Time & Leaderboard Panel (Top) -->
<div id="timeAndLeaderboardPanel" class="time-panel">
<div class="header">
Time &amp; Leaderboards
<button class="minimize"></button>
</div>
<div class="content">
<div class="tab-buttons top-level-tabs">
<button class="tabBtn active" data-tab="leaderboardTab">Leaderboard</button>
<button class="tabBtn" data-tab="timeAccelTab">Time Acceleration</button>
</div>
<div id="leaderboardTab" class="tab-content active">
<div style="
display: flex;
justify-content: space-between;
align-items: flex-start;
text-align: center;
margin-bottom: 5px;
">
<div class="leaderboardColumn" style="width: 48%;">
<h3 style="margin: 0; font-size: 12px;">Current Leaderboard</h3>
<div id="currentLeaderboardColumn" class="leaderboard-text"></div>
</div>
<div class="leaderboardColumn" style="width: 48%;">
<h3 style="margin: 0; font-size: 12px;">All-Time Leaderboard</h3>
<div id="allTimeLeaderboardColumn" class="leaderboard-text"></div>
</div>
</div>
<div id="leaderboardTime" style="
text-align: center;
font-size: 12px;
margin-top: 3px;
"></div>
</div>
<div id="timeAccelTab" class="tab-content">
<div class="control-group" style="text-align:center;">
<label for="timeAccelSlider">Fast-Forward (minutes):</label><br/>
<input type="range" id="timeAccelSlider" min="0" max="60" value="0" step="1">
<span id="timeAccelVal">0</span>
<div class="setting-description">
Slide to skip simulation time quickly. Great for speeding evolution!
</div>
</div>
<div class="control-buttons">
<button id="fastForwardButton">Fast Forward</button>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>

1114
script.js Normal file

File diff suppressed because it is too large Load Diff

304
styles.css Normal file
View File

@ -0,0 +1,304 @@
/* Full-screen layout */
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background: #000;
overflow: hidden;
font-family: 'Roboto', sans-serif;
color: #FFF;
}
/* Canvas covers the entire window */
canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: block;
background: #000;
}
/* Panels for controls + time & leaderboards */
.control-panel,
.time-panel {
position: absolute;
padding: 8px;
user-select: none;
z-index: 2;
border: 1px solid #555;
border-radius: 8px;
box-shadow: 0 0 10px rgba(255, 0, 255, 0.3);
background: linear-gradient(135deg, rgba(20,0,40,0.8), rgba(0,0,0,0.6));
}
/* widths: make both 420px to match each other in desktop */
.control-panel {
width: 420px;
left: 50%;
bottom: 20px;
transform: translateX(-50%);
}
.time-panel {
width: 420px;
left: 50%;
top: 20px;
transform: translateX(-50%);
text-align: center;
}
/* Panel headers */
.control-panel .header,
.time-panel .header {
font-weight: bold;
padding: 6px;
background: rgba(0,0,0,0.8);
display: flex;
justify-content: space-between;
align-items: center;
border-radius: 5px;
box-shadow: inset 0 0 5px rgba(255,0,255,0.2);
}
/* Panel content */
.control-panel .content,
.time-panel .content {
margin-top: 8px;
font-size: 14px;
}
/* Buttons in panels */
.control-panel button,
.time-panel button {
background: #444;
border: none;
color: #FFF;
cursor: pointer;
padding: 4px 8px;
font-weight: bold;
border-radius: 4px;
}
.control-panel button:hover,
.time-panel button:hover {
background: #666;
}
/* Control panel styling */
.control-group {
margin-bottom: 10px;
text-align: left;
}
.control-group label {
display: inline-block;
width: 150px;
font-weight: bold;
}
.control-group input[type=range] {
width: 200px;
}
.control-buttons {
text-align: center;
margin-top: 10px;
}
.control-buttons button {
margin: 0 5px;
padding: 6px 12px;
background: linear-gradient(90deg, #8000ff 0%, #ff00ff 100%);
border: none;
color: #FFF;
cursor: pointer;
font-weight: bold;
border-radius: 4px;
box-shadow: 0 0 5px rgba(255,0,255,0.4);
}
.control-buttons button:hover {
opacity: 0.8;
}
/* Main tab styling */
.top-level-tabs {
display: flex;
margin-bottom: 10px;
}
.top-level-tabs .tabBtn {
flex: 1;
margin: 0 2px;
background: #444;
border: none;
color: #FFF;
cursor: pointer;
padding: 4px 8px;
font-weight: bold;
border-radius: 4px;
}
.top-level-tabs .tabBtn.active {
background: linear-gradient(90deg, #8000ff 0%, #ff00ff 100%);
box-shadow: 0 0 5px rgba(255,0,255,0.4);
}
/* Sub-tab styling for Reward sub-tabs */
.reward-subtabs {
display: flex;
margin-bottom: 10px;
}
.reward-subtabs .tabBtn {
flex: 1;
margin: 0 2px;
background: #444;
border: none;
color: #FFF;
cursor: pointer;
padding: 4px 8px;
font-weight: bold;
border-radius: 4px;
}
.reward-subtabs .tabBtn.active {
background: linear-gradient(90deg, #8000ff 0%, #ff00ff 100%);
box-shadow: 0 0 5px rgba(255,0,255,0.4);
}
.reward-sub-content {
display: none; /* hidden by default */
}
.reward-sub-content.active {
display: block;
}
/* .tab-content is used for top-level content areas */
.tab-content {
display: none; /* hidden by default */
}
.tab-content.active {
display: block;
}
/* Basic text styling for descriptions */
.tab-description {
font-size: 13px;
font-style: italic;
margin-bottom: 10px;
opacity: 0.9;
}
.setting-description {
font-size: 12px;
font-style: italic;
margin-top: 4px;
opacity: 0.8;
}
/* Leaderboard styling */
.leaderboard-text {
font-size: 12px;
line-height: 1.2;
word-break: break-word; /* allow word wrapping */
}
/* Loading overlay for fast-forward */
#loadingOverlay {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.8);
color: #FFF;
display: none;
align-items: center;
justify-content: center;
z-index: 10;
text-align: center;
}
@keyframes sinewave {
0% { transform: translateX(-50%) translateY(0); }
50% { transform: translateX(-50%) translateY(-20px); }
100% { transform: translateX(-50%) translateY(0); }
}
#loadingOverlay .sinewave {
font-size: 24px;
animation: sinewave 1s infinite;
}
/* Center the "?" help button on the right edge */
.help-button {
position: absolute;
top: 50%;
right: 10px;
transform: translateY(-50%);
z-index: 3;
background: #444;
border: 1px solid #555;
color: #FFF;
font-size: 16px;
padding: 4px 8px;
cursor: pointer;
border-radius: 50%;
box-shadow: 0 0 8px rgba(255, 0, 255, 0.5);
}
.help-button:hover {
background: #666;
}
/* Help Modal Overlay */
.help-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.8);
display: none;
z-index: 9999;
align-items: center;
justify-content: center; /* center horizontally */
}
/* Help Modal Content */
.help-content {
position: relative;
width: 400px;
max-width: 90%;
padding: 16px;
border: 1px solid #555;
border-radius: 8px;
background: linear-gradient(135deg, rgba(20,0,40,0.9), rgba(0,0,0,0.7));
box-shadow: 0 0 10px rgba(255, 0, 255, 0.4);
text-align: left;
}
/* Close button inside modal */
.help-close {
position: absolute;
top: 6px;
right: 8px;
background: transparent;
border: none;
color: #FFF;
font-size: 18px;
cursor: pointer;
}
.help-close:hover {
color: #ff00ff;
}
/* Mobile-friendly adjustments */
@media (max-width: 768px) {
.control-panel {
width: 90%;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
}
.time-panel {
width: 90%;
top: 20px;
left: 50%;
transform: translateX(-50%);
}
/* Stack the leaderboards vertically in mobile to avoid overflow */
.leaderboardColumn {
width: 100% !important;
margin-bottom: 10px;
padding-left: 0 !important;
}
}