more ui fixes

This commit is contained in:
Demetri Pirpiris 2025-03-15 12:33:29 -06:00
parent f88acb2182
commit e7623335d3
3 changed files with 258 additions and 113 deletions

View File

@ -7,16 +7,17 @@
<link rel="stylesheet" href="styles.css"> <link rel="stylesheet" href="styles.css">
</head> </head>
<body> <body>
<canvas id="simCanvas"></canvas>
<!-- Wrap canvas in a scalable container -->
<div id="simCanvasWrapper">
<canvas id="simCanvas"></canvas>
</div>
<!-- Loading Overlay --> <!-- Loading Overlay -->
<div id="loadingOverlay"> <div id="loadingOverlay">
<div class="sinewave">Fast Forwarding...</div> <div class="sinewave">Fast Forwarding...</div>
</div> </div>
<!-- Small "?" Help Button -->
<button id="helpButton" class="help-button">?</button>
<!-- Help Modal (Hidden By Default) --> <!-- Help Modal (Hidden By Default) -->
<div id="helpModal" class="help-modal"> <div id="helpModal" class="help-modal">
<div class="help-content"> <div class="help-content">
@ -45,6 +46,8 @@
<div id="controlPanel" class="control-panel"> <div id="controlPanel" class="control-panel">
<div class="header"> <div class="header">
Simulation Controls Simulation Controls
<!-- Move the help button here -->
<button id="helpButton" class="help-button">?</button>
<button class="minimize"></button> <button class="minimize"></button>
</div> </div>
<div class="content"> <div class="content">
@ -54,8 +57,8 @@
<button class="tabBtn active" data-tab="rewardTab">Reward</button> <button class="tabBtn active" data-tab="rewardTab">Reward</button>
<button class="tabBtn" data-tab="foodTab">Food</button> <button class="tabBtn" data-tab="foodTab">Food</button>
<button class="tabBtn" data-tab="snakesTab">Snakes</button> <button class="tabBtn" data-tab="snakesTab">Snakes</button>
<button class="tabBtn" data-tab="evolutionTab">Evolution</button> <!-- Combine Evolution + Environment => 'World' main tab -->
<button class="tabBtn" data-tab="environmentTab">Environment</button> <button class="tabBtn" data-tab="worldTab">World</button>
</div> </div>
<!-- REWARD TAB (with 2 sub-tabs) --> <!-- REWARD TAB (with 2 sub-tabs) -->
@ -159,110 +162,126 @@
</div> </div>
</div> </div>
<!-- SNAKES TAB --> <!-- SNAKES TAB (now subdivided) -->
<div id="snakesTab" class="tab-content"> <div id="snakesTab" class="tab-content">
<div class="tab-description"> <div class="tab-buttons snakes-subtabs">
Control the number of snakes and their intelligence or speed. <button class="tabBtn active" data-subtab="snakesGeneralSubTab">General</button>
<button class="tabBtn" data-subtab="snakesBoostSubTab">Boost &amp; Turn</button>
</div> </div>
<div class="control-group">
<label for="snakeCountSlider">Snake Count:</label> <!-- Sub-tab 1: General -->
<input type="range" id="snakeCountSlider" min="1" max="100" value="30" step="1"> <div id="snakesGeneralSubTab" class="snakes-sub-content active">
<span id="snakeCountVal">30</span> <div class="tab-description">
<div class="setting-description"> Control the basic snake population, speed, and brain size.
Increase or decrease the total population of snakes.
</div> </div>
</div> <div class="control-group">
<div class="control-group"> <label for="snakeCountSlider">Snake Count:</label>
<label for="snakeSpeedSlider">Snake Speed:</label> <input type="range" id="snakeCountSlider" min="1" max="100" value="30" step="1">
<input type="range" id="snakeSpeedSlider" min="1" max="50" step="0.1" value="3.5"> <span id="snakeCountVal">30</span>
<span id="snakeSpeedVal">3.5</span> <div class="setting-description">
<div class="setting-description"> Increase or decrease the total population of snakes.
Snakes turn at a rate up to 0.15 / (lengthFactor) * speed; higher is more agile. </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>
<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>
</div> </div>
<!-- New Slider: Base Turn Rate --> <!-- Sub-tab 2: Boost & Turn -->
<div class="control-group"> <div id="snakesBoostSubTab" class="snakes-sub-content">
<label for="turnRateSlider">Base Turn Rate:</label> <div class="tab-description">
<input type="range" id="turnRateSlider" min="0.01" max="0.3" step="0.01" value="0.15"> Manage the base turn rate and boosting parameters.
<span id="turnRateVal">0.15</span>
<div class="setting-description">
Controls how sharply snakes can turn (before factoring in size).
</div> </div>
</div> <div class="control-group">
<label for="turnRateSlider">Base Turn Rate:</label>
<!-- New Slider: Boost Cost (length/sec) --> <input type="range" id="turnRateSlider" min="0.01" max="0.3" step="0.01" value="0.15">
<div class="control-group"> <span id="turnRateVal">0.15</span>
<label for="boostCostSlider">Boost Cost (length/sec):</label> <div class="setting-description">
<input type="range" id="boostCostSlider" min="0" max="5" step="0.1" value="1"> Controls how sharply snakes can turn (before factoring in size).
<span id="boostCostVal">1</span> </div>
<div class="setting-description">
How many body length units are spent per second while boosting.
</div> </div>
</div> <div class="control-group">
<label for="boostCostSlider">Boost Cost (length/sec):</label>
<!-- New Slider: Boost Speed Multiplier --> <input type="range" id="boostCostSlider" min="0" max="5" step="0.1" value="1">
<div class="control-group"> <span id="boostCostVal">1</span>
<label for="boostMultiplierSlider">Boost Speed Multiplier:</label> <div class="setting-description">
<input type="range" id="boostMultiplierSlider" min="1" max="3" step="0.1" value="2"> How many body length units are spent per second while boosting.
<span id="boostMultiplierVal">2</span> </div>
<div class="setting-description">
How much faster the snake goes when boosting.
</div> </div>
</div> <div class="control-group">
<label for="boostMultiplierSlider">Boost Speed Multiplier:</label>
<div class="control-group"> <input type="range" id="boostMultiplierSlider" min="1" max="3" step="0.1" value="2">
<label for="brainSizeSlider">Brain Size:</label> <span id="boostMultiplierVal">2</span>
<input type="range" id="brainSizeSlider" min="5" max="50" value="15" step="1"> <div class="setting-description">
<span id="brainSizeVal">15</span> How much faster the snake goes when boosting.
<div class="setting-description"> </div>
The bigger the brain, the more complex the snake's decision-making.
</div> </div>
</div> </div>
</div> </div>
<!-- EVOLUTION TAB --> <!-- COMBINED "WORLD" TAB with 2 sub-tabs (Environment + Evolution) -->
<div id="evolutionTab" class="tab-content"> <div id="worldTab" class="tab-content">
<div class="tab-description"> <div class="tab-buttons world-subtabs">
Adjust evolutionary parameters to influence genetic changes. <button class="tabBtn active" data-subtab="worldEnvSubTab">Environment</button>
<button class="tabBtn" data-subtab="worldEvoSubTab">Evolution</button>
</div> </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 --> <!-- Sub-tab 1: Environment (old environmentTab content) -->
<div id="environmentTab" class="tab-content"> <div id="worldEnvSubTab" class="world-sub-content active">
<div class="tab-description"> <div class="tab-description">
Adjust environmental parameters that affect food and snake respawning. Adjust environmental parameters that affect food and snake respawning.
</div> </div>
<div class="control-group"> <div class="control-group">
<label for="foodDecayTimeSlider">Food Decay Time:</label> <label for="foodDecayTimeSlider">Food Decay Time:</label>
<input type="range" id="foodDecayTimeSlider" min="500" max="5000" value="2000" step="100"> <input type="range" id="foodDecayTimeSlider" min="500" max="5000" value="2000" step="100">
<span id="foodDecayTimeVal">2000</span> <span id="foodDecayTimeVal">2000</span>
<div class="setting-description"> <div class="setting-description">
Time (ms) before dropped food disappears. 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> </div>
<div class="control-group">
<label for="respawnDelaySlider">Respawn Delay:</label> <!-- Sub-tab 2: Evolution (old evolutionTab content) -->
<input type="range" id="respawnDelaySlider" min="0" max="5" value="0" step="0.5"> <div id="worldEvoSubTab" class="world-sub-content">
<span id="respawnDelayVal">0</span> <div class="tab-description">
<div class="setting-description"> Adjust evolutionary parameters to influence genetic changes.
Delay (sec) before a new snake spawns after one dies. </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>
</div> </div>
</div> </div>

View File

@ -9,7 +9,7 @@ let lastFrameTime = null;
/* Updated defaults */ /* Updated defaults */
const DEFAULT_BRAIN_SIZE = 15; const DEFAULT_BRAIN_SIZE = 15;
const DEFAULT_SNAKE_SPEED = 3.5; const DEFAULT_SNAKE_SPEED = 1;
const DEFAULT_MAX_FOOD = 120; const DEFAULT_MAX_FOOD = 120;
const DEFAULT_SPAWN_CHANCE = 3; const DEFAULT_SPAWN_CHANCE = 3;
const DEFAULT_FOOD_POINTS = 3; const DEFAULT_FOOD_POINTS = 3;
@ -17,9 +17,9 @@ const DEFAULT_DROPPED_FOOD_POINTS = 10;
const DEFAULT_SURVIVAL_BONUS = 2; const DEFAULT_SURVIVAL_BONUS = 2;
const DEFAULT_BODY_LENGTH_INCREMENT = 10; const DEFAULT_BODY_LENGTH_INCREMENT = 10;
const DEFAULT_THICKNESS_INCREMENT = 0.05; const DEFAULT_THICKNESS_INCREMENT = 0.05;
const DEFAULT_SNAKE_COUNT = 30; const DEFAULT_SNAKE_COUNT = 15;
const DEFAULT_LENGTH_BONUS = 1; const DEFAULT_LENGTH_BONUS = 3;
const DEFAULT_KILL_BONUS = 0; const DEFAULT_KILL_BONUS = 3;
/* Evolution & Environment Defaults */ /* Evolution & Environment Defaults */
const DEFAULT_MUTATION_RATE = 0.1; const DEFAULT_MUTATION_RATE = 0.1;
@ -188,6 +188,11 @@ function removeDraggable(panel) {
attachMinimizeLogic(clone); attachMinimizeLogic(clone);
attachTopLevelTabLogic(clone); attachTopLevelTabLogic(clone);
attachRewardSubTabLogic(clone); attachRewardSubTabLogic(clone);
// new calls for sub-tabs:
attachSnakesSubTabLogic(clone);
attachWorldSubTabLogic(clone);
wireUIEvents(); wireUIEvents();
} }
@ -284,6 +289,48 @@ function attachRewardSubTabLogic(panel) {
}); });
}); });
} }
/* NEW: Attach snakes sub-tab logic */
function attachSnakesSubTabLogic(panel) {
const snakesTab = panel.querySelector('#snakesTab');
if (!snakesTab) return;
const subTabButtons = snakesTab.querySelectorAll('.snakes-subtabs .tabBtn');
subTabButtons.forEach(subBtn => {
subBtn.addEventListener('click', () => {
subTabButtons.forEach(b => b.classList.remove('active'));
const allSubContents = snakesTab.querySelectorAll('.snakes-sub-content');
allSubContents.forEach(s => s.classList.remove('active'));
subBtn.classList.add('active');
const subId = subBtn.getAttribute('data-subtab');
const targetSub = snakesTab.querySelector('#' + subId);
if (targetSub) {
targetSub.classList.add('active');
}
});
});
}
/* NEW: Attach world sub-tab logic */
function attachWorldSubTabLogic(panel) {
const worldTab = panel.querySelector('#worldTab');
if (!worldTab) return;
const subTabButtons = worldTab.querySelectorAll('.world-subtabs .tabBtn');
subTabButtons.forEach(subBtn => {
subBtn.addEventListener('click', () => {
subTabButtons.forEach(b => b.classList.remove('active'));
const allSubContents = worldTab.querySelectorAll('.world-sub-content');
allSubContents.forEach(s => s.classList.remove('active'));
subBtn.classList.add('active');
const subId = subBtn.getAttribute('data-subtab');
const targetSub = worldTab.querySelector('#' + subId);
if (targetSub) {
targetSub.classList.add('active');
}
});
});
}
/* Wire up UI events (sliders, etc.) */ /* Wire up UI events (sliders, etc.) */
function wireUIEvents() { function wireUIEvents() {
@ -1107,8 +1154,23 @@ function init() {
attachTopLevelTabLogic(document.getElementById("controlPanel")); attachTopLevelTabLogic(document.getElementById("controlPanel"));
attachRewardSubTabLogic(document.getElementById("controlPanel")); attachRewardSubTabLogic(document.getElementById("controlPanel"));
attachSnakesSubTabLogic(document.getElementById("controlPanel"));
attachWorldSubTabLogic(document.getElementById("controlPanel"));
attachTopLevelTabLogic(document.getElementById("timeAndLeaderboardPanel")); attachTopLevelTabLogic(document.getElementById("timeAndLeaderboardPanel"));
wireUIEvents(); wireUIEvents();
// Wire up the help button to open the modal
const helpBtn = document.getElementById('helpButton');
const helpModal = document.getElementById('helpModal');
const helpClose = document.getElementById('helpClose');
if (helpBtn && helpModal && helpClose) {
helpBtn.onclick = () => {
helpModal.style.display = 'flex';
};
helpClose.onclick = () => {
helpModal.style.display = 'none';
};
}
} }
init(); init();

View File

@ -10,11 +10,24 @@ html, body {
color: #FFF; color: #FFF;
} }
/* Canvas covers the entire window */ /* Wrap the canvas so we can scale the entire simulation if desired */
#simCanvasWrapper {
width: 100%;
height: 100%;
/* Default scale for desktop */
transform: scale(1.0);
transform-origin: center center;
}
/* On mobile (below 1000px), scale differently. Adjust as desired. */
@media (max-width: 1000px) {
#simCanvasWrapper {
transform: scale(0.85); /* Or scale(1.2) if you want larger on mobile */
}
}
/* The canvas inside the wrapper */
canvas { canvas {
position: absolute;
top: 0;
left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
display: block; display: block;
@ -34,7 +47,7 @@ canvas {
background: linear-gradient(135deg, rgba(20,0,40,0.8), rgba(0,0,0,0.6)); 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 */ /* widths: make both 420px for desktop */
.control-panel { .control-panel {
width: 420px; width: 420px;
left: 50%; left: 50%;
@ -165,6 +178,60 @@ canvas {
display: block; display: block;
} }
/* Sub-tabs for Snakes */
.snakes-subtabs {
display: flex;
margin-bottom: 10px;
}
.snakes-subtabs .tabBtn {
flex: 1;
margin: 0 2px;
background: #444;
border: none;
color: #FFF;
cursor: pointer;
padding: 4px 8px;
font-weight: bold;
border-radius: 4px;
}
.snakes-subtabs .tabBtn.active {
background: linear-gradient(90deg, #8000ff 0%, #ff00ff 100%);
box-shadow: 0 0 5px rgba(255,0,255,0.4);
}
.snakes-sub-content {
display: none;
}
.snakes-sub-content.active {
display: block;
}
/* Sub-tabs for 'World' (Environment + Evolution) */
.world-subtabs {
display: flex;
margin-bottom: 10px;
}
.world-subtabs .tabBtn {
flex: 1;
margin: 0 2px;
background: #444;
border: none;
color: #FFF;
cursor: pointer;
padding: 4px 8px;
font-weight: bold;
border-radius: 4px;
}
.world-subtabs .tabBtn.active {
background: linear-gradient(90deg, #8000ff 0%, #ff00ff 100%);
box-shadow: 0 0 5px rgba(255,0,255,0.4);
}
.world-sub-content {
display: none;
}
.world-sub-content.active {
display: block;
}
/* .tab-content is used for top-level content areas */ /* .tab-content is used for top-level content areas */
.tab-content { .tab-content {
display: none; /* hidden by default */ display: none; /* hidden by default */
@ -219,21 +286,17 @@ canvas {
animation: sinewave 1s infinite; animation: sinewave 1s infinite;
} }
/* Center the "?" help button on the right edge */ /* Help Button style (no longer absolutely positioned) */
.help-button { .help-button {
position: absolute; margin-left: 8px;
top: 50%;
right: 10px;
transform: translateY(-50%);
z-index: 3;
background: #444; background: #444;
border: 1px solid #555; border: 1px solid #555;
color: #FFF; color: #FFF;
font-size: 16px; font-size: 14px;
padding: 4px 8px; padding: 2px 6px;
cursor: pointer; cursor: pointer;
border-radius: 50%; border-radius: 4px;
box-shadow: 0 0 8px rgba(255, 0, 255, 0.5); box-shadow: 0 0 8px rgba(255,0,255,0.5);
} }
.help-button:hover { .help-button:hover {
background: #666; background: #666;
@ -295,6 +358,7 @@ canvas {
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
} }
/* Stack the leaderboards vertically in mobile to avoid overflow */ /* Stack the leaderboards vertically in mobile to avoid overflow */
.leaderboardColumn { .leaderboardColumn {
width: 100% !important; width: 100% !important;