Skip to content

Instantly share code, notes, and snippets.

Created June 7, 2014 20:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/dca5ea513ffccf83403e to your computer and use it in GitHub Desktop.
Save anonymous/dca5ea513ffccf83403e to your computer and use it in GitHub Desktop.
// 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