Move generator functions into proper class
This commit is contained in:
parent
5cc40ef764
commit
d755f9e72e
1 changed files with 106 additions and 94 deletions
200
island.ts
200
island.ts
|
@ -5,6 +5,8 @@ const BLOWUP = 4;
|
|||
const WIDTH = 240;
|
||||
const HEIGHT = 135;
|
||||
|
||||
const DEFAULT_SEED = 128;
|
||||
|
||||
type Lookup2d = (x: number, y: number) => number;
|
||||
function dim(width: number, height: number): Lookup2d {
|
||||
return function xy(x: number, y: number) {
|
||||
|
@ -21,10 +23,26 @@ class IslandGrid {
|
|||
|
||||
xy: Lookup2d;
|
||||
|
||||
basePos: number;
|
||||
lobePos1: number;
|
||||
lobePos2: number;
|
||||
|
||||
done = false;
|
||||
|
||||
constructor(public width: number, public height: number, seed: number) {
|
||||
this.data = Array(width * height).fill(0);
|
||||
this.rng = mulberry32(seed);
|
||||
this.xy = dim(width, height);
|
||||
|
||||
this.basePos = this.data.length >> 1;
|
||||
this.lobePos1 = this.xy(
|
||||
(WIDTH >> 1) + (this.rng() % 48) - 24,
|
||||
(HEIGHT >> 1) + (this.rng() % 48) - 24
|
||||
);
|
||||
this.lobePos2 = this.xy(
|
||||
(WIDTH >> 1) + (this.rng() % 48) - 24,
|
||||
(HEIGHT >> 1) + (this.rng() % 48) - 24
|
||||
);
|
||||
}
|
||||
|
||||
public get(x: number, y: number): number {
|
||||
|
@ -67,6 +85,80 @@ class IslandGrid {
|
|||
|
||||
return hitPositions;
|
||||
}
|
||||
|
||||
public drop(pos: number): void {
|
||||
const lowerNeighbors: number[] = [];
|
||||
|
||||
const check = (adjPos: number) => {
|
||||
if (this.data[adjPos] < this.data[pos]) {
|
||||
lowerNeighbors.push(adjPos);
|
||||
}
|
||||
};
|
||||
|
||||
// try to roll in cardinal directions
|
||||
|
||||
check((pos - this.width) % this.data.length);
|
||||
check((pos - 1) % this.data.length);
|
||||
check((pos + 1) % this.data.length);
|
||||
check((pos + this.width) % this.data.length);
|
||||
|
||||
if (lowerNeighbors.length > 0) {
|
||||
const downhill = lowerNeighbors[this.rng() % lowerNeighbors.length];
|
||||
return this.drop(downhill);
|
||||
}
|
||||
|
||||
// try to roll in diagonal directions
|
||||
|
||||
check((pos - this.width - 1) % this.data.length);
|
||||
check((pos - this.width + 1) % this.data.length);
|
||||
check((pos + this.width - 1) % this.data.length);
|
||||
check((pos + this.width + 1) % this.data.length);
|
||||
|
||||
if (lowerNeighbors.length > 0) {
|
||||
const downhill = lowerNeighbors[this.rng() % lowerNeighbors.length];
|
||||
return this.drop(downhill);
|
||||
}
|
||||
|
||||
// flat, increase elevation
|
||||
const newValue = ++this.data[pos];
|
||||
if (newValue == 9) {
|
||||
this.done = true;
|
||||
}
|
||||
}
|
||||
|
||||
public dropWithin(tiles: number[]) {
|
||||
if (tiles.length > 0) {
|
||||
this.drop(tiles[this.rng() % tiles.length]);
|
||||
}
|
||||
}
|
||||
|
||||
public step() {
|
||||
const lowlandTiles1 = this.floodSearch(this.lobePos1, (tile) => tile > 0);
|
||||
const lowlandTiles2 = this.floodSearch(this.lobePos2, (tile) => tile > 0);
|
||||
|
||||
// grow shore
|
||||
const shoreTiles1 = lowlandTiles1.filter((pos) => this.data[pos] == 0);
|
||||
this.dropWithin(shoreTiles1);
|
||||
const shoreTiles2 = lowlandTiles2.filter((pos) => this.data[pos] == 0);
|
||||
this.dropWithin(shoreTiles2);
|
||||
|
||||
// seed beach
|
||||
const beachTiles = lowlandTiles1.filter((pos) => this.data[pos] == 1);
|
||||
this.dropWithin(beachTiles);
|
||||
|
||||
// expand forest
|
||||
const forestTiles = lowlandTiles1.filter((pos) => this.data[pos] == 2);
|
||||
this.dropWithin(forestTiles);
|
||||
this.dropWithin(forestTiles);
|
||||
this.dropWithin(forestTiles);
|
||||
|
||||
// form mountain
|
||||
const mountainTiles = this.floodSearch(this.basePos, (tile) => tile > 4);
|
||||
this.dropWithin(mountainTiles); // GENERATOR
|
||||
|
||||
// const erodePos = islandTiles[islands.rng() % islandTiles.length];
|
||||
// islands.data[erodePos] = Math.max(islands.data[erodePos] - 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function renderIslands(islands: IslandGrid, cx: CanvasRenderingContext2D) {
|
||||
|
@ -109,103 +201,13 @@ export function IslandApplet() {
|
|||
});
|
||||
cx.scale(BLOWUP, BLOWUP);
|
||||
|
||||
const seedInput = h("input", { type: "number", valueAsNumber: 128 });
|
||||
const seedInput = h("input", { type: "number", valueAsNumber: DEFAULT_SEED });
|
||||
const seedLabel = h("label", {}, "Seed:", seedInput);
|
||||
|
||||
// STATE
|
||||
|
||||
let timerId: number;
|
||||
let islands = new IslandGrid(WIDTH, HEIGHT, 128);
|
||||
|
||||
// GENERATOR
|
||||
|
||||
const len = islands.data.length;
|
||||
const basePos = len >> 1;
|
||||
const lobePos1 = islands.xy(
|
||||
(WIDTH >> 1) + (islands.rng() % 48) - 24,
|
||||
(HEIGHT >> 1) + (islands.rng() % 48) - 24
|
||||
);
|
||||
const lobePos2 = islands.xy(
|
||||
(WIDTH >> 1) + (islands.rng() % 48) - 24,
|
||||
(HEIGHT >> 1) + (islands.rng() % 48) - 24
|
||||
);
|
||||
const width = islands.width;
|
||||
|
||||
function drop(pos: number) {
|
||||
const lowerNeighbors: number[] = [];
|
||||
|
||||
function check(adjPos: number) {
|
||||
if (islands.data[adjPos] < islands.data[pos]) {
|
||||
lowerNeighbors.push(adjPos);
|
||||
}
|
||||
}
|
||||
|
||||
// try to roll in cardinal directions
|
||||
|
||||
check((pos - width) % len);
|
||||
check((pos - 1) % len);
|
||||
check((pos + 1) % len);
|
||||
check((pos + width) % len);
|
||||
|
||||
if (lowerNeighbors.length > 0) {
|
||||
const downhill = lowerNeighbors[islands.rng() % lowerNeighbors.length];
|
||||
return drop(downhill);
|
||||
}
|
||||
|
||||
// try to roll in diagonal directions
|
||||
|
||||
check((pos - width - 1) % len);
|
||||
check((pos - width + 1) % len);
|
||||
check((pos + width - 1) % len);
|
||||
check((pos + width + 1) % len);
|
||||
|
||||
if (lowerNeighbors.length > 0) {
|
||||
const downhill = lowerNeighbors[islands.rng() % lowerNeighbors.length];
|
||||
return drop(downhill);
|
||||
}
|
||||
|
||||
// flat, increase elevation
|
||||
const newValue = ++islands.data[pos];
|
||||
if (newValue == 9) {
|
||||
clearInterval(timerId);
|
||||
}
|
||||
}
|
||||
|
||||
function dropWithin(tiles: number[]) {
|
||||
if (tiles.length > 0) {
|
||||
drop(tiles[islands.rng() % tiles.length]);
|
||||
}
|
||||
}
|
||||
|
||||
function tick() {
|
||||
const lowlandTiles1 = islands.floodSearch(lobePos1, (tile) => tile > 0);
|
||||
const lowlandTiles2 = islands.floodSearch(lobePos2, (tile) => tile > 0);
|
||||
|
||||
// grow shore
|
||||
const shoreTiles1 = lowlandTiles1.filter((pos) => islands.data[pos] == 0);
|
||||
dropWithin(shoreTiles1);
|
||||
const shoreTiles2 = lowlandTiles2.filter((pos) => islands.data[pos] == 0);
|
||||
dropWithin(shoreTiles2);
|
||||
|
||||
// seed beach
|
||||
const beachTiles = lowlandTiles1.filter((pos) => islands.data[pos] == 1);
|
||||
dropWithin(beachTiles);
|
||||
|
||||
// expand forest
|
||||
const forestTiles = lowlandTiles1.filter((pos) => islands.data[pos] == 2);
|
||||
dropWithin(forestTiles);
|
||||
dropWithin(forestTiles);
|
||||
dropWithin(forestTiles);
|
||||
|
||||
// form mountain
|
||||
const mountainTiles = islands.floodSearch(basePos, (tile) => tile > 4);
|
||||
dropWithin(mountainTiles);
|
||||
|
||||
// const erodePos = islandTiles[islands.rng() % islandTiles.length];
|
||||
// islands.data[erodePos] = Math.max(islands.data[erodePos] - 1, 0);
|
||||
|
||||
renderIslands(islands, cx);
|
||||
}
|
||||
let islands = new IslandGrid(WIDTH, HEIGHT, DEFAULT_SEED);
|
||||
|
||||
// CONTROLS
|
||||
|
||||
|
@ -214,9 +216,19 @@ export function IslandApplet() {
|
|||
{
|
||||
onclick: () => {
|
||||
clearInterval(timerId);
|
||||
console.log(seedInput.valueAsNumber);
|
||||
|
||||
islands = new IslandGrid(WIDTH, HEIGHT, seedInput.valueAsNumber);
|
||||
timerId = setInterval(tick, 1000 / 30);
|
||||
|
||||
timerId = setInterval(function tick() {
|
||||
islands.step();
|
||||
islands.step();
|
||||
islands.step();
|
||||
if (islands.done) {
|
||||
clearInterval(timerId);
|
||||
}
|
||||
|
||||
renderIslands(islands, cx);
|
||||
}, 1000 / 30);
|
||||
},
|
||||
},
|
||||
"Generate"
|
||||
|
|
Loading…
Add table
Reference in a new issue