diff --git a/lib/prick_sv.h b/lib/prick_sv.h index d360701..08f3ef6 100644 --- a/lib/prick_sv.h +++ b/lib/prick_sv.h @@ -1,12 +1,12 @@ -/* prick_sv.h: String Views. +/* sv.h: String Views. * Created: 2026-03-01 * Author: Aryadev Chavali * License: See end of file * Commentary: To utilise this library, please put: - #define PRICK_SV_IMPL - #include "prick_sv.h" + #define SV_IMPL + #include "sv.h" in one of your code units. This is a simple read-only string view library. It defines some extremely @@ -14,8 +14,8 @@ require allocation. */ -#ifndef PRICK_SV_H -#define PRICK_SV_H +#ifndef SV_H +#define SV_H #include @@ -23,79 +23,78 @@ typedef struct { uint64_t size; const char *data; -} prick_sv_t; +} sv_t; -#define PRICK_SV(DATA, SIZE) ((prick_sv_t){.data = (DATA), .size = (SIZE)}) -#define PRICK_SV_AUTO(DATA) \ - ((prick_sv_t){.data = (void *)(DATA), .size = sizeof(DATA) - 1}) +#define SV(DATA, SIZE) ((sv_t){.data = (DATA), .size = (SIZE)}) +#define SV_AUTO(DATA) ((sv_t){.data = (void *)(DATA), .size = sizeof(DATA) - 1}) // Pretty printers -#define PRICK_SV_FMT(SV) (int)(SV).size, (SV).data -#define PR_PRICK_SV "%.*s" +#define SV_FMT(SV) (int)(SV).size, (SV).data +#define PR_SV "%.*s" -prick_sv_t prick_sv_chop_left(prick_sv_t, uint64_t size); -prick_sv_t prick_sv_chop_right(prick_sv_t, uint64_t size); -prick_sv_t prick_sv_truncate(prick_sv_t, uint64_t newsize); -prick_sv_t prick_sv_substr(prick_sv_t, uint64_t position, uint64_t size); +sv_t sv_chop_left(sv_t, uint64_t size); +sv_t sv_chop_right(sv_t, uint64_t size); +sv_t sv_truncate(sv_t, uint64_t newsize); +sv_t sv_substr(sv_t, uint64_t position, uint64_t size); -prick_sv_t prick_sv_till(prick_sv_t, const char *reject); -prick_sv_t prick_sv_while(prick_sv_t, const char *accept); +sv_t sv_till(sv_t, const char *reject); +sv_t sv_while(sv_t, const char *accept); -#ifdef PRICK_SV_IMPL +#ifdef SV_IMPL #include #include -prick_sv_t prick_sv_chop_left(prick_sv_t sv, uint64_t size) +sv_t sv_chop_left(sv_t sv, uint64_t size) { if (sv.size <= size) - return PRICK_SV(NULL, 0); - return PRICK_SV(sv.data + size, sv.size - size); + return SV(NULL, 0); + return SV(sv.data + size, sv.size - size); } -prick_sv_t prick_sv_chop_right(prick_sv_t sv, uint64_t size) +sv_t sv_chop_right(sv_t sv, uint64_t size) { if (sv.size <= size) - return PRICK_SV(NULL, 0); - return PRICK_SV(sv.data, sv.size - size); + return SV(NULL, 0); + return SV(sv.data, sv.size - size); } -prick_sv_t prick_sv_truncate(prick_sv_t sv, uint64_t newsize) +sv_t sv_truncate(sv_t sv, uint64_t newsize) { if (newsize > sv.size) - return PRICK_SV(NULL, 0); - return PRICK_SV(sv.data, newsize); + return SV(NULL, 0); + return SV(sv.data, newsize); } -prick_sv_t prick_sv_substr(prick_sv_t sv, uint64_t position, uint64_t size) +sv_t sv_substr(sv_t sv, uint64_t position, uint64_t size) { - prick_sv_t result = prick_sv_truncate(prick_sv_chop_left(sv, position), size); + sv_t result = sv_truncate(sv_chop_left(sv, position), size); return result; } -prick_sv_t prick_sv_till(prick_sv_t sv, const char *reject) +sv_t sv_till(sv_t sv, const char *reject) { if (sv.size == 0 || !sv.data) - return PRICK_SV(NULL, 0); + return SV(NULL, 0); uint64_t offset; for (offset = 0; offset < sv.size && strchr(reject, sv.data[offset]) == NULL; ++offset) continue; - return prick_sv_truncate(sv, offset); + return sv_truncate(sv, offset); } -prick_sv_t prick_sv_while(prick_sv_t sv, const char *accept) +sv_t sv_while(sv_t sv, const char *accept) { if (sv.size == 0 || !sv.data) - return PRICK_SV(NULL, 0); + return SV(NULL, 0); uint64_t offset; for (offset = 0; offset < sv.size && strchr(accept, sv.data[offset]) != NULL; ++offset) continue; - return prick_sv_truncate(sv, offset); + return sv_truncate(sv, offset); } #endif diff --git a/lib/prick_vec.h b/lib/prick_vec.h index 0b88494..9a15353 100644 --- a/lib/prick_vec.h +++ b/lib/prick_vec.h @@ -30,8 +30,8 @@ #include #include -#define PRICK_VEC_INLINE_CAPACITY 32 -#define PRICK_VEC_MULT 2 +#define VEC_INLINE_CAPACITY 32 +#define VEC_MULT 2 typedef struct { @@ -40,48 +40,47 @@ typedef struct union { void *ptr; - alignas(max_align_t) uint8_t inlined[PRICK_VEC_INLINE_CAPACITY]; + alignas(max_align_t) uint8_t inlined[VEC_INLINE_CAPACITY]; }; -} prick_vec_t; +} vec_t; -static_assert(sizeof(prick_vec_t) == 64, - "Expected sizeof(prick_vec_t) to be 64"); +static_assert(sizeof(vec_t) == 64, "Expected sizeof(vec_t) to be 64"); -void prick_vec_append(prick_vec_t *vec, const void *const ptr, uint64_t size); -void prick_vec_append_byte(prick_vec_t *vec, uint8_t byte); -void *prick_vec_data(prick_vec_t *vec); -void prick_vec_ensure_capacity(prick_vec_t *vec, uint64_t capacity); -void prick_vec_ensure_free(prick_vec_t *vec, uint64_t size); -void prick_vec_free(prick_vec_t *vec); -void prick_vec_clone(prick_vec_t *v2, prick_vec_t *v1); +void vec_append(vec_t *vec, const void *const ptr, uint64_t size); +void vec_append_byte(vec_t *vec, uint8_t byte); +void *vec_data(vec_t *vec); +void vec_ensure_capacity(vec_t *vec, uint64_t capacity); +void vec_ensure_free(vec_t *vec, uint64_t size); +void vec_free(vec_t *vec); +void vec_clone(vec_t *v2, vec_t *v1); -#define PRICK_VEC_GET(VEC, INDEX, TYPE) (((TYPE *)prick_vec_data(VEC))[INDEX]) +#define VEC_GET(VEC, INDEX, TYPE) (((TYPE *)vec_data(VEC))[INDEX]) -#ifdef PRICK_VEC_IMPL +#ifdef VEC_IMPL #define MAX(A, B) ((A) > (B) ? (A) : (B)) #include #include -void prick_vec_append(prick_vec_t *vec, const void *const ptr, uint64_t size) +void vec_append(vec_t *vec, const void *const ptr, uint64_t size) { if (!vec || !ptr || !size) return; - prick_vec_ensure_free(vec, size); - memcpy(&PRICK_VEC_GET(vec, vec->size, uint8_t), ptr, size); + vec_ensure_free(vec, size); + memcpy(&VEC_GET(vec, vec->size, uint8_t), ptr, size); vec->size += size; } -void prick_vec_append_byte(prick_vec_t *vec, uint8_t byte) +void vec_append_byte(vec_t *vec, uint8_t byte) { if (!vec) return; - prick_vec_ensure_free(vec, 1); - PRICK_VEC_GET(vec, vec->size, uint8_t) = byte; + vec_ensure_free(vec, 1); + VEC_GET(vec, vec->size, uint8_t) = byte; ++vec->size; } -void *prick_vec_data(prick_vec_t *vec) +void *vec_data(vec_t *vec) { if (!vec) return NULL; @@ -96,15 +95,15 @@ void *prick_vec_data(prick_vec_t *vec) } } -void prick_vec_ensure_capacity(prick_vec_t *vec, uint64_t capacity) +void vec_ensure_capacity(vec_t *vec, uint64_t capacity) { if (!vec) return; if (vec->capacity == 0) - vec->capacity = PRICK_VEC_INLINE_CAPACITY; + vec->capacity = VEC_INLINE_CAPACITY; if (vec->capacity < capacity) { - vec->capacity = MAX(vec->capacity * PRICK_VEC_MULT, capacity); + vec->capacity = MAX(vec->capacity * VEC_MULT, capacity); if (!vec->not_inlined) { // We were a small buffer, and now we cannot be i.e. we need to allocate @@ -123,14 +122,14 @@ void prick_vec_ensure_capacity(prick_vec_t *vec, uint64_t capacity) } } -void prick_vec_ensure_free(prick_vec_t *vec, uint64_t size) +void vec_ensure_free(vec_t *vec, uint64_t size) { if (!vec) return; - prick_vec_ensure_capacity(vec, vec->size + size); + vec_ensure_capacity(vec, vec->size + size); } -void prick_vec_free(prick_vec_t *vec) +void vec_free(vec_t *vec) { if (!vec) return; @@ -139,11 +138,11 @@ void prick_vec_free(prick_vec_t *vec) memset(vec, 1, sizeof(*vec)); } -void prick_vec_clone(prick_vec_t *v2, prick_vec_t *v1) +void vec_clone(vec_t *v2, vec_t *v1) { if (!v1 || !v2) return; - prick_vec_append(v2, prick_vec_data(v1), v1->size); + vec_append(v2, vec_data(v1), v1->size); } #undef MAX diff --git a/main.c b/main.c index d7c5c8f..3325499 100644 --- a/main.c +++ b/main.c @@ -6,19 +6,319 @@ #include #include +#include +#include +#include + +#include "./lib/prick_aliases.h" + +#define SV_IMPL +#include "./lib/prick_sv.h" + +#define VEC_IMPL +#include "./lib/prick_vec.h" + +#define SAFE_SUB(A, B) ((A) < (B) ? 0 : (A) - (B)) + +#define SIZEOF_PROGRAM (1LU << 6) +struct ProgramConcat +{ + sv_t A, B; + u8 tape[SIZEOF_PROGRAM * 2]; +}; + +void program_concat(struct ProgramConcat *ret, sv_t a, sv_t b) +{ + assert(a.size == SIZEOF_PROGRAM && b.size == SIZEOF_PROGRAM); + memset(ret, 0, sizeof(*ret)); + ret->A = a; + ret->B = b; + memcpy(ret->tape, a.data, SIZEOF_PROGRAM); + memcpy(ret->tape + SIZEOF_PROGRAM, b.data, SIZEOF_PROGRAM); +} + +u64 vec_pop(vec_t *vec) +{ + u64 ret = 0; + if (vec->size < sizeof(ret)) + return ret; + vec->size -= sizeof(ret); + memcpy(&ret, (typeof(ret) *)(((u8 *)vec_data(vec)) + vec->size), sizeof(ret)); + return ret; +} + +bool vec_in(vec_t *vec, u64 n) +{ + for (u64 i = 0; i < vec->size / sizeof(n); ++i) + { + if (VEC_GET(vec, i, typeof(n)) == n) + { + return true; + } + } + return false; +} + +void program_execute(struct ProgramConcat *prg) +{ + vec_t cond_stack = {0}; + vec_ensure_capacity(&cond_stack, sizeof(prg->tape) * sizeof(u64)); + + for (u64 ip = 0, head0 = 0, head1 = 0, total_iters = 0; + ip < sizeof(prg->tape) && total_iters < (1LU << 13); ++total_iters) + { + u8 opcode = prg->tape[ip]; + switch (opcode) + { + case '<': + head0 = SAFE_SUB(head0, 1); + ++ip; + break; + case '>': + head0++; + ++ip; + break; + case '{': + head1 = SAFE_SUB(head1, 1); + ++ip; + break; + case '}': + head1++; + ++ip; + break; + case '-': + prg->tape[head0]--; + ++ip; + break; + case '+': + prg->tape[head0]++; + ++ip; + break; + case '.': + prg->tape[head1] = prg->tape[head0]; + ++ip; + break; + case ',': + prg->tape[head0] = prg->tape[head1]; + ++ip; + break; + case '[': + { + if (!vec_in(&cond_stack, ip)) + { + vec_append(&cond_stack, &ip, sizeof(ip)); + } + if (!prg->tape[head0]) + { + // Iterate forward, trying to find a matching closed bracket + u64 square_brackets = 0; + u64 close_ip; + for (close_ip = ip + 1; close_ip < sizeof(prg->tape); ++close_ip) + { + if (prg->tape[close_ip] == '[') + { + ++square_brackets; + } + else if (prg->tape[close_ip] == ']') + { + if (square_brackets == 0) + { + break; + } + --square_brackets; + } + } + if (square_brackets != 0) + { + // NOTE: as per paper, terminate. + ip = sizeof(prg->tape); + } + else + { + ip = close_ip; + } + } + break; + } + case ']': + { + if (prg->tape[head0]) + { + if (cond_stack.size < sizeof(u64)) + { + // NOTE: as per paper, terminate. + ip = sizeof(prg->tape); + } + else + { + ip = vec_pop(&cond_stack); + } + } + else + { + ++ip; + } + break; + } + default: + ++ip; + break; + } + } + + vec_free(&cond_stack); +} + +void program_split(struct ProgramConcat *prg) +{ + assert(prg->A.data && prg->B.data); + memcpy((char *)prg->A.data, prg->tape, SIZEOF_PROGRAM); + memcpy((char *)prg->B.data, prg->tape + SIZEOF_PROGRAM, SIZEOF_PROGRAM); +} #define WIDTH 800 #define HEIGHT 600 +#define NUM_PROGRAMS_POW_2 10 +#define NUM_PROGRAMS (1LU << NUM_PROGRAMS_POW_2) +#define SIMULATION_SIZE (SIZEOF_PROGRAM * NUM_PROGRAMS) +struct Simulation +{ + char buffer[SIMULATION_SIZE]; + u64 p1, p2; +}; + +void simulation_init(struct Simulation *sim) +{ + for (u64 i = 0; i < SIMULATION_SIZE / sizeof(u16); ++i) + { + ((u16 *)(sim->buffer))[i] = rand() % UINT16_MAX; + } +} + +void simulation_pick(struct Simulation *sim) +{ + sim->p1 = rand() % (SIMULATION_SIZE / SIZEOF_PROGRAM); + sim->p2 = rand() % (SIMULATION_SIZE / SIZEOF_PROGRAM); + while (sim->p1 * 8 <= ((sim->p2 * 8) + SIZEOF_PROGRAM) && + sim->p2 * 8 <= ((sim->p1 * 8) + SIZEOF_PROGRAM)) + { + sim->p2 = rand() % (SIMULATION_SIZE / SIZEOF_PROGRAM); + } +} + +void simulation_update(struct Simulation *sim) +{ + sv_t a = SV(sim->buffer + (sim->p1 * SIZEOF_PROGRAM), 64); + sv_t b = SV(sim->buffer + (sim->p2 * SIZEOF_PROGRAM), 64); + + struct ProgramConcat prog_concat = {0}; + program_concat(&prog_concat, a, b); + program_execute(&prog_concat); + program_split(&prog_concat); +} + +Color simulation_cell_color(const u8 *program) +{ + // How do we compute a "colour" for a program? I say we count all the valid + // opcodes in the program. These counts are used as weights for 10 distinct + // colours. + const Vector4 bases[] = { + ['<'] = ColorNormalize(ColorFromHSV(0.121, 0.467, 0.706)), + ['>'] = ColorNormalize(ColorFromHSV(1.000, 0.498, 0.055)), + ['{'] = ColorNormalize(ColorFromHSV(0.173, 0.627, 0.173)), + ['}'] = ColorNormalize(ColorFromHSV(0.839, 0.153, 0.157)), + ['-'] = ColorNormalize(ColorFromHSV(0.580, 0.404, 0.741)), + ['+'] = ColorNormalize(ColorFromHSV(0.549, 0.337, 0.294)), + ['.'] = ColorNormalize(ColorFromHSV(0.890, 0.467, 0.761)), + [','] = ColorNormalize(ColorFromHSV(0.498, 0.498, 0.498)), + ['['] = ColorNormalize(ColorFromHSV(0.737, 0.741, 0.133)), + [']'] = ColorNormalize(ColorFromHSV(0.090, 0.745, 0.812)), + }; + + static const char *VALID_OPS = "<>{}-+.,[]"; + + u64 counter[] = { + ['<'] = 0, ['>'] = 0, ['{'] = 0, ['}'] = 0, ['-'] = 0, + ['+'] = 0, ['.'] = 0, [','] = 0, ['['] = 0, [']'] = 0, + }; + + u64 total_valid = 0; + for (u64 i = 0; i < SIZEOF_PROGRAM; ++i) + { + if (strchr(VALID_OPS, program[i])) + { + counter[(u64)program[i]]++; + ++total_valid; + } + } + + if (total_valid == 0) + return BLACK; + + f64 colour_cells[3]; + for (const char *ptr = VALID_OPS; *ptr; ++ptr) + { + colour_cells[0] += bases[(u64)*ptr].x; + colour_cells[1] += bases[(u64)*ptr].y; + colour_cells[2] += bases[(u64)*ptr].z; + } + colour_cells[0] /= total_valid; + colour_cells[1] /= total_valid; + colour_cells[2] /= total_valid; + + return (Color){.r = 255 * colour_cells[0], + .g = 255 * colour_cells[1], + .b = 255 * colour_cells[2], + .a = 255}; +} + +void simulation_draw(struct Simulation *sim) +{ + // Our grid will be of lengths sqrt(NUM_PROGRAMS) == 1 << + // (NUM_PROGRAMS_POW_2/2). + const size_t GRID_WIDTH = 1LU << (NUM_PROGRAMS_POW_2 / 2); + const size_t CELL_WIDTH = WIDTH / GRID_WIDTH; + + sv_t sv = SV(sim->buffer, SIMULATION_SIZE); + + for (u64 i = 0; i < SIMULATION_SIZE / SIZEOF_PROGRAM; ++i) + { + sv_t program = sv_truncate(sv, SIZEOF_PROGRAM); + + Color color = simulation_cell_color((const u8 *)program.data); + + u64 x = i / GRID_WIDTH; + u64 y = i % GRID_WIDTH; + DrawRectangle(x * CELL_WIDTH, y * CELL_WIDTH, CELL_WIDTH, CELL_WIDTH, + color); + + if (i == sim->p1 || i == sim->p2) + { + DrawRectangleLines(x * CELL_WIDTH, y * CELL_WIDTH, CELL_WIDTH, CELL_WIDTH, + BLUE); + } + + sv = sv_chop_left(sv, 64); + } +} + int main(void) { + srand(time(NULL)); + + struct Simulation sim = {0}; + simulation_init(&sim); + InitWindow(WIDTH, HEIGHT, "CompLife"); SetTargetFPS(60); - while (!WindowShouldClose()) + for (size_t ticks = 0; !WindowShouldClose(); ++ticks) { + simulation_pick(&sim); + simulation_update(&sim); BeginDrawing(); ClearBackground(BLACK); - DrawFPS(0, 0); + simulation_draw(&sim); EndDrawing(); } CloseWindow();