1
0
mirror of https://github.com/osmarks/random-stuff synced 2024-11-09 13:59:55 +00:00
random-stuff/code-guessing/bad-factorio.html

254 lines
9.5 KiB
HTML
Raw Normal View History

2022-02-05 14:45:04 +00:00
<style>
* { box-sizing: border-box;; }
#disp .cell {
width: 32px;
height: 32px;
display: inline-block;
font-family: monospace;
position: relative;
}
.cell img {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
image-rendering: crisp-edges;
image-rendering: pixelated;
}
.cell.selected {
background: yellow;
}
</style>
<div id="disp"></div>
<div id="controls">
<select id="interactmode">
<option selected>select</option>
<option>rotate</option>
<option>place drill</option>
<option>place belt</option>
<option>place inserter</option>
</select>
<button id="step">run step</button>
</div>
<script>
const noiseSeed = Math.random() * (2**32-1)
const hash = (str, seed = 0) => {
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed
for (let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i)
h1 = Math.imul(h1 ^ ch, 2654435761)
h2 = Math.imul(h2 ^ ch, 1597334677)
}
h1 = Math.imul(h1 ^ (h1>>>16), 2246822507) ^ Math.imul(h2 ^ (h2>>>13), 3266489909)
h2 = Math.imul(h2 ^ (h2>>>16), 2246822507) ^ Math.imul(h1 ^ (h1>>>13), 3266489909)
return 4294967296 * (2097151 & h2) + (h1>>>0)
}
const normalize = ([x, y]) => [x / Math.hypot(x, y), y / Math.hypot(x, y)]
const dot = ([a, b], [c, d]) => a*c+b*d
const cartesianProduct = (xs, ys) => xs.flatMap(x => ys.map(y => [x, y]))
const gradients = cartesianProduct([-1, -0.5, 0.5, 1], [-1, -0.5, 0.5, 1]).map(normalize)
const gradientFor = (x, y) => gradients[hash(x+"."+y, noiseSeed) % gradients.length]
const interpolate = (a0, a1, w) => (a1 - a0) * (3.0 - w * 2.0) * w * w + a0
const perlin = (x, y) => {
const i = Math.floor(x), j = Math.floor(y)
const u = x - i, v = y - j
const n00 = dot(gradientFor(i, j), [u, v])
const n01 = dot(gradientFor(i + 1, j), [u - 1, v])
const n10 = dot(gradientFor(i, j + 1), [u, v - 1])
const n11 = dot(gradientFor(i + 1, j + 1), [u - 1, v - 1])
return interpolate(interpolate(n00, n01, u), interpolate(n10, n11, u), v)
}
const disp = document.querySelector("#disp")
let interactMode = document.querySelector("#interactmode").value
document.querySelector("#interactmode").addEventListener("input", ev => {
interactMode = ev.target.value
console.log(interactMode)
})
const directions = {
0: [0, 1],
1: [-1, 0],
2: [0, -1],
3: [1, 0]
}
const arrows = "→↓←↑"
const grid = {}
const SIZE = 24
for (let x = 0; x < SIZE; x++) {
for (let y = 0; y < SIZE; y++) {
let thing = { type: "empty", direction: 0, x, y }
thing.mapNoise = perlin(x * 3 / SIZE, y * 3 / SIZE)
grid[x+"."+y] = thing
}
}
const noises = Object.values(grid).map(x => x.mapNoise)
noises.sort((a, b) => a - b)
const thresh1 = noises[Math.floor(noises.length * (6/7))]
const thresh2 = noises[Math.ceil(noises.length * (1/12))]
for (const cell of Object.values(grid)) {
if (cell.mapNoise > thresh1) {
cell.type = "ore1"
} else if (cell.mapNoise < thresh2) {
cell.type = "ore2"
}
}
const adjacentAt = (x, y, d) => {
const dir = directions[d]
const nx = x + dir[0], ny = y + dir[1]
return grid[nx+"."+ny]
}
let updateOrder
const itemHandlers = ["drill", "belt"]
const recomputeBelts = () => {
for (const cell of Object.values(grid)) {
delete cell.inext
delete cell.iprev
if (itemHandlers.includes(cell.structure)) {
cell.inext = adjacentAt(cell.x, cell.y, cell.direction)
if (cell.inext) cell.inext.iprev = cell
}
}
for (const cell of Object.values(grid)) {
if (cell.inext) {
if (!cell.iprev) { // root of tree thing
let order = 0
let visited = new Set()
let current = cell
while (current && !visited.has(current) && itemHandlers.includes(current.structure)) {
if (current.order === undefined || current.order < order) {
current.order = order
}
order++
visited.add(current)
current = current.inext
}
}
}
}
const iorder = []
for (const cell of Object.values(grid)) {
if (cell.order !== undefined) {
iorder[cell.order] ||= []
iorder[cell.order].push(cell)
}
}
updateOrder = [].concat(...iorder).reverse()
}
const update = () => {
recomputeBelts()
for (const cell of Object.values(grid)) {
if (cell.type === "inserter") {
}
}
for (const cell of updateOrder) {
if (cell.structure === "drill") {
cell.item = cell.type
}
if (cell.inext && !cell.inext.item) {
if (cell.item) {
cell.inext.item = cell.item
cell.item = null
}
}
}
render()
}
document.querySelector("#step").addEventListener("click", () => {
update()
})
const rotate = (d, e) => (d + e) % 4
const render = () => {
while (disp.firstChild) disp.removeChild(disp.firstChild)
for (let y = 0; y < SIZE; y++) {
for (let x = 0; x < SIZE; x++) {
const cell = document.createElement("span")
cell.id = x+"."+y
cell.classList.add("cell")
const thing = grid[x+"."+y]
const type = thing.type
const layers = []
layers.push("blank")
if (type === "empty") {
} else if (type.startsWith("ore")) {
layers.push({ sprite: type, rotation: hash(cell.id) % 4 })
if (thing.structure === "drill") {
layers.push("drill")
}
}
if (thing.structure === "belt") {
const isInboundBelt = side => {
const newCell = adjacentAt(x, y, rotate(thing.direction, side))
return newCell && newCell.structure === "belt" && newCell.direction == rotate(thing.direction, rotate(side, 2))
}
const left = isInboundBelt(1), back = isInboundBelt(2), right = isInboundBelt(3)
if (left && !back) {
layers.push({ sprite: "belt-corner", reflect: true, rotation: 3 })
if (right) { layers.push("belt-conn") }
}
else if (right && !back) { layers.push({ sprite: "belt-corner", rotation: 1 }) }
else {
layers.push("belt")
if (right) { layers.push("belt-conn") }
if (left) { layers.push({ sprite: "belt-conn", reflect: true }) }
console.log(x, y, right, left)
}
if (thing.item) {
layers.push({ sprite: thing.item, scale: 0.5 })
}
}
if (thing.structure === "inserter") {
console.log("inserter")
layers.push("inserter-base")
layers.push("inserter-arm")
}
if (thing.selected) {
cell.classList.add("selected")
}
for (const layer of layers) {
const im = document.createElement("img")
im.src = `/sprites/${typeof layer === "string" ? layer : layer.sprite}.png`
const transforms = []
if (layer.rotation) { transforms.push(`rotate(${layer.rotation * 90}deg)`) }
if (layer.reflect) { transforms.push(`scaleX(-1)`) }
if (layer.scale) { transforms.push(`scale(${layer.scale})`) }
const transformString = transforms.join(" ")
if (transformString) { im.style.transform = transformString }
cell.appendChild(im)
}
cell.style.transform = `rotate(${thing.direction * 90}deg)`
disp.appendChild(cell)
}
disp.appendChild(document.createElement("br"))
}
}
disp.addEventListener("click", ev => {
const cell = ev.target.parentElement.id
console.log(grid[cell], cell)
if (interactMode === "select") {
grid[cell].selected ^= 1
} else if (interactMode === "place drill") {
if (grid[cell].type.startsWith("ore")) {
grid[cell].structure = "drill"
}
} else if (interactMode === "place belt") {
grid[cell].structure = "belt"
} else if (interactMode === "place inserter") {
grid[cell].structure = "inserter"
} else if (interactMode === "rotate") {
grid[cell].direction++
grid[cell].direction %= 4
}
ev.preventDefault()
render()
})
render()
</script>