Skip to content

Instantly share code, notes, and snippets.

Created August 1, 2014 07:29
// img++ -std=c++11 -Wall main.cpp -O3 -lSDL2 -o langton && ./langton
#include <array>
#include <bitset>
#include <cassert>
#include <random>
#include <algorithm>
#include <iterator>
#include <iostream>
#include <SDL2/SDL.h>
using namespace std;
const int scale = 1;
const int WIDTH = 800/scale, HEIGHT = 800/scale;
array<uint32_t, WIDTH*HEIGHT> img;
array<uint64_t, WIDTH*HEIGHT> cnt;
array<char, WIDTH*HEIGHT> states;
SDL_Texture *tex = nullptr;
SDL_Renderer *renderer = nullptr;
SDL_Window *win = nullptr;
static array<uint32_t, 16> colors = {{
0x008000, 0x800080, 0x008080, 0x000080,
0xC0C0C0, 0x808080, 0x800000, 0x808000,
0x000000, 0xFFFFFF, 0xFF0000, 0x00FF00,
0x0000FF, 0xFFFF00, 0x00FFFF, 0xFF00FF,}};
enum ERenderState {STATES, HEAT};
ERenderState render_state = STATES;
int DX[] = { 0, 1, 0, -1};
int DY[] = { -1, 0, 1, 0};
string rule = "LLRRLL";
int max_state;
int x = WIDTH/2, y = HEIGHT/2, d = 0;
void shuffle_colors() {
static random_device rd;
static mt19937 g(rd());
shuffle(colors.begin(), colors.end(), g);
}
double interpolate( double val, double y0, double x0, double y1, double x1 ) {
return (val-x0)*(y1-y0)/(x1-x0) + y0;
}
double base( double val ) {
if ( val <= -0.75 ) return 0;
else if ( val <= -0.25 ) return interpolate( val, 0.0, -0.75, 1.0, -0.25 );
else if ( val <= 0.25 ) return 1.0;
else if ( val <= 0.75 ) return interpolate( val, 1.0, 0.25, 0.0, 0.75 );
else return 0.0;
}
double red( double gray ) {
return base( gray - 0.5 );
}
double green( double gray ) {
return base( gray );
}
double blue( double gray ) {
return base( gray + 0.5 );
}
#define GRADIENT(s) ((int)(red(s) * 255) << 16) | ((int)(green(s) * 255) << 8) | ((int)(blue(s) * 255))
void render() {
if (render_state == STATES) {
for(int idx = 0; idx < WIDTH * HEIGHT; idx++)
img[idx] = colors[states[idx]];
} else if (render_state == HEAT) {
int max_cnt = 0;
for(int idx = 0; idx < WIDTH * HEIGHT; idx++) {
// aging
cnt[idx] *= .99;
max_cnt = max_cnt < cnt[idx] ? cnt[idx] : max_cnt;
}
double maxf = log(1 + max_cnt);
for(int idx = 0; idx < WIDTH * HEIGHT; idx++) {
double s = log(1 + cnt[idx]) / maxf;
assert(s >= 0);
assert(s <= 1.0);
img[idx] = GRADIENT( s );
}
// printf("max cnt = %d\n", max_cnt);
}
// draw
SDL_UpdateTexture(tex, NULL, img.data(), WIDTH * sizeof(uint32_t));
SDL_Rect rect = {0,0, scale * WIDTH, scale * HEIGHT};
SDL_RenderCopy(renderer, tex, NULL, &rect);
}
int main(int argc, char **argv) {
if (0 != SDL_Init(SDL_INIT_EVERYTHING) ) exit(42);
SDL_CreateWindowAndRenderer(scale * WIDTH, scale * HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL, &win, &renderer);
if(nullptr == win || nullptr == renderer) exit(42);
for(auto& s : states) s = 0;
for(auto& c : cnt) c = 0;
for(auto& c : img) c = colors[0];
tex = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
SDL_UpdateTexture(tex, NULL, img.data(), WIDTH * sizeof(uint32_t));
if(argc > 1) rule = string(argv[1]);
max_state = rule.length() < colors.size() ? rule.length() : colors.size();
int generations_pr_frame = 1 << 26;
if (argc > 2) generations_pr_frame = atoi(argv[2]);
// build transistion table
// (DIR << 4) + state => DIR
char table[4 * 16];
for(int dir = 0; dir < 4; dir++) {
for(int state = 0; state < max_state; state++) {
unsigned char tmp = state; // weird unsigned promoting bugs
unsigned char S = (dir << 4) + tmp;
unsigned char next_dir = dir; // weird unsigned promoting bugs
if ('R' == rule[state]) {
next_dir = next_dir == 3 ? 0 : next_dir + 1;
} else {
next_dir = next_dir == 0 ? 3 : next_dir - 1;
}
table[S] = next_dir;
}
}
char NS[16];
for(int i = 0; i < 16; i++) NS[i] = i+1;
NS[max_state-1] = 0;
SDL_Event e;
float fps = 10.0;
unsigned long generation = 0;
auto start_time = SDL_GetTicks();
while(1) {
for(int i = 0; i < generations_pr_frame; i++) {
int idx = y * WIDTH + x;
// register
cnt[idx]++;
char s = states[idx];
// turn
d = table[s + (d << 4)];
// flip
states[idx] = NS[s];
// move
x += DX[d];
y += DY[d];
// wrap borders
if (x < 0) x += WIDTH;
else if (x >= WIDTH) x = 0;
if (y < 0) y += HEIGHT;
else if (y >= HEIGHT) y = 0;
}
generation += generations_pr_frame;
// calc FPS
auto end_time = SDL_GetTicks();
auto dt = end_time - start_time;
// limit FPS
while (dt < 10) {
SDL_Delay(10);
end_time = SDL_GetTicks();
dt = end_time - start_time;
}
fps = fps * .9 + .1 * (1000.0 / dt);
start_time = end_time;
// update texture
render();
// check input
while (SDL_PollEvent(&e)){
if (e.type == SDL_QUIT || SDLK_q == e.key.keysym.sym) exit(0);
if (SDLK_UP == e.key.keysym.sym) generations_pr_frame *= 2;
if (SDLK_DOWN == e.key.keysym.sym) generations_pr_frame /= 2;
if (SDLK_h == e.key.keysym.sym) render_state = HEAT;
if (SDLK_s == e.key.keysym.sym) render_state = STATES;
if (SDLK_c == e.key.keysym.sym) shuffle_colors();
generations_pr_frame = generations_pr_frame > 1 ? generations_pr_frame : 1;
}
// display
char buf[512];
sprintf(buf, "ANT:%s %lu generation (%3d ms, %.2f fps) %d gpf",
rule.c_str(), generation, dt, fps, generations_pr_frame);
SDL_SetWindowTitle(win, buf);
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(win);
SDL_Quit();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment