From 8a9b437ae77f33a363523a93380088fb0e3c32f6 Mon Sep 17 00:00:00 2001 From: Pabloader Date: Sun, 18 Aug 2024 08:09:26 +0000 Subject: [PATCH] Not finished WFC --- .clang-format | 3 + README.md | 3 +- build/assets/stdlib.c | 69 ++++++++++ build/assets/stdlib.h | 19 +++ build/build.ts | 12 +- build/wasmPlugin.ts | 33 ++++- bun.lockb | Bin 37231 -> 28503 bytes compile_flags.txt | 9 ++ package.json | 2 +- src/common/display/brick.tsx | 3 +- src/common/display/canvas.ts | 25 ++++ src/common/display/index.ts | 6 - src/games/brick-dungeon/index.ts | 2 +- src/games/life/index.ts | 11 +- src/games/life/life.c | 10 +- src/games/wfc/assets/wfc.png | Bin 0 -> 204 bytes src/games/wfc/index.ts | 43 +++++++ src/games/wfc/wfc.c | 212 +++++++++++++++++++++++++++++++ src/types.d.ts | 4 + 19 files changed, 429 insertions(+), 37 deletions(-) create mode 100644 .clang-format create mode 100644 build/assets/stdlib.c create mode 100644 build/assets/stdlib.h create mode 100644 compile_flags.txt create mode 100644 src/common/display/canvas.ts delete mode 100644 src/common/display/index.ts create mode 100644 src/games/wfc/assets/wfc.png create mode 100644 src/games/wfc/index.ts create mode 100644 src/games/wfc/wfc.c diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..fe1cc65 --- /dev/null +++ b/.clang-format @@ -0,0 +1,3 @@ +IndentWidth: 4 +ColumnLimit: 240 +PointerAlignment: Left \ No newline at end of file diff --git a/README.md b/README.md index f723e96..3a23b87 100644 --- a/README.md +++ b/README.md @@ -103,8 +103,7 @@ bun run build sudo apt install clang lld wabt ``` - Supports only function exports & `memory` - - exported all non-static functions - - no stdlib + - No stdlib ## Publishing diff --git a/build/assets/stdlib.c b/build/assets/stdlib.c new file mode 100644 index 0000000..ecdbf15 --- /dev/null +++ b/build/assets/stdlib.c @@ -0,0 +1,69 @@ +#include + +#define BLOCK_SIZE (64 * 1024) +#define BLOCK_1MB 16 + +static uintptr_t bump_pointer = (uintptr_t)&__heap_base; +static uintptr_t heap_end = BLOCK_1MB * BLOCK_SIZE; +IMPORT(grow) void grow(uint32_t blocks); + +static void* bump_alloc(uintptr_t n) { + uintptr_t r = bump_pointer; + bump_pointer += n; + + while (bump_pointer >= heap_end) { + grow(heap_end / BLOCK_SIZE); + heap_end *= 2; + } + + return (void*)r; +} + +void* malloc(uintptr_t n) { + return bump_alloc(n); +} + +void free(void* p) { (void)p; } + +void* memset(void* s, uint8_t c, uint32_t n) { + uint8_t* p = (uint8_t*)s; + while (n--) { + *p++ = c; + } + return s; +} + +void* memcpy(void* dest, const void* src, uint32_t n) { + uint8_t* d = (uint8_t*)dest; + const uint8_t* s = (const uint8_t*)src; + while (n--) { + *d++ = *s++; + } + return dest; +} + +int memcmp(const void* s1, const void* s2, uint32_t n) { + const uint8_t* p1 = (const uint8_t*)s1; + const uint8_t* p2 = (const uint8_t*)s2; + + while (n--) { + if (*p1 != *p2) { + return (*p1 - *p2); + } + p1++; + p2++; + } + + return 0; // Memory blocks are equal +} + +static uint64_t rand_state; + +void srand(uint64_t seed) { rand_state = seed; } + +uint64_t rand(void) { + uint64_t z = (rand_state += 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + return z ^ (z >> 31); +} diff --git a/build/assets/stdlib.h b/build/assets/stdlib.h new file mode 100644 index 0000000..3639e49 --- /dev/null +++ b/build/assets/stdlib.h @@ -0,0 +1,19 @@ +#pragma once +#include +#include +extern unsigned char __heap_base; + +#define IMPORT(name) __attribute__((import_module("env"), import_name(#name))) +#define EXPORT(name) __attribute__((export_name(#name))) + +EXPORT(malloc) void* malloc(uintptr_t); +void free(void*); + +void* memset(void* s, uint8_t c, uint32_t n); +void* memcpy(void* dest, const void* src, uint32_t n); +int memcmp(const void* s1, const void* s2, uint32_t n); + +IMPORT(log) void print_int(int64_t); + +EXPORT(__srand) void srand(uint64_t seed); +uint64_t rand(void); diff --git a/build/build.ts b/build/build.ts index 390f192..20ae4df 100644 --- a/build/build.ts +++ b/build/build.ts @@ -3,7 +3,7 @@ import { $ } from 'bun'; import path from 'path'; import fs from 'fs/promises'; import { buildHTML } from "./html"; -import inquirer from 'inquirer'; +import select from '@inquirer/select'; import { isGame, getGames } from './isGame'; const outDir = path.resolve(import.meta.dir, '..', '..', 'dist'); @@ -13,12 +13,10 @@ let game = process.argv[2]; const publish = process.env.PUBLISH_LOCATION; while (!await isGame(game)) { - const answer = await inquirer.prompt([{ - type: 'list', - name: 'game', - choices: await getGames(), - }]); - game = answer.game; + const game = await select({ + message: 'Game to build:', + choices: (await getGames()).map(value => ({ value })), + }); } const html = await buildHTML(game, true); diff --git a/build/wasmPlugin.ts b/build/wasmPlugin.ts index fc489e9..f209375 100644 --- a/build/wasmPlugin.ts +++ b/build/wasmPlugin.ts @@ -15,13 +15,34 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin let wasmPath = path.resolve(import.meta.dir, '..', '..', 'dist', 'tmp.wasm'); let jsContent: string = ` async function instantiate(url) { - const { instance } = await WebAssembly.instantiateStreaming(fetch(url)); + const memory = new WebAssembly.Memory({ + initial: 16, + }); + let data = new DataView(memory.buffer); + const { instance } = await WebAssembly.instantiateStreaming(fetch(url), { + env: { + memory, + log(...args) { + console.log('[wasm]', ...args); + }, + grow(blocks) { + if (blocks > 0) { + memory.grow(blocks); + data = new DataView(memory.buffer); + } + } + } + }); + return { ...instance.exports, - data: new DataView(instance.exports.memory.buffer), + memory, + get data() { return data; }, }; } - const module = await instantiate(new URL("tmp.wasm", import.meta.url)); + const module = await instantiate(new URL($WASM$)); + if (typeof module.__srand === 'function') module.__srand(BigInt(Date.now())); + export default module; `; if (args.path.endsWith('.ts')) { @@ -49,7 +70,11 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin } else if (args.path.endsWith('.wasm')) { wasmPath = args.path; } else { - const result = await $`clang --target=wasm32 -O3 --no-standard-libraries -Wl,--export-all -Wl,--no-entry -fno-builtin -o ${wasmPath} ${args.path}`; + const buildAssets = path.resolve(import.meta.dir, 'assets'); + const stdlib = `${buildAssets}/stdlib.c`; + const opt = production ? '-O3': '-O0'; + const result = await $`clang --target=wasm32 ${opt} -flto -fno-builtin --no-standard-libraries -I ${buildAssets} -Wall -Wextra -Wpedantic -Werror -Wl,--lto-O3 -Wl,--no-entry -Wl,--import-memory -o ${wasmPath} ${args.path} ${stdlib}`; + if (result.exitCode !== 0) { throw new Error('Compile failed, check output'); } diff --git a/bun.lockb b/bun.lockb index a0c3a0c5be419ac2f9ae01828eaa701c0ce82d33..1b68a434ac0c2e2c2f224243cd7a17899cfa884a 100755 GIT binary patch delta 6505 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 diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..71ef868 --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1,9 @@ + --target=wasm32 +-fno-builtin +--no-standard-libraries +-I +build/assets/ +-Wall +-Wextra +-Wpedantic +-Werror \ No newline at end of file diff --git a/package.json b/package.json index d57b119..4843cd9 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "build": "bun build/build.ts" }, "dependencies": { + "@inquirer/select": "2.3.10", "classnames": "2.5.1", "preact": "10.22.0" }, @@ -18,7 +19,6 @@ "assemblyscript": "0.27.29", "bun-lightningcss": "0.2.0", "html-minifier": "4.0.0", - "inquirer": "9.3.4", "typescript": "5.5.2", "uglify-js": "3" } diff --git a/src/common/display/brick.tsx b/src/common/display/brick.tsx index f2298fa..c352a0c 100644 --- a/src/common/display/brick.tsx +++ b/src/common/display/brick.tsx @@ -1,5 +1,4 @@ import { render } from "preact"; -import type { Display } from "."; import { clamp, range } from "@common/utils"; import classNames from "classnames"; @@ -18,7 +17,7 @@ export interface BrickDisplayImage { height: number; } -export class BrickDisplay implements Display { +export class BrickDisplay { #field: boolean[] = new Array(FIELD_HEIGHT * FIELD_WIDTH); #miniField: boolean[] = new Array(MINI_FIELD_HEIGHT * MINI_FIELD_WIDTH); #score: number = 0; diff --git a/src/common/display/canvas.ts b/src/common/display/canvas.ts new file mode 100644 index 0000000..cdca469 --- /dev/null +++ b/src/common/display/canvas.ts @@ -0,0 +1,25 @@ +export function createCanvas(width: number, height: number) { + const canvas = document.createElement('canvas'); + + canvas.width = width; + canvas.height = height; + canvas.style.height = '100%'; + canvas.style.imageRendering = 'pixelated'; + + document.body.style.display = 'flex'; + document.body.style.justifyContent = 'center'; + document.body.append(canvas); + + return canvas; +} + +export function getImageData(image: HTMLImageElement): ImageData { + const canvas = document.createElement('canvas'); + canvas.width = image.naturalWidth; + canvas.height = image.naturalHeight; + const ctx = canvas.getContext('2d')!; + ctx.drawImage(image, 0, 0); + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + + return imageData; +} \ No newline at end of file diff --git a/src/common/display/index.ts b/src/common/display/index.ts deleted file mode 100644 index 714f5eb..0000000 --- a/src/common/display/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface Display { - init(): void; - update(): void; -} - -export { BrickDisplay, type BrickDisplayImage } from './brick'; \ No newline at end of file diff --git a/src/games/brick-dungeon/index.ts b/src/games/brick-dungeon/index.ts index 51cdca3..18ee0b7 100644 --- a/src/games/brick-dungeon/index.ts +++ b/src/games/brick-dungeon/index.ts @@ -1,4 +1,4 @@ -import { BrickDisplay } from "@common/display"; +import { BrickDisplay } from "@common/display/brick"; import { isPressed, updateKeys } from "@common/input"; import spritesheetImage from './assets/spritesheet.png'; diff --git a/src/games/life/index.ts b/src/games/life/index.ts index 09fe4fa..b239491 100644 --- a/src/games/life/index.ts +++ b/src/games/life/index.ts @@ -1,9 +1,10 @@ +import { createCanvas } from "@common/display/canvas"; import life from "./life.c"; const width = life.getWidth(); const height = life.getHeight(); -const canvas = document.createElement('canvas'); +const canvas = createCanvas(width, height); const context = canvas.getContext('2d')!; const imageData = context.createImageData(width, height); @@ -12,15 +13,7 @@ const step = life.step as CallableFunction; const pixels = new Uint8Array(life.memory.buffer, life.getPixels(), width * height * 4); export default function main() { - document.body.append(canvas); initField(Date.now()); - canvas.width = width; - canvas.height = height; - canvas.style.height = '100%'; - canvas.style.imageRendering = 'pixelated'; - - document.body.style.display = 'flex'; - document.body.style.justifyContent = 'center'; console.log(life, pixels.length); diff --git a/src/games/life/life.c b/src/games/life/life.c index cc2a0dc..4a09696 100644 --- a/src/games/life/life.c +++ b/src/games/life/life.c @@ -11,11 +11,11 @@ void* malloc(uint32_t n); static uint8_t rand8(void); static int countNeighbours(int x, int y); -int getWidth() { return width; } -int getHeight() { return height; } -uint8_t* getPixels() { return pixels; } +int getWidth(void) { return width; } +int getHeight(void) { return height; } +uint8_t* getPixels(void) { return pixels; } -void step() +void step(void) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { @@ -76,7 +76,7 @@ static uint8_t rand_state[STATE_BYTES] = { 0x87, 0xdd, 0xdc, 0x10, 0x35, 0xbc, 0 static uint8_t rand8(void) { static uint16_t c = 0x42; - static int i = 0; + static uint32_t i = 0; uint16_t t; uint8_t x; diff --git a/src/games/wfc/assets/wfc.png b/src/games/wfc/assets/wfc.png new file mode 100644 index 0000000000000000000000000000000000000000..d07b823be841b3dc1af26530cf0f01425c951049 GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j+3?x7IEPn{37>k44ofy`glX(f`Bn0?`xB_V< z1rt{dn>0(q{xIwNg=#k!q`f;l>-Q(KnNI_<_8FK@$@9+C5As$qvaz(cFEL1Hiq3nO zJo|tA*(axO|M~y_|LnEfTk6^~v$7(?g36q_))*Px3{SpZy{mt^S69Bl@8mt_fp$oF xx;Tb#%uE(&Wnc?r6JWR~BO?=$xx$8pfx+H_$ + +#define PATTERN_SIZE 3 +#define PATTERN_BATCH 8 + +typedef struct { + uint32_t neighbours[9]; +} pattern_t; + +typedef struct { + /** pattern indices */ + uint32_t* possibilities; + uint32_t num_possibilities; +} superposition_t; + +typedef enum { NORTH, EAST, SOUTH, WEST, DIRECTION_MAX } direction_t; + +pattern_t* patterns; +uint32_t num_patterns; + +uint32_t* pixels; +superposition_t* superpositions; +uint32_t width; +uint32_t height; + +void patterns_init(uint32_t* pixel_data, uint16_t image_width, uint16_t image_height); +void superposition_init(void); + +EXPORT(init) void init(uint32_t* pixel_data, uint16_t image_width, uint16_t image_height, uint16_t canvas_width, uint16_t canvas_height) { + width = canvas_width; + height = canvas_height; + + patterns_init(pixel_data, image_width, image_height); + superposition_init(); +} + +pattern_t pattern_extract(uint32_t* pixel_data, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t batch) { + pattern_t pattern = {0}; + + for (uint8_t i = 0; i < PATTERN_SIZE * PATTERN_SIZE; i++) { + int8_t dx = (i % PATTERN_SIZE) - (PATTERN_SIZE / 2); + int8_t dy = (i / PATTERN_SIZE) - (PATTERN_SIZE / 2); + + int8_t tmp; + + if (batch == 1) { // horizontal flip + dx = PATTERN_SIZE - dx; + } else if (batch == 2) { // vertical flip + dy = PATTERN_SIZE - dy; + } else if (batch == 3) { // both flips + dx = PATTERN_SIZE - dx; + dy = PATTERN_SIZE - dy; + } else if (batch == 4) { // rotate 90 + tmp = dx; + dx = dy; + dy = PATTERN_SIZE - tmp; + } else if (batch == 5) { // rotate 270 + tmp = dy; + dy = dx; + dx = PATTERN_SIZE - tmp; + } else if (batch == 6) { // flip main axis + tmp = dy; + dy = dx; + dx = tmp; + } else if (batch == 7) { // flip secondary axis + tmp = dy; + dy = PATTERN_SIZE - dx; + dx = PATTERN_SIZE - tmp; + } + + uint16_t xx = (x + dx + width) % width; + uint16_t yy = (y + dy + height) % height; + + uint32_t idx = xx + yy * width; + pattern.neighbours[i] = pixel_data[idx]; + } + return pattern; +} + +bool pattern_check(pattern_t pattern) { + for (uint32_t i = 0; i < num_patterns; i++) { + if (memcmp(&pattern, &patterns[i], sizeof(pattern)) == 0) { + return false; // found duplicate + } + } + return true; +} + +void patterns_init(uint32_t* pixel_data, uint16_t image_width, uint16_t image_height) { + uint32_t max_patterns = image_width * image_height * PATTERN_BATCH; + patterns = malloc(max_patterns * sizeof(*patterns)); + + num_patterns = 0; + + for (uint16_t y = 0; y < image_height; y++) { + for (uint16_t x = 0; x < image_width; x++) { + for (uint8_t batch = 0; batch < PATTERN_BATCH; batch++) { + pattern_t pattern = pattern_extract(pixel_data, x, y, image_width, image_height, batch); + if (pattern_check(pattern)) { + patterns[num_patterns++] = pattern; + } + } + } + } + + print_int(num_patterns); + print_int(max_patterns); +} + +void superposition_init(void) { + pixels = malloc(width * height * sizeof(*pixels)); + superpositions = malloc(width * height * sizeof(*superpositions)); + + for (uint32_t i = 0; i < width * height; i++) { + superposition_t superposition = { + .num_possibilities = num_patterns, + .possibilities = malloc(num_patterns * sizeof(uint32_t)), + }; + + for (uint32_t p = 0; p < num_patterns; p++) { + superposition.possibilities[p] = p; + } + + superpositions[i] = superposition; + } +} + +bool pattern_match(pattern_t a, pattern_t b, direction_t direction) { + switch (direction) { + case NORTH: + for (uint8_t x = 0; x < PATTERN_SIZE; x++) { + if (a.neighbours[x] != b.neighbours[PATTERN_SIZE * (PATTERN_SIZE - 1) + x]) { + return false; + } + } + return true; + case EAST: + for (uint8_t y = 0; y < PATTERN_SIZE; y++) { + if (a.neighbours[y * PATTERN_SIZE + PATTERN_SIZE - 1] != b.neighbours[y * PATTERN_SIZE]) { + return false; + } + } + return true; + case SOUTH: + for (uint8_t x = 0; x < PATTERN_SIZE; x++) { + if (a.neighbours[PATTERN_SIZE * (PATTERN_SIZE - 1) + x] != b.neighbours[x]) { + return false; + } + } + return true; + case WEST: + for (uint8_t y = 0; y < PATTERN_SIZE; y++) { + if (a.neighbours[y * PATTERN_SIZE] != b.neighbours[y * PATTERN_SIZE + PATTERN_SIZE - 1]) { + return false; + } + } + return true; + default: + return false; + } +} + +void superposition_remove(superposition_t* superposition, uint32_t pattern) { + uint32_t i; + for (i = 0; i < superposition->num_possibilities; i++) { + if (superposition->possibilities[i] == pattern) + break; + } + + if (i < superposition->num_possibilities) { + for (; i < superposition->num_possibilities - 1; i++) { + superposition->possibilities[i] = superposition->possibilities[i + 1]; + } + superposition->num_possibilities--; + } +} + +void superpositions_draw(void) { + for (uint32_t i = 0; i < width * height; i++) { + uint64_t sum = 0; + superposition_t superposition = superpositions[i]; + for (uint32_t p = 0; p < superposition.num_possibilities; p++) { + sum += patterns[superposition.possibilities[p]].neighbours[(PATTERN_SIZE * PATTERN_SIZE) / 2]; + } + if (superposition.num_possibilities > 0) { + pixels[i] = sum / superposition.num_possibilities; + } else { + pixels[i] = 0; + } + } +} + +void superpositions_propagate(void) { + // TODO +} + +void superposition_collapse(superposition_t* superposition) { + if (superposition->num_possibilities > 1) { + uint32_t choice = rand() % superposition->num_possibilities; + superposition->possibilities[0] = superposition->possibilities[choice]; + superposition->num_possibilities = 1; + + superpositions_propagate(); + } +} + +EXPORT(step) void step(void) { + superposition_collapse(&superpositions[420]); + superpositions_draw(); +} + +EXPORT(getPixels) uint32_t* get_pixels(void) { return pixels; } diff --git a/src/types.d.ts b/src/types.d.ts index 8237dc1..cb926eb 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -48,6 +48,8 @@ declare module "*.c" { const instance: { memory: WebAssembly.Memory; data: DataView; + malloc: (size: number) => number; + free: (ptr: number) => void; [x: string]: (...args: any) => any; }; @@ -57,6 +59,8 @@ declare module "*.cpp" { const instance: { memory: WebAssembly.Memory; data: DataView; + malloc: (size: number) => void; + free: (ptr: number) => void; [x: string]: (...args: any) => any; };