seeded random

all runs are now seeded
seed defaults to the last 8 digits of UTC time in milliseconds
set a custom randomization seed in settings
seed controls:
    at start - boss list, mob type list, level list, horizontal flip
    during run - tech, gun, field choices, some custom level randomization
doesn't control: mob spawns, mob size, some minor level differences, specific level boss choices, ammo reward values, specific tech effects

bug fix with ground state
This commit is contained in:
landgreen
2022-02-06 08:52:39 -08:00
parent 496cc83878
commit 775f45b863
10 changed files with 105 additions and 48 deletions

View File

@@ -1,4 +1,39 @@
"use strict";
//convert text into numbers for seed
Math.hash = s => { for (var i = 0, h = 9; i < s.length;) h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9); return h ^ h >>> 9 }
// const date1 = new Date()
// Math.seed = date1.getUTCDate() * date1.getUTCFullYear(); // daily seed, day + year
// Math.seed = Date.now() //random every time: just the time in seconds UTC
Math.seed = Math.floor(Date.now() % 100000000) //random every time: just the time in seconds UTC
Math.seededRandom = function(min = 0, max = 1) { // in order to work 'Math.seed' must NOT be undefined
Math.seed = (Math.seed * 9301 + 49297) % 233280;
return min + Math.seed / 233280 * (max - min);
}
document.getElementById("seed").placeholder = Math.seed //display seed in settings
//Math.seed is set to document.getElementById("seed").value in level.populate level at the start of runs
// console.log(Math.seed)
function shuffle(array) {
var currentIndex = array.length,
temporaryValue,
randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
// randomIndex = Math.floor(Math.random() * currentIndex);
randomIndex = Math.floor(Math.seededRandom(0, currentIndex)) //Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
//collision groups
// cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet | cat.mobShield | cat.phased
const cat = {
@@ -36,23 +71,6 @@ const color = { //light
// map: "#444",
// }
function shuffle(array) {
var currentIndex = array.length,
temporaryValue,
randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
// shrink power up selection menu
// if (screen.height < 800) {
// document.getElementById("choose-grid").style.fontSize = "1em"; //1.3em is normal

View File

@@ -16,7 +16,6 @@ const level = {
if (level.levelsCleared === 0) { //this code only runs on the first level
// m.immuneCycle = Infinity //you can't take damage
// localSettings.levelsClearedLastGame = 10
// spawn.setSpawnList();spawn.setSpawnList();
// level.difficultyIncrease(30) //30 is near max on hard //60 is near max on why
// simulation.isHorizontalFlipped = true
// m.setField("standing wave")
@@ -222,10 +221,12 @@ const level = {
}
},
populateLevels() {
if (document.getElementById("seed").value) Math.seed = Math.hash(document.getElementById("seed").value) //update randomizer seed in case the player changed it
if (simulation.isTraining) {
level.levels = level.trainingLevels.slice(0) //copy array, not by just by assignment
} else {
simulation.isHorizontalFlipped = (Math.random() < 0.5) ? true : false //if true, some maps are flipped horizontally
simulation.isHorizontalFlipped = (Math.seededRandom() < 0.5) ? true : false //if true, some maps are flipped horizontally
level.levels = level.playableLevels.slice(0) //copy array, not by just by assignment
if (simulation.isCommunityMaps) {
// level.levels.push(level.communityLevels)
@@ -235,8 +236,9 @@ const level = {
} else {
level.levels = shuffle(level.levels); //shuffles order of maps
}
level.levels.splice(Math.floor(level.levels.length * (0.4 + 0.6 * Math.random())), 0, "reservoir"); //add level to the back half of the randomized levels list
level.levels.splice(Math.floor(level.levels.length * (0.4 + 0.6 * Math.random())), 0, "reactor"); //add level to the back half of the randomized levels list
// level.levels.splice(Math.floor(level.levels.length * (0.4 + 0.6 * Math.random())), 0, "reservoir"); //add level to the back half of the randomized levels list
level.levels.splice(Math.floor(Math.seededRandom((level.levels.length) * 0.4, level.levels.length)), 0, "reservoir"); //add level to the back half of the randomized levels list
level.levels.splice(Math.floor(Math.seededRandom((level.levels.length) * 0.4, level.levels.length)), 0, "reactor"); //add level to the back half of the randomized levels list
level.levels.splice(0, 2); //remove 2 levels from the start of the array
if (!build.isExperimentSelection || (build.hasExperimentalMode && !simulation.isCheating)) { //experimental mode is endless, unless you only have an experiment Tech
level.levels.unshift("intro"); //add level to the start of the randomized levels list
@@ -244,6 +246,9 @@ const level = {
level.levels.push("final"); //add level to the end of the randomized levels list
}
}
//set seeded random lists of mobs and bosses
for (let i = 0; i < level.levels.length; i++) spawn.mobTypeSpawnOrder.push(spawn.fullPickList[Math.floor(Math.seededRandom(0, spawn.fullPickList.length))])
for (let i = 0; i < level.levels.length * 2; i++) spawn.bossTypeSpawnOrder.push(spawn.randomBossList[Math.floor(Math.seededRandom(0, spawn.randomBossList.length))])
},
flipHorizontal() {
const flipX = (who) => {
@@ -2508,8 +2513,6 @@ const level = {
level.exit.y = -430;
// level.difficultyIncrease(14); //hard mode level 7
spawn.setSpawnList();
spawn.setSpawnList();
level.defaultZoom = 1500
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = color.background //"#ddd";
@@ -3232,8 +3235,6 @@ const level = {
// level.difficultyIncrease(30) //30 is near max on hard //60 is near max on why
// m.immuneCycle = Infinity //you can't take damage
// spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns
// spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns
simulation.isHorizontalFlipped = true
if (simulation.isHorizontalFlipped) { //flip the map horizontally

View File

@@ -961,7 +961,7 @@ const m = {
}
},
setMaxEnergy() {
m.maxEnergy = (tech.isMaxEnergyTech ? 0.5 : 1) + tech.bonusEnergy + tech.healMaxEnergyBonus + tech.harmonicEnergy
m.maxEnergy = (tech.isMaxEnergyTech ? 0.5 : 1) + tech.bonusEnergy + tech.healMaxEnergyBonus + tech.harmonicEnergy + 2 * tech.isGroundState
simulation.makeTextLog(`<span class='color-var'>m</span>.<span class='color-f'>maxEnergy</span> <span class='color-symbol'>=</span> ${(m.maxEnergy.toFixed(2))}`)
},
fieldMeterColor: "#0cf",

View File

@@ -566,7 +566,8 @@ const powerUps = {
}
}
if (options.length > 0) {
return options[Math.floor(Math.random() * options.length)]
// return options[Math.floor(Math.random() * options.length)]
return options[Math.floor(Math.seededRandom(0, options.length))]
}
},
choiceLog: [], //records all previous choice options
@@ -650,7 +651,8 @@ const powerUps = {
}
if (options.length > 0) {
const choose = options[Math.floor(Math.random() * options.length)]
// const choose = options[Math.floor(Math.random() * options.length)]
const choose = options[Math.floor(Math.seededRandom(0, options.length))]
const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : "";
if (tech.tech[choose].isFieldTech) {
@@ -813,7 +815,10 @@ const powerUps = {
}
}
if (options.length > 0) {
return options[Math.floor(Math.random() * options.length)]
// console.log(`random: ${Math.seededRandom(0, options.length)}`)
return options[Math.floor(Math.seededRandom(0, options.length))]
// return options[Math.floor(Math.random() * options.length)]
}
},
choiceLog: [], //records all previous choice options

View File

@@ -658,7 +658,9 @@ const simulation = {
b.setFireMethod()
b.setFireCD();
// simulation.updateTechHUD();
powerUps.tech.choiceLog = []
powerUps.tech.choiceLog = [];
powerUps.gun.choiceLog = [];
powerUps.field.choiceLog = [];
powerUps.totalPowerUps = 0;
powerUps.research.count = 0;
m.setFillColors();

View File

@@ -2,12 +2,19 @@
const spawn = {
nonCollideBossList: ["cellBossCulture", "bomberBoss", "powerUpBoss", "orbitalBoss", "spawnerBossCulture", "growBossCulture"],
// other bosses: suckerBoss, laserBoss, tetherBoss, //these need a particular level to work so they are not included in the random pool
randomLevelBoss(x, y, options = [
"shieldingBoss", "orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss",
randomBossList: ["shieldingBoss", "orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss",
"powerUpBoss", "powerUpBossBaby", "snakeBoss", "streamBoss", "pulsarBoss", "spawnerBossCulture", "grenadierBoss", "growBossCulture", "blinkBoss",
"snakeSpitBoss", "laserBombingBoss", "blockBoss", "revolutionBoss", "mantisBoss", "slashBoss"
]) {
spawn[options[Math.floor(Math.random() * options.length)]](x, y)
],
bossTypeSpawnOrder: [], //preset list of boss names calculated at the start of a run by the randomSeed
bossTypeSpawnIndex: 0, //increases as the boss type cycles
randomLevelBoss(x, y, options = []) {
if (options.length === 0) {
const boss = spawn.bossTypeSpawnOrder[spawn.bossTypeSpawnIndex++ % spawn.bossTypeSpawnOrder.length]
spawn[boss](x, y)
} else {
spawn[options[Math.floor(Math.random() * options.length)]](x, y)
}
},
pickList: ["starter", "starter"],
fullPickList: [
@@ -33,11 +40,17 @@ const spawn = {
"spawner",
"ghoster",
],
mobTypeSpawnOrder: [], //preset list of mob names calculated at the start of a run by the randomSeed
mobTypeSpawnIndex: 0, //increases as the mob type cycles
allowedGroupList: ["spinner", "striker", "springer", "laser", "focuser", "beamer", "exploder", "spawner", "shooter", "launcher", "launcherOne", "stabber", "sniper", "pulsar", "grenadier", "slasher"],
setSpawnList() { //this is run at the start of each new level to determine the possible mobs for the level
//each level has 2 mobs: one new mob and one from the last level
spawn.pickList.splice(0, 1);
spawn.pickList.push(spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]);
const push = spawn.mobTypeSpawnOrder[spawn.mobTypeSpawnIndex++ % spawn.mobTypeSpawnOrder.length]
spawn.pickList.push(push);
// if (spawn.mobTypeSpawnIndex > spawn.mobTypeSpawnOrder.length) spawn.mobTypeSpawnIndex = 0
//each level has 2 mobs: one new mob and one from the last level
// spawn.pickList.splice(0, 1);
// spawn.pickList.push(spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]);
},
spawnChance(chance) {
return Math.random() < chance + 0.07 * simulation.difficulty && mob.length < -1 + 16 * Math.log10(simulation.difficulty + 1)

View File

@@ -3067,7 +3067,7 @@ const tech = {
},
{
name: "particle collider",
description: `<strong>clicking</strong> <strong class='color-m'>tech</strong> while <strong>paused</strong> <strong>ejects</strong> them<br><em><strong>3%</strong> chance to convert your tech into <strong class='color-f'>energy</strong></em>`,
description: `<strong>clicking</strong> <strong class='color-m'>tech</strong> while <strong>paused</strong> <strong>ejects</strong> them<br><em><strong>3%</strong> chance to convert that tech into <strong class='color-f'>energy</strong></em>`,
maxCount: 1,
count: 0,
frequency: 1,