Created
June 7, 2014 20:45
-
-
Save anonymous/dca5ea513ffccf83403e to your computer and use it in GitHub Desktop.
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
// g++ -std=c++11 main.cpp -O3 -lSDL2 -o magic && ./magic | |
#include <string.h> | |
#include <random> | |
#include <algorithm> | |
#include <cassert> | |
#include <SDL2/SDL.h> | |
using namespace std; | |
const unsigned int SAPLING = 1; | |
const unsigned int TREE = 2; | |
const unsigned int ELDER = 3; | |
struct cell_t { | |
unsigned int tree : 4; | |
unsigned int age : 22; | |
unsigned int lumberjack : 1; | |
unsigned int bear : 1; | |
unsigned int dirty : 1; | |
}__attribute__((packed)); // weee packed in two 32 bits | |
SDL_Color BG_col {255, 255, 0}; | |
SDL_Color Sapling_Col { 96, 255, 0}; | |
SDL_Color Tree_Col { 84, 184, 24}; | |
SDL_Color Elder_Col { 24, 124, 25}; | |
SDL_Color Bear_Col {146, 87, 3}; | |
SDL_Color Lumberjack_Col {229, 23, 37}; | |
const int BEAR_STEPS = 5; | |
const int LJ_STEPS = 2; | |
const int W = 1000, H = 800; | |
int N = 10; | |
cell_t *grid; | |
random_device rd; | |
mt19937 g(rd()); | |
uniform_real_distribution<float> randf(0,1); | |
uniform_int_distribution<int> randi(-1,1); | |
uniform_int_distribution<int> randdir(0,7); | |
// stats counters for current month | |
int mawings; | |
int planted; | |
int harvested; | |
// counters | |
int bears; | |
int saplings, trees, elders; | |
int lumberjacks; | |
// yearly | |
int yearly_mawings; | |
int yearly_planted; | |
int yearly_harvested; | |
// state | |
int current_month; | |
// helpers | |
int first_bear; | |
int first_lj; | |
void init() { | |
grid = new cell_t[N*N]; | |
memset(grid, '0', sizeof(cell_t) * N * N); | |
int idx = 0; | |
for(int i = 0; i < (10 * N*N / 100.0); i++) | |
grid[idx++].lumberjack = 1; | |
for(int i = 0; i < (50 * N*N / 100.0); i++) | |
grid[idx++].tree = TREE; | |
for(int i = 0; i < ( 2 * N*N / 100.0); i++) | |
grid[idx++].bear = 1; | |
shuffle(grid, grid + N*N, g); | |
yearly_harvested = 0; | |
yearly_mawings = 0; | |
yearly_planted = 0; | |
// dirty | |
for(int idx = 0; idx < N*N; idx++) grid[idx].dirty = 1; | |
} | |
void print_cell(const cell_t& c) { | |
if (c.bear) | |
printf("B"); | |
else if (c.lumberjack) | |
printf("L"); | |
else if (c.tree > 0 ) | |
printf("%d", c.tree); | |
else | |
printf(" "); | |
} | |
void scan_grid(bool render) { | |
// and count | |
lumberjacks = 0; bears = 0; trees = 0; saplings = 0; elders = 0; | |
for (int j = 0; j < N; j++) { | |
for (int i = 0; i < N; i++) { | |
auto &c = grid[j * N + i]; | |
if(render) | |
print_cell(c); | |
if(c.lumberjack) lumberjacks++; | |
if(SAPLING == c.tree) saplings++; | |
if(TREE == c.tree) trees++; | |
if(ELDER == c.tree) elders++; | |
if(c.bear) bears++; | |
} | |
if(render) | |
printf("\n"); | |
} | |
// if(render) | |
// printf("Bears: %4d\n" | |
// "Trees: %4d\n" | |
// "LJacks: %4d\n", bears,trees,lumberjacks); | |
yearly_harvested += harvested; | |
yearly_mawings += mawings; | |
yearly_planted += planted; | |
} | |
void display_stats() { | |
return; | |
if (mawings) | |
printf("[Month %04d]: [%d] Lumberjack(s) was Maw'd by a bear\n", current_month, mawings); | |
if (harvested) | |
printf("[Month %04d]: [%d] pieces of lumber harvested\n", current_month, harvested); | |
if (planted) | |
printf("[Month %04d]: [%d] saplings planted\n", current_month, planted); | |
} | |
void render_grid(SDL_Renderer *renderer) { | |
const float step = (1.0 * H) / (1.0 * N); assert(H < W); assert(N <= H); | |
SDL_Rect r { 0, 0, (int)step, (int)step}; | |
SDL_Color col; | |
int dirty = 0; | |
for (int j = 0; j < N; j++) { | |
for (int i = 0; i < N; i++) { | |
auto &c = grid[j * N + i]; | |
if (!c.dirty) continue; | |
dirty++; | |
r.y = (int) j * step; | |
r.x = (int) i * step; | |
// draw background | |
col = BG_col; | |
if(SAPLING == c.tree) col = Sapling_Col; | |
if(TREE == c.tree) col = Tree_Col; | |
if(ELDER == c.tree) col = Elder_Col; | |
SDL_SetRenderDrawColor(renderer, col.r, col.g, col.b, 255); | |
SDL_RenderFillRect(renderer, &r); | |
if (c. bear || c.lumberjack) { | |
if(c.bear) col = Bear_Col; | |
if(c.lumberjack) col = Lumberjack_Col; | |
SDL_SetRenderDrawColor(renderer, col.r, col.g, col.b, 155); | |
SDL_RenderFillRect(renderer, &r); | |
} | |
c.dirty = 0; | |
} | |
} | |
// printf("%.2f dirty\n", 100 * dirty/float(N*N)); | |
// a plot of sorts | |
col = {0,0,0}; | |
SDL_SetRenderDrawColor(renderer, col.r, col.g, col.b, 255); | |
SDL_RenderDrawLine(renderer, H, current_month %H, W, current_month %H); | |
col = Bear_Col; | |
SDL_SetRenderDrawColor(renderer, col.r, col.g, col.b, 255); | |
SDL_RenderDrawPoint(renderer, H + 200 * bears/(.25* N*N), current_month % H); | |
col = Sapling_Col; | |
SDL_SetRenderDrawColor(renderer, col.r, col.g, col.b, 255); | |
SDL_RenderDrawPoint(renderer, H + 200 * saplings/(.75* N*N) , current_month % H); | |
col = Tree_Col; | |
SDL_SetRenderDrawColor(renderer, col.r, col.g, col.b, 255); | |
SDL_RenderDrawPoint(renderer, H + 200 * trees/(.75* N*N) , current_month % H); | |
col = Elder_Col; | |
SDL_SetRenderDrawColor(renderer, col.r, col.g, col.b, 255); | |
SDL_RenderDrawPoint(renderer, H + 200 * elders/(.75* N*N) , current_month % H); | |
col = Lumberjack_Col; | |
SDL_SetRenderDrawColor(renderer, col.r, col.g, col.b, 255); | |
SDL_RenderDrawPoint(renderer, H + 200 * lumberjacks/(.25* N*N) , current_month % H); | |
// col = {255,255,0}; | |
// SDL_SetRenderDrawColor(renderer, col.r, col.g, col.b, 255); | |
// SDL_RenderDrawPoint(renderer, H + 200 * dirty/(N*N), current_month % H); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// SAMPLING // | |
//////////////////////////////////////////////////////////////////////////////// | |
vector<int> neighborhood(const int x, const int y, const int n) { | |
int xmin = max(x - n, 0); | |
int xmax = min(x + n, N-1); | |
int ymin = max(y - n, 0); | |
int ymax = min(y + n, N-1); | |
vector<int> res; | |
for (int j = ymin; j <= ymax; j++) { | |
for (int i = xmin; i <= xmax; i++) { | |
if ( i == j && i == 0) continue; | |
res.push_back( j * N + i); | |
} | |
} | |
return res; | |
} | |
SDL_Point directions[] = { {-1,-1}, {0, -1}, {1, -1}, | |
{-1, 0}, {1, 0}, | |
{-1, 1}, {0, 1}, {1, 1}, }; | |
int sample_pos(int x, int y) { | |
int i, j; | |
// do { | |
auto p = directions[randdir(g)]; | |
i = x + p.x; | |
j = y + p.y; | |
// } while (i < 0 || j< 0 || i >= N || j >= N); | |
return ((j+N)%N) * N + ((i+N)%N); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// HANDLING // | |
//////////////////////////////////////////////////////////////////////////////// | |
void plant_sapling(const int x, const int y) { | |
auto hood = neighborhood(x,y,1); | |
shuffle(hood.begin(), hood.end(), g); | |
for(auto idx: hood) { | |
auto &c = grid[idx]; | |
if (c.tree) continue; | |
grid[idx].tree = SAPLING; // plant | |
grid[idx].age = 0; | |
grid[idx].dirty = 1; | |
planted++; | |
break; | |
} | |
} | |
void handle_tree(const int x, const int y, cell_t& c) { | |
// aging | |
c.age++; | |
if (SAPLING == c.tree && c.age > 12 ) { c.tree = TREE; c.dirty = 1;} | |
if (TREE == c.tree && c.age > 120) { c.tree = ELDER; c.dirty = 1;} | |
if (SAPLING == TREE) return; | |
double p = .1; | |
if (ELDER == c.tree) | |
p += .1; | |
if (randf(g) < p) | |
plant_sapling(x,y); | |
} | |
void handle_lumberjack(int x, int y) { | |
// 'pick him up' | |
size_t last_pos = y * N + x; | |
grid[last_pos].dirty = 1; | |
grid[last_pos].lumberjack = 0; | |
for(int i = 0; i < LJ_STEPS ; i++) { | |
auto idx = sample_pos(last_pos % N ,last_pos / N); | |
auto &c = grid[idx]; | |
if (c.lumberjack) continue; | |
if (c.bear) { | |
mawings++; | |
return; | |
} | |
if (c.tree > SAPLING) { | |
grid[idx].tree = 0; // harvest | |
harvested++; | |
// if (ELDER == c.tree) harvested++; | |
last_pos = idx; | |
break; | |
} | |
last_pos = idx; | |
} | |
// set him down | |
// printf("set down (%3d,%3d)\n",last_pos % N ,last_pos / N); | |
grid[last_pos].lumberjack = 1; | |
grid[last_pos].dirty = 1; | |
if( first_lj < 0) first_lj = last_pos; | |
} | |
void handle_bear(int x, int y) { | |
// 'pick him up' | |
size_t last_pos = y * N + x; | |
grid[last_pos].dirty = 1; | |
grid[last_pos].bear = 0; | |
for(int i = 0; i < BEAR_STEPS ; i++) { | |
auto idx = sample_pos(last_pos % N ,last_pos / N); | |
auto &c = grid[idx]; | |
if (c.bear) continue; | |
if (c.lumberjack) { | |
mawings++; | |
last_pos = idx; | |
c.lumberjack = 0; // kill | |
break; | |
} | |
last_pos = idx; | |
} | |
// set him down | |
// printf("set down (%3d,%3d)\n",last_pos % N ,last_pos / N); | |
grid[last_pos].bear = 1; | |
grid[last_pos].dirty = 1; | |
if (first_bear < 0) first_bear = last_pos; | |
} | |
void fire() { | |
int idx = first_lj; | |
if (first_lj < 0 || !grid[first_lj].lumberjack) idx = int(randf(g) * N * N); | |
while(1) { // scan from random start | |
if (grid[idx].lumberjack ) {grid[idx].lumberjack = 0; break; grid[idx].dirty = 1;} | |
idx++; idx %= (N*N); | |
} | |
} | |
void hire(int l) { | |
for (int i = 0; i < l; i++) { | |
int idx = int(randf(g) * N * N); | |
while(1) { // scan from random start | |
if ( !grid[idx].lumberjack && !grid[idx].bear ) {grid[idx].lumberjack = 1; grid[idx].dirty = 1; break;} | |
idx++; idx %= (N*N); | |
} | |
} | |
} | |
void take_bear() { | |
int idx = first_bear; | |
if (idx < 0 || !grid[idx].bear) idx = int(randf(g) * N * N); | |
while(1) { // scan from random start | |
if (grid[idx].bear ) {grid[idx].bear = 0; grid[idx].dirty = 1; break;} | |
idx++; idx %= N*N; | |
} | |
} | |
void tick() { | |
mawings = 0; | |
planted = 0; | |
harvested = 0; | |
first_bear = -1; // very short list to find one to kill | |
first_lj = -1; // very short list to find one to fire | |
int cnt = 0; | |
int idx = (randf(g) * N * N); // start random place | |
while(cnt < (N*N)) { | |
int i = idx % N; | |
int j = idx / N; | |
// printf("%d:", idx); | |
auto &c = grid[idx]; | |
// printf("%2d \n", c); | |
if(c.tree) | |
handle_tree(i,j,c); | |
if(c.lumberjack) { | |
handle_lumberjack(i,j); | |
} | |
if(c.bear) { | |
handle_bear(i,j); | |
} | |
idx++; | |
idx %= (N*N); | |
cnt++; | |
} | |
} | |
void tick_year() { | |
//printf("[Year %04d]]: %d lumberjacks %d bears %d saplings %d trees %d elder trees\n", current_month/12, lumberjacks , bears , saplings , trees , elders); | |
//printf("[Year %04d]]: %d harvested %d mawings %d planted\n", current_month/12, yearly_harvested, yearly_mawings, yearly_planted); | |
int lumberdiff = yearly_harvested - lumberjacks; | |
if ( lumberdiff < 0 && lumberjacks > 1) { | |
fire(); | |
//printf("[Year %04d]]: %d harvested and a Lumberjack was fired\n", current_month/12, yearly_harvested); | |
} else if ( lumberdiff > 9 ) { | |
hire(lumberdiff / 10); | |
//printf("[Year %04d]]: %d harvested and %d Lumberjack(s) was hired\n", current_month/12, yearly_harvested, lumberdiff / 10); | |
} else { | |
//printf("[Year %04d]]: %d harvested\n", current_month/12, yearly_harvested); | |
} | |
// bears | |
if (0 == yearly_mawings ) { | |
int idx = int(randf(g) * N * N); | |
while(1) { // scan from random start | |
if (!grid[idx].lumberjack && !grid[idx].bear ) {grid[idx].bear = 1; break;} | |
idx++; idx %= N*N; | |
} | |
//printf("[Year %04d]]: a cub was born to us\n", current_month/12); | |
} else { | |
take_bear(); | |
//printf("[Year %04d]]: the zoo took one away\n", current_month/12); | |
} | |
yearly_harvested = 0; | |
yearly_mawings = 0; | |
yearly_planted = 0; | |
} | |
int main(int argc, char ** argv) { | |
printf("%zd sizeof(cell_t)\n", sizeof(cell_t)); | |
int months = 22; | |
if (argc > 1) months = atoi(argv[1]); | |
if (argc > 2) N = atoi(argv[2]); | |
init(); | |
if (SDL_Init(SDL_INIT_EVERYTHING) != 0){ | |
printf("SDL_Init Error: "); | |
return 1; | |
} | |
SDL_Window *win; | |
win = SDL_CreateWindow("Magic Forrest!", 10, 10, W, H, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL) ; | |
if (win == nullptr){ | |
printf("SDL_CreateWindow Error: "); | |
return 1; | |
} | |
SDL_Renderer *renderer = SDL_CreateRenderer(win, -1, | |
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); | |
if (renderer == nullptr){ | |
printf("SDL_CreateRenderer Error: "); | |
return 1; | |
} | |
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); | |
bool quit = false; | |
SDL_Event e; | |
scan_grid(false); | |
auto start_timer = SDL_GetTicks(); | |
float fps = 10.0; | |
for (current_month = 0; current_month <= months && !quit; current_month++) { | |
while (SDL_PollEvent(&e)){ | |
if (e.type == SDL_QUIT | |
|| e.type == SDL_MOUSEBUTTONDOWN | |
|| SDLK_q == e.key.keysym.sym) { | |
quit = true; | |
continue; | |
} | |
} | |
auto start_month = SDL_GetTicks(); | |
tick(); | |
scan_grid(false); | |
display_stats(); | |
if ( true || !(current_month %10) ) { // true || | |
render_grid(renderer); | |
SDL_RenderPresent(renderer); | |
} | |
if (current_month > 0 && ((current_month % 12) == 0) ) { | |
// yearly bookeeping | |
tick_year(); | |
} | |
if (0 == lumberjacks) { | |
hire(1); | |
printf("[Month %04d]: lumberjack hired\n", current_month, planted); | |
} | |
auto end_month = SDL_GetTicks(); | |
auto dt = end_month - start_month; | |
fps = fps * .9 + .1 * (1000.0 / dt); | |
printf("\r%5d ms last month %.2f fps", | |
dt, | |
fps); | |
fflush(stdout); | |
} | |
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