Hexagon explorer
An unfinished experiment in interactive map generation. Each hexagon color affects the likelihood of nearby hexagons being same/different -- similar to there being a deck of cards contributed by the surroundings. The idea was that you could focus on exploring the areas you like, thus developing them further than the areas you dislike.
Legend:
- Green: grass. Contributes 5 grass cards and 1 hill card to adjacent explorations.
- Yellow: hill. Contributes 4 hill, 1 grass, and 1 mountain card to adjacent explorations.
- Blue: mountain. Contributes 2 grass, 2 hill, and 2 mountain cards to adjacent explorations.
Ideally, would introduce longer-ranged card contributions—e.g. an "outpost" card which contributes 1 "monster den" card to all hexagons exactly 3 tiles away. Also, conditional contributions, such as a road tile which contributes "8" roads to nearby explorations if it's adjacent to less than 2 roads. And finally, sub-tiles/cards, such as a "sheep" card which can randomly appear on top of grass tiles or a "townfolk" card which appears randomly in towns.
Combining all three would be a way to make questlines: e.g. the "terrorized townfolk" sub-card of "city" tiles contributes 1 "goblin king" sub-card to all "mountain" tiles, as long as there hasn't been a "goblin king" already drawn on the map. Then, the "goblin king" card contributes 1 "heroic adventurer" sub-sub-card to any "tavern" sub-cards of "road" tiles, and so on.
let decks = {[[0,0]]: {hill: 0, grass: 1, mountain: 0}}
let tiles = {}
let colors = {
hill: [255, 255, 50],
mountain: [50, 50, 255],
grass: [0, 255, 0],
}
let spreads = {
hill: {hill: 4, grass: 1, mountain: 1},
mountain: {hill: 2, grass: 2, mountain: 2},
grass: {hill: 1, grass: 5, mountain: 0},
}
function setup() {
createCanvas(600, 600);
}
let offsetPx = [-300, -300]
let sizePx = 20
function draw() {
background(220);
let corners = [
px2hex(vadd(offsetPx, [0, 0]), sizePx),
px2hex(vadd(offsetPx, [width, 0]), sizePx),
px2hex(vadd(offsetPx, [0, height]), sizePx),
px2hex(vadd(offsetPx, [width, height]), sizePx),
]
let minQr = vfloor(corners.reduce(vmin))
let maxQr = vceil(corners.reduce(vmax))
for (let q = minQr[0]; q <= maxQr[0]; q ++) {
for (let r = minQr[1]; r <= maxQr[1]; r ++) {
let [x, y] = vsub(hex2px([q, r], sizePx), offsetPx)
let s = 1
if (tiles[[q, r]]) {
fill(...colors[tiles[[q, r]]])
} else if (decks[[q, r]]) {
noFill()
line(x - 3, y, x + 3, y)
line(x, y - 3, x, y + 3)
s = 0.75
} else {
continue
}
beginShape()
vertex(x + SQRT3 / 2 * sizePx * s, y + 1/2 * sizePx * s)
vertex(x + 0 * sizePx * s, y + 1 * sizePx * s)
vertex(x - SQRT3 / 2 * sizePx * s, y + 1/2 * sizePx * s)
vertex(x - SQRT3 / 2 * sizePx * s, y - 1/2 * sizePx * s)
vertex(x + 0 * sizePx * s, y - 1 * sizePx * s)
vertex(x + SQRT3 / 2 * sizePx * s, y - 1/2 * sizePx * s)
endShape(CLOSE)
}
}
let [q, r] = hexround(px2hex(vadd(offsetPx, [mouseX, mouseY]), sizePx))
circle(...vsub(hex2px([q, r], sizePx), offsetPx), 10)
}
function mousePressed() {
let [q, r] = hexround(px2hex(vadd(offsetPx, [mouseX, mouseY]), sizePx))
if (decks[[q, r]]) {
let deck = decks[[q, r]]
delete decks[[q, r]]
// Pick a tile from the deck:
let total = Object.values(deck).reduce((a, b) => a + b)
let rand = random(total)
let picked = Object.entries(deck).find(([_, w]) => (rand -= w) < 0)[0]
tiles[[q, r]] = picked
// Spread tiles around
let toSpread = spreads[picked]
// https://www.redblobgames.com/grids/hexagons/#neighbors
for (let dir of [[1, 0], [1, -1], [0, -1], [-1, 0], [-1, 1], [0, 1]]) {
let pos = vadd([q, r], dir)
if (!tiles[pos]) {
decks[pos] = madd_mod(decks[pos], toSpread)
}
}
}
}
function vadd([a,b], [c,d]) {
return [a + c, b + d]
}
function vsub([a,b], [c,d]) {
return [a - c, b - d]
}
function vmin([a,b], [c,d]) {
return [min(a, c), min(b, d)]
}
function vmax([a,b], [c,d]) {
return [max(a, c), max(b, d)]
}
function vfloor([a, b]) {
return [floor(a), floor(b)]
}
function vceil([a, b]) {
return [ceil(a), ceil(b)]
}
// https://observablehq.com/@jrus/hexround
function hexround([q, r]) {
let qg = Math.round(q), rg = Math.round(r);
q -= qg;
r -= rg;
let dq = Math.round(q + 0.5*r) * (q*q >= r*r);
let dr = Math.round(r + 0.5*q) * (q*q < r*r);
return [qg + dq, rg + dr];
}
function madd_mod(a, b) {
b = b || {}
let r = a || {}
for (let k in b) {
r[k] = (r[k] || 0) + b[k]
}
return r
}
// https://www.redblobgames.com/grids/hexagons/#pixel-to-hex
const SQRT3 = Math.sqrt(3)
function px2hex([x, y], size) {
let q = x * SQRT3 / 3 - y * 1/3
let r = y * 2/3
return [q / size, r / size]
}
function hex2px([q, r], size) {
let x = q * SQRT3 + r * SQRT3/2
let y = r * 3/2
return [x * size, y * size]
}(Originally seen at https://editor.p5js.org/bojidar-bg/sketches/fog9K46F7)
Browse more articles?
← Sine ribbon Experiments tagged p5 (74/85) Pulsing cycloids →
← Reflection fractal Experiments tagged wip (5/5) →|
← Sine ribbon Experiments on this site (74/85) Pulsing cycloids →