Created
August 1, 2014 07:29
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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