|
|
|
@ -737,6 +737,7 @@ class Board { |
|
|
|
|
this.buttons.randomize = addButton(x0, y0, 'Randomize'); |
|
|
|
|
this.buttons.restart = addButton(x0, y0 + ysp, 'Try Again', 'disabled'); |
|
|
|
|
this.buttons.undo = addButton(x0, y0 + ysp*2, 'Undo', 'disabled'); |
|
|
|
|
this.buttons.working = addButton(x0, y0 + ysp*3, 'Working…', 'working'); |
|
|
|
|
|
|
|
|
|
const cfgy0 = 10; |
|
|
|
|
|
|
|
|
@ -844,6 +845,29 @@ class Rng { |
|
|
|
|
arrayChoose(array) { |
|
|
|
|
return array[Math.floor(this.next() * array.length)]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Sneak items in the given order into another array |
|
|
|
|
* |
|
|
|
|
* @param array - the modified array |
|
|
|
|
* @param startKeepout - how many leading lements to leave untouched |
|
|
|
|
* @param endKeepout - how many trailing elements to leave untouched |
|
|
|
|
* @param items - items to sneak in |
|
|
|
|
* @return the modified array |
|
|
|
|
*/ |
|
|
|
|
arraySneak(array, startKeepout, endKeepout, items) { |
|
|
|
|
let positions = []; |
|
|
|
|
for (let i = 0; i < items.length; i++) { |
|
|
|
|
positions.push(startKeepout + this.nextInt(array.length - startKeepout - endKeepout)) |
|
|
|
|
} |
|
|
|
|
positions.sort((a, b) => a - b); |
|
|
|
|
// inject them into the array
|
|
|
|
|
items.forEach((pair, i) => { |
|
|
|
|
array.splice(positions[i] + i, 0, pair); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return array; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class SettingsStorage { |
|
|
|
@ -1044,7 +1068,7 @@ class Game { |
|
|
|
|
|
|
|
|
|
// Defer start to give browser time to render the background
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
this.newGame(args.seed) |
|
|
|
|
this.newGameWithLoader(args.seed) |
|
|
|
|
}, 50); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1407,12 +1431,12 @@ class Game { |
|
|
|
|
installButtonHandlers() { |
|
|
|
|
this.board.buttons.restart.addEventListener('click', () => { |
|
|
|
|
this.info("New Game with the same seed"); |
|
|
|
|
this.newGame(this.rng.seed); |
|
|
|
|
this.newGameWithLoader(this.rng.seed); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.board.buttons.randomize.addEventListener('click', () => { |
|
|
|
|
this.info("New Game with a random seed"); |
|
|
|
|
this.newGame(+new Date); |
|
|
|
|
this.newGameWithLoader(+new Date); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.board.buttons.btnAbout.addEventListener('click', () => { |
|
|
|
@ -1488,6 +1512,15 @@ class Game { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
newGameWithLoader(seed) { |
|
|
|
|
this.board.buttons.working.classList.add('show'); |
|
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
this.newGame(seed); |
|
|
|
|
this.board.buttons.working.classList.remove('show'); |
|
|
|
|
}, 20); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
newGame(seed) { |
|
|
|
|
if (seed !== null) { |
|
|
|
|
this.rng.setSeed(seed); |
|
|
|
@ -1693,40 +1726,23 @@ class BaseOrbPlacer { |
|
|
|
|
toPlace.push(['salt', 'salt']); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// these are always paired like this, and don't support salt
|
|
|
|
|
toPlace = toPlace.concat([ |
|
|
|
|
// shuffle the pairs that have random order (i.e. not metals)
|
|
|
|
|
this.rng.arrayShuffle(toPlace); |
|
|
|
|
|
|
|
|
|
this.rng.arraySneak(toPlace, 3, 0, [ |
|
|
|
|
['mors', 'vitae'], |
|
|
|
|
['mors', 'vitae'], |
|
|
|
|
['mors', 'vitae'], |
|
|
|
|
['mors', 'vitae'], |
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
// shuffle the pairs that have random order (i.e. not metals)
|
|
|
|
|
this.rng.arrayShuffle(toPlace); |
|
|
|
|
|
|
|
|
|
// the order here is actually significant, so let's pay attention...
|
|
|
|
|
const metals = [ |
|
|
|
|
this.rng.arraySneak(toPlace, 4, 0, [ |
|
|
|
|
['lead', 'mercury'], |
|
|
|
|
['tin', 'mercury'], |
|
|
|
|
['iron', 'mercury'], |
|
|
|
|
['copper', 'mercury'], |
|
|
|
|
['silver', 'mercury'], |
|
|
|
|
]; |
|
|
|
|
let mPos = []; |
|
|
|
|
for (let i = 0; i < metals.length; i++) { |
|
|
|
|
let x; |
|
|
|
|
// find a unique position
|
|
|
|
|
do { |
|
|
|
|
x = this.rng.nextInt(toPlace.length + i); |
|
|
|
|
} while (mPos.includes(x)); |
|
|
|
|
mPos.push(x) |
|
|
|
|
} |
|
|
|
|
mPos.sort((a, b) => a - b); |
|
|
|
|
this.trace('Metal positions ', mPos); |
|
|
|
|
// inject them into the array
|
|
|
|
|
metals.forEach((pair, i) => { |
|
|
|
|
toPlace.splice(mPos[i] + i, 0, pair); |
|
|
|
|
}); |
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
this.debug('Placement order (last first):', toPlace); |
|
|
|
|
|
|
|
|
@ -1973,7 +1989,7 @@ class RecursiveOrbPlacer extends BaseOrbPlacer { |
|
|
|
|
|
|
|
|
|
placeOne() { |
|
|
|
|
this.recDepth++; |
|
|
|
|
if (this.recDepth > 2000) { |
|
|
|
|
if (this.recDepth > 1000) { |
|
|
|
|
throw new Error("Too many backtracks"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|