summaryrefslogtreecommitdiff
path: root/2026/games_submissions/the0x539
diff options
context:
space:
mode:
authorLLLL Colonq <llll@colonq>2026-08-29 04:06:51 -0400
committerLLLL Colonq <llll@colonq>2026-08-29 04:06:51 -0400
commit761a3172bf4c15aed3836e4d1cd6f815b651b0e0 (patch)
treecc90491529e83349d84d42a8e9982e5f0f38338e /2026/games_submissions/the0x539
parent0d2d8cd897e94bd94e0b3ae7c6ce9aa580a44a0a (diff)
Fix
Diffstat (limited to '2026/games_submissions/the0x539')
-rw-r--r--2026/games_submissions/the0x539/.gitignore1
-rwxr-xr-x2026/games_submissions/the0x539/MinecraftStandard.otfbin159732 -> 0 bytes
-rw-r--r--2026/games_submissions/the0x539/crafting.js98
-rw-r--r--2026/games_submissions/the0x539/data.js101
-rw-r--r--2026/games_submissions/the0x539/difficulty.js100
-rw-r--r--2026/games_submissions/the0x539/index.html26
-rw-r--r--2026/games_submissions/the0x539/interaction.js581
-rwxr-xr-x2026/games_submissions/the0x539/items/boots.pngbin142 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/items/bow.pngbin160 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/items/chest.pngbin775 -> 0 bytes
-rw-r--r--2026/games_submissions/the0x539/items/chestplate.pngbin389 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/items/clock.gifbin2153 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/items/cobble.pngbin518 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/items/dust.pngbin179 -> 0 bytes
-rw-r--r--2026/games_submissions/the0x539/items/gold.pngbin399 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/items/helmet.pngbin134 -> 0 bytes
-rw-r--r--2026/games_submissions/the0x539/items/hopper.pngbin404 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/items/ingot.pngbin156 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/items/leggings.pngbin146 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/items/log.pngbin3913 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/items/pickaxe.pngbin174 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/items/piston.pngbin635 -> 0 bytes
-rw-r--r--2026/games_submissions/the0x539/items/plank.pngbin4974 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/items/rod.pngbin183 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/items/stick.pngbin388 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/items/string.pngbin162 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/items/tooltip-backdrop.pngbin768 -> 0 bytes
-rw-r--r--2026/games_submissions/the0x539/main.js197
-rw-r--r--2026/games_submissions/the0x539/screen.css168
-rwxr-xr-x2026/games_submissions/the0x539/sfx/big-ding.oggbin15799 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/sfx/oof.oggbin12546 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/sfx/small-ding.oggbin7139 -> 0 bytes
-rwxr-xr-x2026/games_submissions/the0x539/tooltip-backdrop.pngbin710 -> 0 bytes
-rw-r--r--2026/games_submissions/the0x539/ui.pngbin764 -> 0 bytes
34 files changed, 0 insertions, 1272 deletions
diff --git a/2026/games_submissions/the0x539/.gitignore b/2026/games_submissions/the0x539/.gitignore
deleted file mode 100644
index 845270b..0000000
--- a/2026/games_submissions/the0x539/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-harness
diff --git a/2026/games_submissions/the0x539/MinecraftStandard.otf b/2026/games_submissions/the0x539/MinecraftStandard.otf
deleted file mode 100755
index 8fa0b3e..0000000
--- a/2026/games_submissions/the0x539/MinecraftStandard.otf
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/crafting.js b/2026/games_submissions/the0x539/crafting.js
deleted file mode 100644
index 7a61cfc..0000000
--- a/2026/games_submissions/the0x539/crafting.js
+++ /dev/null
@@ -1,98 +0,0 @@
-import { recipes, amounts } from './data.js';
-
-export function getRecipeOutput() {
- const input = readGrid();
- if (input.length === 0) return null;
-
- const mirroredInput = input.map(row => row.toReversed());
-
- for (const [item, recipe] of Object.entries(recipes)) {
- const isMatch = compare(input, recipe) || compare(mirroredInput, recipe);
- if (!isMatch) continue;
-
- const amount = amounts[item] ?? 1;
- return [item, amount];
- }
-
- return null;
-}
-
-function last(arr) {
- return arr[arr.length - 1];
-}
-
-function isSymmetric(grid) {
- return grid.every(row => row[0] === last(row));
-}
-
-function readGrid() {
- const cells = document.querySelectorAll('crafting-grid > inventory-cell');
-
- const rows = [];
- for (let y = 0; y < 3; y++) {
- const row = [];
- for (let x = 0; x < 3; x++) {
- let item = null;
- const stack = cells[3 * y + x].firstElementChild;
- if (!!stack) {
- item = stack.getAttribute('data-item');
- }
- row.push(item);
- }
- rows.push(row);
- }
-
- trimGrid(rows);
-
- return rows;
-}
-
-function trimGrid(grid) {
- if (grid.flat().every(v => v === null)) {
- while (grid.length > 0) {
- grid.pop();
- }
- return;
- }
-
- // it is now established that the grid is non-empty,
- // so there will always be at least one row+col
-
- // trim blank rows from bottom
- while (last(grid).every(v => v === null)) {
- grid.pop();
- }
-
- // trim blank rows from top
- while (grid[0].every(v => v === null)) {
- grid.shift();
- }
-
- // trim blank columns from right
- while (grid.every(row => last(row) === null)) {
- for (const row of grid) {
- row.pop();
- }
- }
-
- // trim blank columns from left
- while (grid.every(row => row[0] === null)) {
- for (const row of grid) {
- row.shift();
- }
- }
-}
-
-function compare(a, b) {
- if (a.length !== b.length) return false;
-
- for (let y = 0; y < a.length; y++) {
- if (a[y].length !== b[y].length) return false;
-
- for (let x = 0; x < a[y].length; x++) {
- if (a[y][x] !== b[y][x]) return false;
- }
- }
-
- return true;
-}
diff --git a/2026/games_submissions/the0x539/data.js b/2026/games_submissions/the0x539/data.js
deleted file mode 100644
index 977c557..0000000
--- a/2026/games_submissions/the0x539/data.js
+++ /dev/null
@@ -1,101 +0,0 @@
-const ingredients = {
- i: 'ingot',
- l: 'log',
- p: 'plank',
- s: 'stick',
- d: 'dust',
- c: 'cobble',
- C: 'chest',
- S: 'string',
- g: 'gold',
-}
-
-function recipe(...shape) {
- return shape.map(line => Array.from(line).map(ch => {
- if (ch === ' ') {
- return null;
- } else {
- const item = ingredients[ch];
- console.assert(!!item);
- return item;
- }
- }));
-}
-
-export const recipes = {
- helmet: recipe(
- 'iii',
- 'i i',
- ),
- chestplate: recipe(
- 'i i',
- 'iii',
- 'iii',
- ),
- leggings: recipe(
- 'iii',
- 'i i',
- 'i i',
- ),
- boots: recipe(
- 'i i',
- 'i i',
- ),
- plank: recipe(
- 'l',
- ),
- stick: recipe(
- 'p',
- 'p',
- ),
- pickaxe: recipe(
- 'ppp',
- ' s ',
- ' s ',
- ),
- piston: recipe(
- 'ppp',
- 'cic',
- 'cdc',
- ),
- chest: recipe(
- 'ppp',
- 'p p',
- 'ppp',
- ),
- hopper: recipe(
- 'i i',
- 'iCi',
- ' i ',
- ),
- rod: recipe(
- ' s',
- ' sS',
- 's S',
- ),
- bow: recipe(
- ' sS',
- 's S',
- ' sS',
- ),
- clock: recipe(
- ' g ',
- 'gdg',
- ' g ',
- ),
-};
-
-export const amounts = {
- plank: 4,
- stick: 4,
-}
-
-export const stackSizes = {
- helmet: 1,
- chestplate: 1,
- leggings: 1,
- boots: 1,
- pickaxe: 1,
- rod: 1,
- bow: 1,
-}
diff --git a/2026/games_submissions/the0x539/difficulty.js b/2026/games_submissions/the0x539/difficulty.js
deleted file mode 100644
index 87a86b9..0000000
--- a/2026/games_submissions/the0x539/difficulty.js
+++ /dev/null
@@ -1,100 +0,0 @@
-export const scenarios = [
- {
- level: 0,
- items: {
- log: 2,
- },
- goal: ['chest'],
- time: _ => 10,
- },
- {
- level: 5,
- items: {
- plank: 7,
- cobble: 6,
- dust: 8,
- ingot: 4,
- },
- goal: ['piston'],
- time: d => 15 - d,
- },
- {
- level: 10,
- items: {
- log: 2,
- cobble: 4,
- dust: 2,
- ingot: 1,
- },
- goal: ['piston'],
- time: d => 15 - d,
- },
- {
- level: 13,
- items: {
- log: 8,
- },
- goal: ['pickaxe'],
- time: d => 15 - d,
- },
- {
- level: 18,
- items: {
- ingot: 8,
- plank: 8,
- },
- goal: ['hopper'],
- time: d => 20 - d,
- },
- {
- level: 21,
- items: {
- ingot: 12,
- log: 3,
- },
- goal: ['hopper'],
- time: d => 20 - d,
- },
- {
- level: 25,
- items: {
- ingot: 24,
- },
- goal: ['helmet', 'chestplate', 'leggings', 'boots'],
- time: d => 18 - d,
- },
- {
- level: 30,
- items: {
- ingot: 28,
- log: 1,
- string: 3,
- },
- goal: ['helmet', 'chestplate', 'leggings', 'boots', 'bow'],
- time: d => 30 - d,
- },
- {
- level: 35,
- items: {
- ingot: 24,
- log: 1,
- string: 3,
- gold: 64,
- dust: 17,
- },
- goal: ['helmet', 'chestplate', 'leggings', 'boots', 'bow'],
- time: _ => 15,
- },
- {
- level: 40,
- items: {
- ingot: 24,
- log: 1,
- string: 3,
- gold: 63,
- dust: 15,
- },
- goal: ['helmet', 'chestplate', 'leggings', 'boots', 'bow'],
- time: _ => 15,
- },
-];
diff --git a/2026/games_submissions/the0x539/index.html b/2026/games_submissions/the0x539/index.html
deleted file mode 100644
index 5a073ed..0000000
--- a/2026/games_submissions/the0x539/index.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="UTF-8" />
- <script defer type="module" src="./main.js"></script>
- <link rel="stylesheet" href="./screen.css" />
- </head>
- <body>
- <img class="background" src="./ui.png" />
-
- <label for="todo-list">To do:</label>
- <ul id="todo-list"></ul>
-
- <crafting-grid></crafting-grid>
- <crafting-output></crafting-output>
- <inventory-grid></inventory-grid>
- <follow-cursor>
- <item-tooltip></item-tooltip>
- <grabbed-stack></grabbed-stack>
- </follow-cursor>
-
- <audio src="./sfx/small-ding.ogg" id="small-ding" preload="auto"></audio>
- <audio src="./sfx/big-ding.ogg" id="big-ding" preload="auto"></audio>
- <audio src="./sfx/oof.ogg" id="oof" preload="auto"></audio>
- </body>
-</html>
diff --git a/2026/games_submissions/the0x539/interaction.js b/2026/games_submissions/the0x539/interaction.js
deleted file mode 100644
index d45d5c9..0000000
--- a/2026/games_submissions/the0x539/interaction.js
+++ /dev/null
@@ -1,581 +0,0 @@
-import { stackSizes } from './data.js';
-import { getRecipeOutput } from './crafting.js';
-
-class CraftEvent extends Event {
- item;
- constructor(item) {
- super('craft');
- this.item = item;
- }
-}
-
-const grabbedStack = document.querySelector('grabbed-stack');
-const craftingGrid = document.querySelector('crafting-grid');
-const inventoryGrid = document.querySelector('inventory-grid');
-const outputCell = document.querySelector('crafting-output');
-
-let state = 'idle';
-
-const splitTargets = new Set();
-
-export function resetInventory() {
- for (const stack of document.querySelectorAll('item-stack')) {
- stack.remove();
- }
- splitTargets.clear();
- state = 'idle';
-}
-
-export function createItem(item, count) {
- const elem = document.createElement('item-stack');
- elem.setAttribute('data-item', item);
- elem.setAttribute('data-count', count);
- const countElem = document.createElement('data');
- countElem.textContent = count.toString();
- elem.appendChild(countElem);
- return elem;
-}
-
-export function mouseDown(event) {
- if (state !== 'idle') return;
- if (event.button !== 0 && event.button !== 2) return;
-
- const cell = event.currentTarget;
-
- // Shift click: Transfer a full stack to the other place.
- if (event.shiftKey) {
- const sourceStack = cell.firstElementChild;
- if (sourceStack) {
- const targetGrid = cell.parentElement === inventoryGrid ? craftingGrid : inventoryGrid;
- shiftClickTransfer(sourceStack, targetGrid);
- }
- state = 'shift-drag';
- return;
- }
-
- const heldStack = grabbedStack.firstElementChild;
-
- // Click with empty cursor: pick up items
- if (!heldStack) {
- const sourceStack = cell.firstElementChild;
- if (!sourceStack) {
- return;
- }
-
- if (event.button === 2) {
- // Right click: take half the stack, rounded up
- const sourceCount = getCount(sourceStack);
- if (sourceCount === 1) {
- grabbedStack.appendChild(sourceStack);
- updateRecipeOutput();
- return;
- }
-
- const takenCount = Math.ceil(sourceCount / 2);
- const newCount = sourceCount - takenCount;
- const item = sourceStack.getAttribute('data-item');
- setCount(sourceStack, newCount);
- grabbedStack.appendChild(createItem(item, takenCount));
- } else {
- // Left click: take the whole stack. Drag to pick up other stacks
- grabbedStack.appendChild(sourceStack);
- state = 'pickup-drag';
- updateRecipeOutput();
- }
- } else {
- // Click with items held: deposit items
-
- if (event.button === 2) {
- // Right click: deposit one item. Drag to deposit one item in each cell
- state = 'split-one';
- depositOne(cell);
- } else {
- // Left click: split stack evenly across all empty/compatible cells.
- // If no compatible cells are dragged over, swap stack with the cell the mouse was released on.
- // (In the common case, this is just what a "click" looks like on a cell with incompatible contents.)
- heldStack.setAttribute('data-original-count', heldStack.getAttribute('data-count'));
- state = 'split-evenly';
- addSplitTarget(cell);
- }
- }
-}
-
-export function mouseEnter(event) {
- const cell = event.currentTarget;
-
- switch (state) {
- case 'shift-drag': {
- const sourceStack = cell.firstElementChild;
- if (!sourceStack) break;
- const targetGrid = cell.parentElement === inventoryGrid ? craftingGrid : inventoryGrid;
- shiftClickTransfer(sourceStack, targetGrid);
- updateRecipeOutput();
- break;
- }
-
- case 'pickup-drag': {
- const sourceStack = cell.firstElementChild;
- if (!sourceStack) break;
- const heldStack = grabbedStack.firstElementChild;
- const item = sourceStack.getAttribute('data-item');
- if (item !== heldStack.getAttribute('data-item')) return;
-
- const stackSize = stackSizes[item] ?? 64;
-
- const newCount = getCount(heldStack) + getCount(sourceStack);
- if (newCount > stackSize) return;
-
- setCount(heldStack, newCount);
- sourceStack.remove();
- updateRecipeOutput();
- break;
- }
-
- case 'split-one':
- depositOne(cell);
- break;
-
- case 'split-evenly':
- addSplitTarget(cell);
- break;
-
- default:
- break;
- }
-}
-
-export function mouseUp(event) {
- switch (state) {
- case 'split-evenly':
- case 'split-exhausted': {
- if (event.button !== 0) return;
-
- if (splitTargets.size <= 1) {
- const targetCell = event.target.closest('inventory-cell');
- if (targetCell) {
- depositAll(targetCell);
- }
- } else {
- commitSplit();
- }
- break;
- }
-
- case 'pickup-drag':
- if (event.button !== 0) return;
- state = 'idle';
- break;
-
- case 'split-one':
- if (event.button !== 2) return;
- state = 'idle';
- break;
-
- case 'shift-drag':
- // this could be either button. whatever.
- state = 'idle';
- break;
-
- default:
- break;
- }
-}
-
-export function craftClick(event) {
- if (event.shiftKey) {
- craftAll();
- } else {
- craftOne();
- }
-}
-
-function craftOne() {
- const previewStack = outputCell.firstElementChild;
- if (!previewStack) return;
-
- const item = previewStack.getAttribute('data-item');
- const craftCount = getCount(previewStack);
- const stackSize = stackSizes[item] ?? 64;
-
- const heldStack = grabbedStack.firstElementChild;
- if (heldStack !== null) {
- if (heldStack.getAttribute('data-item') !== item) {
- return;
- }
-
- const heldCount = getCount(heldStack);
- if (heldCount + craftCount > stackSize) {
- return;
- }
-
- setCount(heldStack, heldCount + craftCount);
- } else {
- grabbedStack.appendChild(createItem(item, craftCount));
- }
- consumeIngredients();
- document.dispatchEvent(new CraftEvent(item));
-}
-
-function craftAll() {
- const previewStack = outputCell.firstElementChild;
- if (!previewStack) return;
-
- const item = previewStack.getAttribute('data-item');
- const craftCount = getCount(previewStack);
- const stackSize = stackSizes[item] ?? 64;
-
- let success = false;
-
- do {
- let unallocated = craftCount;
-
- const nonEmptyTargets = [];
- let emptyTarget = null;
-
- for (const cell of inventoryGrid.children) {
- const stack = cell.firstElementChild;
- if (stack === null) {
- // This slot is empty.
- // All crafting outputs that don't fit into existing stacks
- // will be deposited into the first empty slot.
- emptyTarget ??= cell;
- continue;
- }
-
- if (stack.getAttribute('data-item') !== item) {
- // This slot already contains a different item,
- // so we can't add crafting outputs to it.
- continue;
- }
-
- const spareCapacity = stackSize - getCount(stack);
- if (spareCapacity <= 0) {
- // This slot contains the correct item, but is already full.
- continue;
- }
-
- const amountToAdd = Math.min(unallocated, spareCapacity);
- unallocated -= amountToAdd;
- nonEmptyTargets.push([stack, amountToAdd]);
-
- if (unallocated === 0) {
- // All crafting outputs can fit into existing stacks of the item.
- break;
- }
- }
-
- if (unallocated > 0 && emptyTarget === null) {
- // The inventory is full: no slots are empty and existing stacks don't have enough room.
- // Abort this attempt and conclude the loop.
- break;
- }
-
- // Add crafting output to existing stacks, to whatever extent possible.
- for (const [stack, amountToAdd] of nonEmptyTargets) {
- setCount(stack, getCount(stack) + amountToAdd);
- }
-
- // Put any remaining items into the first empty slot.
- if (unallocated > 0) {
- emptyTarget.appendChild(createItem(item, unallocated));
- }
-
- success = true;
-
- // Consume one set of ingredients.
- // Cease crafting if this causes the recipe output to change.
- } while (!consumeIngredients());
-
- if (success) {
- document.dispatchEvent(new CraftEvent(item));
- }
-}
-
-function consumeIngredients() {
- let anyExhausted = false;
-
- for (const cell of craftingGrid.children) {
- const stack = cell.firstElementChild;
- if (!stack) continue;
-
- const count = getCount(stack);
- if (count === 1) {
- stack.remove();
- anyExhausted = true;
- } else {
- setCount(stack, count - 1);
- }
- }
-
- return anyExhausted && updateRecipeOutput();
-}
-
-// Returns whether the output actually changed
-function updateRecipeOutput() {
- const outputStack = outputCell.firstElementChild;
-
- const output = getRecipeOutput();
- if (output === null) {
- if (outputStack === null) {
- return false;
- } else {
- outputStack.remove();
- return true;
- }
- }
-
- const [item, count] = output;
- if (outputStack === null) {
- outputCell.appendChild(createItem(item, count));
- return true;
- }
-
- if (outputStack.getAttribute('data-item') === item && getCount(outputStack) === count) {
- return false;
- }
-
- outputStack.setAttribute('data-item', item);
- setCount(outputStack, count);
- return true;
-}
-
-export function consumeClock() {
- if (state === 'split-exhausted') {
- commitSplit();
- }
-
- if (state === 'split-evenly' && splitTargets.size > 1) {
- // oh dear. this is a complicated situation
- // first, let's try to search for uninvolved clocks
- const uninvolved = document.querySelector('inventory-cell > item-stack[data-item="clock"]:not([data-original-count]):not(crafting-output item-stack)');
- if (!!uninvolved) {
- const count = getCount(uninvolved);
- if (count === 1) {
- uninvolved.remove();
- } else {
- setCount(uninvolved, count - 1);
- }
- // phew
- return;
- }
-
- // okay so we will actually need to remove a clock from the items being split, ugh
- // not a lot of code, but low confidence that it works properly
- const heldStack = grabbedStack.firstElementChild;
- const count = +heldStack.getAttribute('data-original-count');
- heldStack.setAttribute('data-original-count', count - 1);
- updateSplitPreview();
- return;
- }
-
- const stack = document.querySelector('item-stack[data-item="clock"][data-count]:not(crafting-output item-stack)')
- const count = getCount(stack);
- if (count === 1) {
- if (stack.parentElement === grabbedStack) {
- // conclude drag operations if they're done using a stack of clocks that gets deleted
- state = 'idle';
- }
- stack.remove();
- } else {
- setCount(stack, count - 1);
- }
-}
-
-function getCount(itemStack) {
- return +itemStack.getAttribute('data-count');
-}
-
-function setCount(itemStack, value) {
- itemStack.setAttribute('data-count', value);
- itemStack.firstElementChild.textContent = value.toString();
-}
-
-function depositOne(targetCell) {
- const heldStack = grabbedStack.firstElementChild;
- const item = heldStack.getAttribute('data-item');
- const heldCount = getCount(heldStack);
-
- const stackSize = stackSizes[item] ?? 64;
-
- const targetStack = targetCell.firstElementChild;
- if (targetStack) {
- if (targetStack.getAttribute('data-item') !== item) {
- return;
- }
-
- const targetCount = getCount(targetStack);
- if (targetCount >= stackSize) return;
-
- setCount(targetStack, targetCount + 1);
- } else {
- targetCell.appendChild(createItem(item, 1));
- }
-
- if (heldCount === 1) {
- heldStack.remove();
- state = 'idle';
- } else {
- setCount(heldStack, heldCount - 1);
- }
-
- updateRecipeOutput();
-}
-
-function shiftClickTransfer(sourceStack, targetGrid) {
- const item = sourceStack.getAttribute('data-item');
- const stackSize = stackSizes[item] ?? 64;
- let count = getCount(sourceStack);
-
- // First, search for existing stacks to add to
- for (const cell of targetGrid.children) {
- const targetStack = cell.firstElementChild;
- if (!targetStack) continue;
- if (targetStack.getAttribute('data-item') !== item) continue;
- const curCount = getCount(targetStack);
- const available = stackSize - curCount;
- if (available === 0) continue;
-
- const transferSize = Math.min(count, available);
- const newCount = curCount + transferSize;
- setCount(targetStack, newCount);
-
- count -= transferSize;
- if (count === 0) {
- sourceStack.remove();
- updateRecipeOutput();
- return;
- } else {
- setCount(sourceStack, count);
- }
- }
-
- // Now, look for any empty slots to move whatever's left to
- for (const cell of targetGrid.children) {
- if (!cell.firstElementChild) {
- cell.appendChild(sourceStack);
- break;
- }
- }
-
- updateRecipeOutput();
-}
-
-function addSplitTarget(targetCell) {
- const heldStack = grabbedStack.firstElementChild;
- const item = heldStack.getAttribute('data-item');
-
- const targetStack = targetCell.firstElementChild;
- if (targetStack) {
- if (targetStack.getAttribute('data-item') !== item) {
- return;
- }
- }
-
- splitTargets.add(targetCell);
-
- if (splitTargets.size > 1) {
- updateSplitPreview();
- }
-}
-
-function updateSplitPreview() {
- const heldStack = grabbedStack.firstElementChild;
- const item = heldStack.getAttribute('data-item');
- const heldCount = +heldStack.getAttribute('data-original-count');
-
- const stackSize = stackSizes[item] ?? 64;
-
- const splitCount = Math.max(1, Math.floor(heldCount / splitTargets.size));
- let remainder = heldCount;
-
- for (const targetCell of splitTargets) {
- let targetStack = targetCell.firstElementChild;
- if (!targetStack) {
- targetStack = createItem(item, 0);
- targetCell.appendChild(targetStack);
- }
- if (!targetStack.hasAttribute('data-original-count')) {
- targetStack.setAttribute('data-original-count', targetStack.getAttribute('data-count'));
- }
- const targetCount = +targetStack.getAttribute('data-original-count');
- const available = stackSize - targetCount;
- const transferSize = Math.min(splitCount, available);
- setCount(targetStack, targetCount + transferSize);
- remainder -= transferSize;
- // TODO: give stacks yellow text if they're too full to accomodate their full share of the split
-
- if (remainder === 0) break;
- }
-
- setCount(heldStack, remainder);
-
- if (splitCount === 1 && remainder === 0) {
- state = 'split-exhausted';
- }
-}
-
-function commitSplit() {
- const heldStack = grabbedStack.firstElementChild;
- const heldCount = getCount(heldStack);
-
- if (heldCount === 0) {
- heldStack.remove();
- } else {
- heldStack.removeAttribute('data-original-count');
- }
-
- for (const target of splitTargets) {
- target.firstElementChild?.removeAttribute('data-original-count');
- }
-
- splitTargets.clear();
-
- state = 'idle';
-
- updateRecipeOutput();
-}
-
-function depositAll(targetCell) {
- const heldStack = grabbedStack.firstElementChild;
- const item = heldStack.getAttribute('data-item');
- const heldCount = getCount(heldStack);
-
- const stackSize = stackSizes[item] ?? 64;
-
- const targetStack = targetCell.firstElementChild;
- if (!!targetStack && targetStack.getAttribute('data-item') !== item) {
- // swap held and target stacks because they don't match
- targetCell.appendChild(heldStack);
- grabbedStack.appendChild(targetStack);
- } else {
- if (!targetStack) {
- // deposit the full stack to the slot
- targetCell.appendChild(heldStack);
- } else {
- // deposit as much as possible
- const targetCount = getCount(targetStack);
- const available = stackSize - targetCount;
- const transferSize = Math.min(heldCount, available);
- const newCount = targetCount + transferSize;
- setCount(targetStack, newCount);
-
- if (transferSize < heldCount) {
- setCount(heldStack, heldCount - transferSize);
- } else {
- heldStack.remove();
- }
- }
- }
-
- heldStack.removeAttribute('data-original-count');
-
- for (const target of splitTargets) {
- target.firstElementChild?.removeAttribute('data-original-count');
- }
-
- splitTargets.clear();
-
- state = 'idle';
-
- updateRecipeOutput();
-}
diff --git a/2026/games_submissions/the0x539/items/boots.png b/2026/games_submissions/the0x539/items/boots.png
deleted file mode 100755
index ca6c33b..0000000
--- a/2026/games_submissions/the0x539/items/boots.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/bow.png b/2026/games_submissions/the0x539/items/bow.png
deleted file mode 100755
index 6922e69..0000000
--- a/2026/games_submissions/the0x539/items/bow.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/chest.png b/2026/games_submissions/the0x539/items/chest.png
deleted file mode 100755
index eb7f766..0000000
--- a/2026/games_submissions/the0x539/items/chest.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/chestplate.png b/2026/games_submissions/the0x539/items/chestplate.png
deleted file mode 100644
index 0cc9a63..0000000
--- a/2026/games_submissions/the0x539/items/chestplate.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/clock.gif b/2026/games_submissions/the0x539/items/clock.gif
deleted file mode 100755
index d4a7698..0000000
--- a/2026/games_submissions/the0x539/items/clock.gif
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/cobble.png b/2026/games_submissions/the0x539/items/cobble.png
deleted file mode 100755
index 55dbc4f..0000000
--- a/2026/games_submissions/the0x539/items/cobble.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/dust.png b/2026/games_submissions/the0x539/items/dust.png
deleted file mode 100755
index 9d17c14..0000000
--- a/2026/games_submissions/the0x539/items/dust.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/gold.png b/2026/games_submissions/the0x539/items/gold.png
deleted file mode 100644
index 99b1a46..0000000
--- a/2026/games_submissions/the0x539/items/gold.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/helmet.png b/2026/games_submissions/the0x539/items/helmet.png
deleted file mode 100755
index 850dac6..0000000
--- a/2026/games_submissions/the0x539/items/helmet.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/hopper.png b/2026/games_submissions/the0x539/items/hopper.png
deleted file mode 100644
index 9655818..0000000
--- a/2026/games_submissions/the0x539/items/hopper.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/ingot.png b/2026/games_submissions/the0x539/items/ingot.png
deleted file mode 100755
index 17e8aa8..0000000
--- a/2026/games_submissions/the0x539/items/ingot.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/leggings.png b/2026/games_submissions/the0x539/items/leggings.png
deleted file mode 100755
index a28a10f..0000000
--- a/2026/games_submissions/the0x539/items/leggings.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/log.png b/2026/games_submissions/the0x539/items/log.png
deleted file mode 100755
index 56d24f0..0000000
--- a/2026/games_submissions/the0x539/items/log.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/pickaxe.png b/2026/games_submissions/the0x539/items/pickaxe.png
deleted file mode 100755
index 9dea9e2..0000000
--- a/2026/games_submissions/the0x539/items/pickaxe.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/piston.png b/2026/games_submissions/the0x539/items/piston.png
deleted file mode 100755
index f08618a..0000000
--- a/2026/games_submissions/the0x539/items/piston.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/plank.png b/2026/games_submissions/the0x539/items/plank.png
deleted file mode 100644
index 863206a..0000000
--- a/2026/games_submissions/the0x539/items/plank.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/rod.png b/2026/games_submissions/the0x539/items/rod.png
deleted file mode 100755
index 6610384..0000000
--- a/2026/games_submissions/the0x539/items/rod.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/stick.png b/2026/games_submissions/the0x539/items/stick.png
deleted file mode 100755
index d32381d..0000000
--- a/2026/games_submissions/the0x539/items/stick.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/string.png b/2026/games_submissions/the0x539/items/string.png
deleted file mode 100755
index ad78eb4..0000000
--- a/2026/games_submissions/the0x539/items/string.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/items/tooltip-backdrop.png b/2026/games_submissions/the0x539/items/tooltip-backdrop.png
deleted file mode 100755
index 05c02cd..0000000
--- a/2026/games_submissions/the0x539/items/tooltip-backdrop.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/main.js b/2026/games_submissions/the0x539/main.js
deleted file mode 100644
index 14a2b45..0000000
--- a/2026/games_submissions/the0x539/main.js
+++ /dev/null
@@ -1,197 +0,0 @@
-import { recipes, amounts } from './data.js';
-import {
- createItem,
- mouseDown,
- mouseEnter,
- mouseUp,
- craftClick,
- consumeClock,
- resetInventory,
-} from './interaction.js';
-import { scenarios } from './difficulty.js';
-
-const followCursor = document.querySelector('follow-cursor');
-const tooltip = document.querySelector('item-tooltip');
-const craftingGrid = document.querySelector('crafting-grid');
-const inventoryGrid = document.querySelector('inventory-grid');
-const craftingOutput = document.querySelector('crafting-output');
-const todoList = document.getElementById('todo-list');
-
-const desiredCrafts = new Set();
-
-const playSound = {
- playImpl(id, volume = 1.0, pitch = 1.0) {
- const audio = document.getElementById(id);
-
- audio.currentTime = 0;
-
- const p = new Promise(resolve => {
- const f = (event) => {
- audio.removeEventListener('ended', f);
- resolve(event);
- };
- audio.addEventListener('ended', f);
- });
-
- audio.volume = volume;
- audio.playbackRate = pitch;
- audio.preservesPitch = false;
-
- audio.play();
- return p;
- },
-
- smallDing() {
- return this.playImpl('small-ding', 0.1, 0.55 + 0.7 * Math.random());
- },
-
- bigDing() {
- return this.playImpl('big-ding', 0.75);
- },
-
- oof() {
- return this.playImpl('oof', 0.75);
- },
-};
-
-function onCraft(event) {
- const item = event.item;
- if (desiredCrafts.has(item)) {
- desiredCrafts.delete(item);
-
- document.querySelector(`#todo-list > li:has([data-item="${item}"])`)?.remove();
-
- if (desiredCrafts.size > 0) {
- playSound.smallDing();
- } else {
- endGame(true);
- }
- }
-}
-
-function handleMessage(msg) {
- if (msg.op === 'start') {
- startGame(msg.difficulty);
- }
-}
-
-function giveItem(item, count) {
- const emptySlots = document.querySelectorAll('inventory-grid > inventory-cell:empty');
- const index = Math.floor(Math.random() * emptySlots.length);
- emptySlots[index].appendChild(createItem(item, count));
-}
-
-let timerInterval = null;
-
-function timer() {
- if (!document.querySelector('item-stack[data-item="clock"]:not(crafting-output item-stack)')) {
- // time's up!
- endGame(false);
- } else {
- consumeClock();
- }
-}
-
-function startGame(difficulty) {
- document.body.classList.remove('failed');
-
- resetInventory();
-
- const scenario = scenarios.toReversed().find(s => difficulty >= s.level);
-
- desiredCrafts.clear();
- for (const item of scenario.goal) {
- desiredCrafts.add(item);
- }
-
- todoList.replaceChildren();
- for (const item of desiredCrafts) {
- const listItem = document.createElement('li');
-
- const stack = document.createElement('item-stack');
- stack.setAttribute('data-item', item);
- listItem.append(stack);
-
- todoList.append(listItem);
- }
-
- for (const [item, amount] of Object.entries(scenario.items)) {
- giveItem(item, amount);
- }
- giveItem('clock', scenario.time(difficulty - scenario.level));
-
- document.addEventListener('mousemove', onMouseMove);
- timerInterval = setInterval(timer, 1000);
- window.parent.postMessage({ op: 'started', verb: 'craft!' });
-}
-
-async function endGame(win) {
- clearInterval(timerInterval);
- timerInterval = null;
- if (win) {
- await playSound.bigDing();
- } else {
- document.body.classList.add('failed');
- await playSound.oof();
- }
- await new Promise(resolve => setTimeout(resolve, 500));
- document.removeEventListener('mousemove', onMouseMove);
- window.parent.postMessage({ op: 'done', win });
-}
-
-function createCell() {
- const elem = document.createElement('inventory-cell');
- elem.addEventListener('mousedown', mouseDown);
- elem.addEventListener('mouseenter', mouseEnter);
- return elem;
-}
-
-function onMouseMove(event) {
- followCursor.setAttribute('style', `left: ${event.clientX}px; top: ${event.clientY}px`);
-
- const hoverItem = document.querySelector('item-stack:hover');
- if (hoverItem !== null) {
- const name = hoverItem.getAttribute('data-item');
- tooltip.textContent = name;
- } else {
- tooltip.textContent = '';
- }
-}
-
-document.addEventListener('contextmenu', e => e.preventDefault());
-
-const allItems = new Set();
-for (const [output, shape] of Object.entries(recipes)) {
- allItems.add(output);
- for (const item of shape.flat()) {
- if (item) {
- allItems.add(item);
- }
- }
-}
-
-const stylesheet = document.styleSheets[0];
-
-for (const item of allItems) {
- stylesheet.insertRule(`
- item-stack[data-item="${item}"]::before {
- background-image: url("./items/${item}.png");
- }
- `);
-}
-
-for (let i = 0; i < 3 * 3; i++) {
- craftingGrid.appendChild(createCell());
-}
-
-for (let i = 0; i < 12 * 3; i++) {
- inventoryGrid.appendChild(createCell());
-}
-
-document.addEventListener('mouseup', mouseUp);
-document.addEventListener('craft', onCraft);
-
-craftingOutput.addEventListener('click', craftClick);
-
-window.addEventListener('message', m => handleMessage(m.data));
-window.parent.postMessage({ op: 'ready' });
diff --git a/2026/games_submissions/the0x539/screen.css b/2026/games_submissions/the0x539/screen.css
deleted file mode 100644
index d9a6f72..0000000
--- a/2026/games_submissions/the0x539/screen.css
+++ /dev/null
@@ -1,168 +0,0 @@
-@font-face {
- font-family: "Minecraft";
- src: url("./MinecraftStandard.otf") format("opentype");
-}
-
-* {
- box-sizing: border-box;
- user-select: none;
- font-family: Minecraft;
- font-smooth: never;
- font-size: 6px;
- text-rendering: geometricPrecision;
- line-height: 1em;
-}
-
-body {
- margin: 0;
-}
-
-crafting-grid,
-inventory-grid {
- position: absolute;
- display: grid;
- align-items: center;
- justify-items: center;
-}
-
-follow-cursor {
- position: absolute;
- pointer-events: none;
-}
-
-grabbed-stack {
- display: block;
- translate: -50% -50%;
- z-index: 10;
- pointer-events: none;
-}
-
-:root:has(grabbed-stack > item-stack) {
- cursor: grabbing;
-}
-
-inventory-cell,
-crafting-output {
- width: 16px;
- height: 16px;
-
- &:hover,
- &:has(> item-stack[data-original-count]) {
- background-color: rgba(255, 255, 255, 0.5);
- }
-
- &:has(> item-stack) {
- cursor: grab;
- }
-}
-
-crafting-grid {
- left: 62px;
- top: 22px;
- grid-template: repeat(3, 18px) / repeat(3, 18px);
-}
-
-crafting-output {
- position: absolute;
- left: 157px;
- top: 41px;
-}
-
-inventory-grid {
- left: 12px;
- top: 94px;
- grid-template: repeat(3, 18px) / repeat(12, 18px);
-}
-
-item-stack {
- display: block;
- position: relative;
-
- & > data {
- position: absolute;
- color: #fcfcfc;
- text-shadow: #3e3e3e 1px 1px;
- bottom: 0;
- right: 0;
- translate: 1px -1px;
- }
-
- &[data-count="0"] {
- display: none;
- }
-
- &[data-count="1"] > data {
- display: none;
- }
-
- &[data-item="clock"]::before {
- background-image: url("./items/clock.gif");
- }
-
- &::before {
- display: block;
- width: 16px;
- height: 16px;
- content: "";
- background-size: 16px 16px;
- }
-}
-
-img {
- pointer-events: none;
-}
-
-item-tooltip {
- display: block;
- color: white;
- border-style: solid;
- border-width: 4px;
- border-image-source: url("./tooltip-backdrop.png");
- border-image-slice: 4 fill;
- text-shadow: gray 1px 1px;
- height: 16px;
- line-height: 1em;
- color: #fcfcfc;
- text-shadow: #3e3e3e 1px 1px;
- translate: 8px -100%;
-
- &:empty,
- &:has(+ * > item-stack) {
- display: none;
- }
-}
-
-body.failed {
- filter: brightness(0.8) sepia(75%) hue-rotate(-45deg);
- cursor: not-allowed;
- & > * {
- pointer-events: none;
- }
-}
-
-label[for="todo-list"] {
- position: absolute;
- top: 12px;
- left: 12px;
- color: #3f3f3f;
-
- &:has(+ #todo-list:empty) {
- display: none;
- }
-}
-
-#todo-list {
- position: absolute;
- left: 12px;
- top: 24px;
- margin: 0;
- padding: 0;
- display: flex;
- width: 32px;
- flex-wrap: wrap;
- justify-content: center;
-
- & > li {
- list-style: none;
- }
-}
diff --git a/2026/games_submissions/the0x539/sfx/big-ding.ogg b/2026/games_submissions/the0x539/sfx/big-ding.ogg
deleted file mode 100755
index a3f4c0b..0000000
--- a/2026/games_submissions/the0x539/sfx/big-ding.ogg
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/sfx/oof.ogg b/2026/games_submissions/the0x539/sfx/oof.ogg
deleted file mode 100755
index 10448dc..0000000
--- a/2026/games_submissions/the0x539/sfx/oof.ogg
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/sfx/small-ding.ogg b/2026/games_submissions/the0x539/sfx/small-ding.ogg
deleted file mode 100755
index a38b2cc..0000000
--- a/2026/games_submissions/the0x539/sfx/small-ding.ogg
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/tooltip-backdrop.png b/2026/games_submissions/the0x539/tooltip-backdrop.png
deleted file mode 100755
index 893b468..0000000
--- a/2026/games_submissions/the0x539/tooltip-backdrop.png
+++ /dev/null
Binary files differ
diff --git a/2026/games_submissions/the0x539/ui.png b/2026/games_submissions/the0x539/ui.png
deleted file mode 100644
index ab1873c..0000000
--- a/2026/games_submissions/the0x539/ui.png
+++ /dev/null
Binary files differ