neighbour based algorithm for threads

It does seem to affect since sim falls into a "stable" configuration
where cells rarely change quite quickly.  I'm thinking of making a
better colouring algorithm to see if I can get better images.
This commit is contained in:
2026-03-10 22:50:38 +00:00
parent 0e1229569a
commit 0bba1894dc
4 changed files with 63 additions and 25 deletions

View File

@@ -30,6 +30,18 @@ typedef double f64;
// 64 byte programs
#define SIZEOF_PROGRAM (1LU << 6)
#define NUM_PROGRAMS_POW_2 17
#define NUM_PROGRAMS (1LU << NUM_PROGRAMS_POW_2)
#define SIMULATION_SIZE (SIZEOF_PROGRAM * NUM_PROGRAMS)
#define THREAD_POOL 4
// GUI stuff
#define WIDTH 800
#define HEIGHT 600
// Our grid will be of length sqrt(NUM_PROGRAMS). This is the same as
// 1LU<<(NUM_PROGRAMS_POW_2/2).
static const size_t GRID_WIDTH = 1LU << (NUM_PROGRAMS_POW_2 / 2);
#endif
/* Copyright (C) 2026 Aryadev Chavali

View File

@@ -17,12 +17,6 @@
#include "program_iter.h"
#include "simulation.h"
#define WIDTH 800
#define HEIGHT 600
// Our grid will be of length sqrt(NUM_PROGRAMS). This is the same as
// 1LU<<(NUM_PROGRAMS_POW_2/2).
static const size_t GRID_WIDTH = 1LU << (NUM_PROGRAMS_POW_2 / 2);
static const size_t CELL_WIDTH = WIDTH / GRID_WIDTH;
static const char *VALID_OPS = "<>{}-+.,[]";

View File

@@ -21,33 +21,71 @@ bool any_threads_using(const struct ThreadState *const states, u64 n)
return false;
}
void get_neighbours(u64 index, u64 ret[4])
{
memset(ret, 0xFF, sizeof(*ret) * 4);
u64 x = index / GRID_WIDTH;
u64 y = index % GRID_WIDTH;
u64 ptr = 0;
if (x > 0)
{
ret[ptr++] = ((x - 1) * GRID_WIDTH) + y;
}
if (x < GRID_WIDTH - 1)
{
ret[ptr++] = ((x + 1) * GRID_WIDTH) + y;
}
if (y > 0)
{
ret[ptr++] = (x * GRID_WIDTH) + (y + 1);
}
if (y < GRID_WIDTH - 1)
{
ret[ptr++] = (x * GRID_WIDTH) + (y - 1);
}
}
void thread_pick(struct ThreadState *state)
{
struct Simulation *sim = state->sim;
u64 p1 = 0, p2 = 0;
while (true)
{
u64 p1 = rand() % (SIMULATION_SIZE / SIZEOF_PROGRAM);
p1 = rand() % (SIMULATION_SIZE / SIZEOF_PROGRAM);
if (any_threads_using(sim->states, p1))
{
continue;
}
// TODO: Instead of picking a RANDOM p2, why not choose a neighbour of p1?
// This way programs that are likely to replicate are replicating closer to
// themselves, and thus have a higher chance of further replication.
u64 p2 = rand() % (SIMULATION_SIZE / SIZEOF_PROGRAM);
while (p1 * 8 <= ((p2 * 8) + SIZEOF_PROGRAM) &&
p2 * 8 <= ((p1 * 8) + SIZEOF_PROGRAM))
u64 neighbours[4];
get_neighbours(p1, neighbours);
u64 p2 = -1;
for (u64 i = 0; i < 4; ++i)
{
if (neighbours[i] == UINT64_MAX ||
any_threads_using(sim->states, neighbours[i]))
continue;
p2 = neighbours[i];
break;
}
if (p2 < GRID_WIDTH)
{
break;
}
// Otherwise pick randomly.
p2 = rand() % (SIMULATION_SIZE / SIZEOF_PROGRAM);
while ((p1 * 8 <= ((p2 * 8) + SIZEOF_PROGRAM) &&
p2 * 8 <= ((p1 * 8) + SIZEOF_PROGRAM)) ||
any_threads_using(sim->states, p2))
{
p2 = rand() % (SIMULATION_SIZE / SIZEOF_PROGRAM);
}
if (any_threads_using(sim->states, p2))
{
continue;
}
state->p1 = p1;
state->p2 = p2;
break;
}
state->p1 = p1;
state->p2 = p2;
}
const struct timespec THREAD_DEFAULT_SLEEP = {.tv_sec = 1};

View File

@@ -12,12 +12,6 @@
#include <stdatomic.h>
#include <threads.h>
#define NUM_PROGRAMS_POW_2 10
#define NUM_PROGRAMS (1LU << NUM_PROGRAMS_POW_2)
#define SIMULATION_SIZE (SIZEOF_PROGRAM * NUM_PROGRAMS)
#define THREAD_POOL 8
struct ThreadState
{
u64 p1, p2;