mirror of
https://github.com/osmarks/random-stuff
synced 2025-09-10 22:36:01 +00:00
publish backlog of things
This commit is contained in:
254
code-guessing/bad-factorio.html
Normal file
254
code-guessing/bad-factorio.html
Normal file
@@ -0,0 +1,254 @@
|
||||
<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>
|
Reference in New Issue
Block a user