Factor out generators

This commit is contained in:
Tangent Wantwight 2024-01-13 12:10:37 -05:00
parent a7e0145736
commit 4fc076d80c
2 changed files with 101 additions and 49 deletions

74
island/generators.ts Normal file
View File

@ -0,0 +1,74 @@
import { BEACH, ICECAP, LIGHT_FOREST, MOUNTAIN, WATER } from "./data";
import { IslandGrid } from "./grid";
export type IsDone = boolean;
export type LobeGenerator = () => IsDone;
export type LobeGeneratorConstructor = (
islands: IslandGrid,
basePos: number
) => LobeGenerator;
/** form mountain with icecap */
export const BIG_MOUNTAIN: LobeGeneratorConstructor =
(islands: IslandGrid, basePos: number) => () => {
const mountainTiles = islands.floodSearch(
basePos,
(tile) => tile > MOUNTAIN
);
islands.dropWithin(mountainTiles);
return mountainTiles.some((pos) => islands.data[pos] == ICECAP);
};
/** form low-lying beach */
export const BEACH_LOBE: LobeGeneratorConstructor =
(islands: IslandGrid, basePos: number) => () => {
const islandTiles = islands.floodSearch(basePos, (tile) => tile > WATER);
const shoreTiles = islandTiles.filter((pos) => islands.data[pos] == WATER);
islands.dropWithin(shoreTiles);
return true;
};
/** form forested zone */
export const FOREST_LOBE: LobeGeneratorConstructor =
(islands: IslandGrid, basePos: number) => () => {
const islandTiles = islands.floodSearch(basePos, (tile) => tile > WATER);
// grow shore
const shoreTiles = islandTiles.filter((pos) => islands.data[pos] == WATER);
islands.dropWithin(shoreTiles);
// seed beach
const beachTiles = islandTiles.filter((pos) => islands.data[pos] == BEACH);
islands.dropWithin(beachTiles);
// expand forest
const forestLobe = islands.floodSearch(basePos, (tile) => tile > BEACH);
const forestTiles = forestLobe.filter(
(pos) => islands.data[pos] == LIGHT_FOREST
);
islands.dropWithin(forestTiles);
islands.dropWithin(forestTiles);
islands.dropWithin(forestTiles);
return true;
};
/** form low-lying beach with eroded sections */
export const ERODED_LOBE: LobeGeneratorConstructor =
(islands: IslandGrid, basePos: number) => () => {
const islandTiles = islands.floodSearch(basePos, (tile) => tile > WATER);
const shoreTiles = islandTiles.filter((pos) => islands.data[pos] == WATER);
islands.dropWithin(shoreTiles);
// erode
if (islandTiles.length > 1) {
const erodePos = islandTiles[islands.rng() % islandTiles.length];
islands.data[erodePos] = Math.max(islands.data[erodePos] - 1, WATER);
}
return true;
};

View File

@ -1,13 +1,17 @@
import { Prng, mulberry32 } from "../lib/prng";
import { BEACH, ICECAP, LIGHT_FOREST, MOUNTAIN, WATER } from "./data";
import {
BEACH_LOBE,
BIG_MOUNTAIN,
FOREST_LOBE,
LobeGenerator,
} from "./generators";
export class IslandGrid {
data: number[];
rng: Prng;
basePos: number;
lobePos1: number;
lobePos2: number;
generators: LobeGenerator[] = [];
done = false;
@ -15,14 +19,24 @@ export class IslandGrid {
this.data = Array(width * height).fill(0);
this.rng = mulberry32(seed);
this.basePos = this.data.length >> 1;
this.lobePos1 = this.xy(
(width >> 1) + (this.rng() % 48) - 24,
(height >> 1) + (this.rng() % 48) - 24
this.generators.push(BIG_MOUNTAIN(this, this.data.length >> 1));
this.generators.push(
FOREST_LOBE(
this,
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
this.generators.push(
BEACH_LOBE(
this,
this.xy(
(width >> 1) + (this.rng() % 48) - 24,
(height >> 1) + (this.rng() % 48) - 24
)
)
);
}
@ -112,44 +126,8 @@ export class IslandGrid {
}
public step() {
const lowlandTiles1 = this.floodSearch(
this.lobePos1,
(tile) => tile > WATER
);
const lowlandTiles2 = this.floodSearch(
this.lobePos2,
(tile) => tile > WATER
);
// grow shore
const shoreTiles1 = lowlandTiles1.filter((pos) => this.data[pos] == WATER);
this.dropWithin(shoreTiles1);
const shoreTiles2 = lowlandTiles2.filter((pos) => this.data[pos] == WATER);
this.dropWithin(shoreTiles2);
// seed beach
const beachTiles = lowlandTiles1.filter((pos) => this.data[pos] == BEACH);
this.dropWithin(beachTiles);
// expand forest
const forestLobe = this.floodSearch(this.lobePos1, (tile) => tile > BEACH);
const forestTiles = forestLobe.filter(
(pos) => this.data[pos] == LIGHT_FOREST
);
this.dropWithin(forestTiles);
this.dropWithin(forestTiles);
this.dropWithin(forestTiles);
// form mountain
const mountainTiles = this.floodSearch(
this.basePos,
(tile) => tile > MOUNTAIN
);
this.dropWithin(mountainTiles); // GENERATOR
if (lowlandTiles2.length > 1) {
const erodePos = lowlandTiles2[this.rng() % lowlandTiles2.length];
this.data[erodePos] = Math.max(this.data[erodePos] - 1, 0);
}
this.done = this.generators
.map((generator) => generator())
.every((done) => done);
}
}