const width = 2160; const height = 1080; const BIASES = { 0x000000: 1, 0x64ff50: 0.5, 0xc8ff50: 0.45, 0x46aafa: 0.4, 0x0078ff: 0.35, 0x0000ff: 0.3, 0xf5a500: 0.15, 0xff0000: 0.1, 0x666666: 0.01, 0xb2b2b2: 0.02 }; function validXY(x, y) { if (x < 0 || x >= width) return false; if (y < 0 || y >= height) return false; return true; } function getRGBA(data, i, out) { for (let j = i * 4; j < i * 4 + 4; j++) { out[j - i*4] = data[j]; } return out; } function setRGBA(data, i, input) { for (let j = i * 4; j < i * 4 + 4; j++) { data[j] = input[j - i * 4]; } } function isLand(data) { if (data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255) return false; if (data[0] == 0 && data[1] == 0 && data[2] == 0 && data[3] == 255) return false; return true; } function Game(baseMap) { this.img = new Image(); this.baseMap = baseMap; this.colors = {}; this.aggression = {}; } Game.prototype.main = function() { var {biomes, ctx, colonized} = this; var dat = ctx.getImageData(0,0,width,height); var data = dat.data; var out = new Uint8Array(4); for (let k = 0; k < width*height; k++) { let i = k; if (!colonized[i]) continue; var x = i % width; var y = Math.floor(i / width); let rgba = getRGBA(biomes.data,i,out); if (isLand(rgba)) setRGBA(data,i,this.colors[colonized[i]]); let weight = 10; let broken = false; let i2 = i; for (let dx = -1; dx <= 1; dx++) { for (let dy = -1; dy <= 1; dy++ ) { weight--; let x2 = x + dx; let y2 = y + dy; let i = x2 + y2 * width; if (!validXY(x2,y2)) continue; let rgba = getRGBA(biomes.data,i,out); let rgbaH = rgba[0] * 0x10000 + rgba[1] * 0x100 + rgba[2]; let bias = ((rgbaH in BIASES) ? BIASES[rgbaH] : 0.2) / weight; if (colonized[y2 * width + x2] != 0) bias /= this.aggression[colonized[i]]; if (Math.random() > bias) { continue; } if (colonized[y2 * width + x2] == colonized[i2]) { continue; } colonized[y2 * width + x2] = colonized[i2]; broken = true; break; } if (broken) break; } } ctx.putImageData(dat,0,0,0,0,width,height); } Game.prototype.map = function() { var {biomes, ctx} = this; var dat = ctx.getImageData(0,0,width,height); var data = dat.data; var out = new Uint8Array(4); for (let i = 0; i < biomes.data.length; i++) { let rgba = getRGBA(biomes.data,i, out); if (!isLand(rgba,i)) { setRGBA(data,i,[255,255,255,255]) } else { setRGBA(data,i,[0,0,0,255]); } } ctx.putImageData(dat,0,0,0,0,width,height); } Game.prototype.place = function() { var {biomes,ctx,colonized} = this; var out = new Uint8Array(4); let i = 0; for (let count = 1; count < 101; count++) { while (true) { i = Math.floor(Math.random() * biomes.data.length / 4); let rgba = getRGBA(biomes.data,i,out); if (isLand(rgba)) break; } colonized[i] = count; this.aggression[count] = Math.random(); this.colors[count] = [Math.floor(Math.random() * 255),Math.floor(Math.random() * 255),Math.floor(Math.random() * 255),255]; } } Game.prototype.loop = function() { let that = this; setInterval(function() { that.main() } ,1000 / 60); } Game.prototype.startGame = function() { var canvas = document.querySelector("#canvas"); var ctx = canvas.getContext("2d",{ willReadFrequently: true }); this.ctx = ctx; ctx.imageSmoothingEnabled = false; ctx.drawImage(this.img,0, 0,width,height); var dat = ctx.getImageData(0,0,width,height); this.biomes = dat; this.colonized = new Uint8Array(width*height); this.map(); this.place(); this.loop(); } Game.prototype.init = function() { let that = this; this.img.src = this.baseMap; this.img.onload = function() { that.startGame() }; } new Game("base.png").init();