Not finished WFC
This commit is contained in:
parent
665892a798
commit
8a9b437ae7
|
|
@ -0,0 +1,3 @@
|
||||||
|
IndentWidth: 4
|
||||||
|
ColumnLimit: 240
|
||||||
|
PointerAlignment: Left
|
||||||
|
|
@ -103,8 +103,7 @@ bun run build
|
||||||
sudo apt install clang lld wabt
|
sudo apt install clang lld wabt
|
||||||
```
|
```
|
||||||
- Supports only function exports & `memory`
|
- Supports only function exports & `memory`
|
||||||
- exported all non-static functions
|
- No stdlib
|
||||||
- no stdlib
|
|
||||||
|
|
||||||
|
|
||||||
## Publishing
|
## Publishing
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
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);
|
||||||
|
|
@ -3,7 +3,7 @@ import { $ } from 'bun';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
import { buildHTML } from "./html";
|
import { buildHTML } from "./html";
|
||||||
import inquirer from 'inquirer';
|
import select from '@inquirer/select';
|
||||||
import { isGame, getGames } from './isGame';
|
import { isGame, getGames } from './isGame';
|
||||||
|
|
||||||
const outDir = path.resolve(import.meta.dir, '..', '..', 'dist');
|
const outDir = path.resolve(import.meta.dir, '..', '..', 'dist');
|
||||||
|
|
@ -13,12 +13,10 @@ let game = process.argv[2];
|
||||||
const publish = process.env.PUBLISH_LOCATION;
|
const publish = process.env.PUBLISH_LOCATION;
|
||||||
|
|
||||||
while (!await isGame(game)) {
|
while (!await isGame(game)) {
|
||||||
const answer = await inquirer.prompt([{
|
const game = await select({
|
||||||
type: 'list',
|
message: 'Game to build:',
|
||||||
name: 'game',
|
choices: (await getGames()).map(value => ({ value })),
|
||||||
choices: await getGames(),
|
});
|
||||||
}]);
|
|
||||||
game = answer.game;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const html = await buildHTML(game, true);
|
const html = await buildHTML(game, true);
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,34 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin
|
||||||
let wasmPath = path.resolve(import.meta.dir, '..', '..', 'dist', 'tmp.wasm');
|
let wasmPath = path.resolve(import.meta.dir, '..', '..', 'dist', 'tmp.wasm');
|
||||||
let jsContent: string = `
|
let jsContent: string = `
|
||||||
async function instantiate(url) {
|
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 {
|
return {
|
||||||
...instance.exports,
|
...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;
|
export default module;
|
||||||
`;
|
`;
|
||||||
if (args.path.endsWith('.ts')) {
|
if (args.path.endsWith('.ts')) {
|
||||||
|
|
@ -49,7 +70,11 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin
|
||||||
} else if (args.path.endsWith('.wasm')) {
|
} else if (args.path.endsWith('.wasm')) {
|
||||||
wasmPath = args.path;
|
wasmPath = args.path;
|
||||||
} else {
|
} 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) {
|
if (result.exitCode !== 0) {
|
||||||
throw new Error('Compile failed, check output');
|
throw new Error('Compile failed, check output');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
--target=wasm32
|
||||||
|
-fno-builtin
|
||||||
|
--no-standard-libraries
|
||||||
|
-I
|
||||||
|
build/assets/
|
||||||
|
-Wall
|
||||||
|
-Wextra
|
||||||
|
-Wpedantic
|
||||||
|
-Werror
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
"build": "bun build/build.ts"
|
"build": "bun build/build.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@inquirer/select": "2.3.10",
|
||||||
"classnames": "2.5.1",
|
"classnames": "2.5.1",
|
||||||
"preact": "10.22.0"
|
"preact": "10.22.0"
|
||||||
},
|
},
|
||||||
|
|
@ -18,7 +19,6 @@
|
||||||
"assemblyscript": "0.27.29",
|
"assemblyscript": "0.27.29",
|
||||||
"bun-lightningcss": "0.2.0",
|
"bun-lightningcss": "0.2.0",
|
||||||
"html-minifier": "4.0.0",
|
"html-minifier": "4.0.0",
|
||||||
"inquirer": "9.3.4",
|
|
||||||
"typescript": "5.5.2",
|
"typescript": "5.5.2",
|
||||||
"uglify-js": "3"
|
"uglify-js": "3"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { render } from "preact";
|
import { render } from "preact";
|
||||||
import type { Display } from ".";
|
|
||||||
import { clamp, range } from "@common/utils";
|
import { clamp, range } from "@common/utils";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
|
@ -18,7 +17,7 @@ export interface BrickDisplayImage {
|
||||||
height: number;
|
height: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BrickDisplay implements Display {
|
export class BrickDisplay {
|
||||||
#field: boolean[] = new Array(FIELD_HEIGHT * FIELD_WIDTH);
|
#field: boolean[] = new Array(FIELD_HEIGHT * FIELD_WIDTH);
|
||||||
#miniField: boolean[] = new Array(MINI_FIELD_HEIGHT * MINI_FIELD_WIDTH);
|
#miniField: boolean[] = new Array(MINI_FIELD_HEIGHT * MINI_FIELD_WIDTH);
|
||||||
#score: number = 0;
|
#score: number = 0;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
export interface Display {
|
|
||||||
init(): void;
|
|
||||||
update(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export { BrickDisplay, type BrickDisplayImage } from './brick';
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { BrickDisplay } from "@common/display";
|
import { BrickDisplay } from "@common/display/brick";
|
||||||
import { isPressed, updateKeys } from "@common/input";
|
import { isPressed, updateKeys } from "@common/input";
|
||||||
|
|
||||||
import spritesheetImage from './assets/spritesheet.png';
|
import spritesheetImage from './assets/spritesheet.png';
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
|
import { createCanvas } from "@common/display/canvas";
|
||||||
import life from "./life.c";
|
import life from "./life.c";
|
||||||
|
|
||||||
const width = life.getWidth();
|
const width = life.getWidth();
|
||||||
const height = life.getHeight();
|
const height = life.getHeight();
|
||||||
|
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = createCanvas(width, height);
|
||||||
const context = canvas.getContext('2d')!;
|
const context = canvas.getContext('2d')!;
|
||||||
const imageData = context.createImageData(width, height);
|
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);
|
const pixels = new Uint8Array(life.memory.buffer, life.getPixels(), width * height * 4);
|
||||||
|
|
||||||
export default function main() {
|
export default function main() {
|
||||||
document.body.append(canvas);
|
|
||||||
initField(Date.now());
|
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);
|
console.log(life, pixels.length);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,11 @@ void* malloc(uint32_t n);
|
||||||
static uint8_t rand8(void);
|
static uint8_t rand8(void);
|
||||||
static int countNeighbours(int x, int y);
|
static int countNeighbours(int x, int y);
|
||||||
|
|
||||||
int getWidth() { return width; }
|
int getWidth(void) { return width; }
|
||||||
int getHeight() { return height; }
|
int getHeight(void) { return height; }
|
||||||
uint8_t* getPixels() { return pixels; }
|
uint8_t* getPixels(void) { return pixels; }
|
||||||
|
|
||||||
void step()
|
void step(void)
|
||||||
{
|
{
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
for (int x = 0; x < width; x++) {
|
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 uint8_t rand8(void)
|
||||||
{
|
{
|
||||||
static uint16_t c = 0x42;
|
static uint16_t c = 0x42;
|
||||||
static int i = 0;
|
static uint32_t i = 0;
|
||||||
uint16_t t;
|
uint16_t t;
|
||||||
uint8_t x;
|
uint8_t x;
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 204 B |
|
|
@ -0,0 +1,43 @@
|
||||||
|
import wfc from './wfc.c';
|
||||||
|
import wfcImage from './assets/wfc.png';
|
||||||
|
import { createCanvas, getImageData } from "@common/display/canvas"
|
||||||
|
|
||||||
|
const width = 256;
|
||||||
|
const height = 256;
|
||||||
|
|
||||||
|
const canvas = createCanvas(width, height);
|
||||||
|
const context = canvas.getContext('2d')!;
|
||||||
|
const imageData = context.createImageData(width, height);
|
||||||
|
let pixels: Uint8ClampedArray;
|
||||||
|
|
||||||
|
export default function main() {
|
||||||
|
console.log(wfc);
|
||||||
|
|
||||||
|
const imageData = getImageData(wfcImage);
|
||||||
|
const dataPtr = wfc.malloc(imageData.data.length);
|
||||||
|
let buffer = new Uint8ClampedArray(wfc.memory.buffer, dataPtr, imageData.data.length);
|
||||||
|
buffer.set(imageData.data);
|
||||||
|
|
||||||
|
wfc.init(dataPtr, imageData.width, imageData.height, canvas.width, canvas.height);
|
||||||
|
pixels = new Uint8ClampedArray(wfc.memory.buffer, wfc.getPixels(), width * height * 4);
|
||||||
|
requestAnimationFrame(loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
let sum = 0;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
async function loop() {
|
||||||
|
const start = performance.now();
|
||||||
|
wfc.step();
|
||||||
|
const end = performance.now();
|
||||||
|
|
||||||
|
sum += end - start;
|
||||||
|
count++;
|
||||||
|
|
||||||
|
imageData.data.set(pixels);
|
||||||
|
|
||||||
|
context.putImageData(imageData, 0, 0);
|
||||||
|
context.clearRect(0, 0, 35, 15);
|
||||||
|
context.fillText(`${(sum / count).toFixed(1)} ms`, 2, 10);
|
||||||
|
requestAnimationFrame(loop);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,212 @@
|
||||||
|
#include <stdlib.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; }
|
||||||
|
|
@ -48,6 +48,8 @@ declare module "*.c" {
|
||||||
const instance: {
|
const instance: {
|
||||||
memory: WebAssembly.Memory;
|
memory: WebAssembly.Memory;
|
||||||
data: DataView;
|
data: DataView;
|
||||||
|
malloc: (size: number) => number;
|
||||||
|
free: (ptr: number) => void;
|
||||||
|
|
||||||
[x: string]: (...args: any) => any;
|
[x: string]: (...args: any) => any;
|
||||||
};
|
};
|
||||||
|
|
@ -57,6 +59,8 @@ declare module "*.cpp" {
|
||||||
const instance: {
|
const instance: {
|
||||||
memory: WebAssembly.Memory;
|
memory: WebAssembly.Memory;
|
||||||
data: DataView;
|
data: DataView;
|
||||||
|
malloc: (size: number) => void;
|
||||||
|
free: (ptr: number) => void;
|
||||||
|
|
||||||
[x: string]: (...args: any) => any;
|
[x: string]: (...args: any) => any;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue