#include #include #include #include #include #define max(x, y) ({ \ __typeof__(x) __x = x, __y = y; \ (__x < __y ? __y : __x); \ }) #define min(x, y) ({ \ __typeof__(x) __x = x, __y = y; \ (__x > __y ? __y : __x); \ }) enum { TILE_KIND_WALL, TILE_KIND_EMPTY }; typedef uint32_t Tile_Kind; #define TILE_CAPACITY 1000000 typedef struct Tile { Tile_Kind kind; uint32_t capacity; uint32_t volume; } Tile; #define LEVEL_WIDTH 40 #define LEVEL_HEIGHT 25 #define TILE_SIZE 20 static Tile level[LEVEL_WIDTH * LEVEL_HEIGHT]; enum { ENTITY_KIND_FAUCET }; #define TILE_AT(x, y) (*({ \ uint32_t __x = x; \ uint32_t __y = y; \ level + (__x + LEVEL_WIDTH * __y); \ })) static inline bool flowable_to(uint32_t x, uint32_t y) { return x < LEVEL_WIDTH && y < LEVEL_HEIGHT && TILE_AT(x, y).kind == TILE_KIND_EMPTY; } typedef uint32_t Entity_Kind; typedef struct Entity { Entity_Kind kind; uint16_t x, y; union { struct { uint32_t flow; } faucet; } entity; } Entity; #define MAX_ENTITIES 10 static uint32_t entity_count; static Entity entities[MAX_ENTITIES]; void draw_tile(uint32_t x, uint32_t y) { Tile_Kind kind = TILE_AT(x, y).kind; if (kind == TILE_KIND_WALL) { glColor3f(1, 0, 0); glBegin(GL_QUADS); glVertex2f((x + 0) * TILE_SIZE, (y + 0) * TILE_SIZE); glVertex2f((x + 1) * TILE_SIZE, (y + 0) * TILE_SIZE); glVertex2f((x + 1) * TILE_SIZE, (y + 1) * TILE_SIZE); glVertex2f((x + 0) * TILE_SIZE, (y + 1) * TILE_SIZE); glEnd(); glColor3f(1, 1, 1); } else if (kind == TILE_KIND_EMPTY) { glColor3f(0, 0, 1); float fraction = (double)TILE_AT(x, y).volume / (double)TILE_AT(x, y).capacity; if (fraction > 1.0f) { glColor3f(fraction - 1.0f, fraction - 2.0f, 1); fraction = 1.0f; } glBegin(GL_QUADS); glVertex2f((x + 0) * TILE_SIZE, (y + 1 - fraction) * TILE_SIZE); glVertex2f((x + 1) * TILE_SIZE, (y + 1 - fraction) * TILE_SIZE); glVertex2f((x + 1) * TILE_SIZE, (y + 1) * TILE_SIZE); glVertex2f((x + 0) * TILE_SIZE, (y + 1) * TILE_SIZE); glEnd(); glColor3f(1, 1, 1); } else { abort(); } } void draw_entity(uint32_t i) { Entity_Kind kind = entities[i].kind; float x = entities[i].x; float y = entities[i].y; if (kind == ENTITY_KIND_FAUCET) { glColor3f(0, 1, 1); glBegin(GL_QUADS); glVertex2f((x + 0.25f) * TILE_SIZE, (y + 0.25f) * TILE_SIZE); glVertex2f((x + 0.75f) * TILE_SIZE, (y + 0.25f) * TILE_SIZE); glVertex2f((x + 0.75f) * TILE_SIZE, (y + 0.75f) * TILE_SIZE); glVertex2f((x + 0.25f) * TILE_SIZE, (y + 0.75f) * TILE_SIZE); glEnd(); glColor3f(1, 1, 1); } else { abort(); } } void update(void) { for (uint32_t i = 0; i < entity_count; ++i) { if (entities[i].kind == ENTITY_KIND_FAUCET) { TILE_AT(entities[i].x, entities[i].y).volume += entities[i].entity.faucet.flow; } } for (uint32_t y = 0; y < LEVEL_HEIGHT; ++y) { for (uint32_t x = 0; x < LEVEL_WIDTH; ++x) { if (TILE_AT(x, y).kind == TILE_KIND_EMPTY) { if (TILE_AT(x, y).volume == 0) continue; if (flowable_to(x, y + 1) && TILE_AT(x, y + 1).volume < TILE_AT(x, y + 1).capacity) { uint32_t flow = min( TILE_AT(x, y).volume, TILE_AT(x, y + 1).capacity - TILE_AT(x, y + 1).volume); TILE_AT(x, y + 1).volume += flow; TILE_AT(x, y).volume -= flow; } if (TILE_AT(x, y).volume == 0) continue; bool have_left = flowable_to(x - 1, y); bool have_right = flowable_to(x + 1, y); if (have_left || have_right) { uint32_t total = TILE_AT(x, y).volume; if (have_left) total += TILE_AT(x - 1, y).volume; if (have_right) total += TILE_AT(x + 1, y).volume; uint32_t n_h = 1 + have_left + have_right; uint32_t nth = total / n_h; if (have_left) { TILE_AT(x - 1, y).volume = nth; total -= nth; } if (have_right) { TILE_AT(x + 1, y).volume = nth; total -= nth; } TILE_AT(x, y).volume = total; } if (flowable_to(x, y - 1) && TILE_AT(x, y).volume > TILE_AT(x, y).capacity) { uint32_t flow = TILE_AT(x, y).volume - TILE_AT(x, y).capacity; TILE_AT(x, y - 1).volume += flow; TILE_AT(x, y).volume -= flow; } } } } } #define CYCLE_MS 10 #define CYCLES_PER_SEC 100 static int last_frame_time; static int time_left_over; void display(void) { int now = glutGet(GLUT_ELAPSED_TIME); int dt = now - last_frame_time + time_left_over; last_frame_time = now; while (dt >= CYCLE_MS) { update(); dt -= CYCLE_MS; } time_left_over = dt; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, 800, 500, 0, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); for (uint32_t y = 0; y < LEVEL_HEIGHT; ++y) { for (uint32_t x = 0; x < LEVEL_WIDTH; ++x) { draw_tile(x, y); } } for (uint32_t i = 0; i < entity_count; ++i) { draw_entity(i); } glutSwapBuffers(); } void reshape(int width, int height) { glViewport(0, 0, width, height); } void idle(void) { glutPostRedisplay(); } void read_level(char const *path) { FILE *f = fopen(path, "rb"); if (!f) { fprintf(stderr, "can't open %s\n", path); exit(EXIT_FAILURE); } for (uint32_t y = 0; y < LEVEL_HEIGHT; ++y) { for (uint32_t x = 0; x < LEVEL_WIDTH; ++x) { int c = fgetc(f); if (c == '#') { TILE_AT(x, y).kind = TILE_KIND_WALL; } else { TILE_AT(x, y).kind = TILE_KIND_EMPTY; TILE_AT(x, y).capacity = TILE_CAPACITY; TILE_AT(x, y).volume = 0; if (c == ' ') { } else if (c == 'F') { if (entity_count == MAX_ENTITIES) { fprintf(stderr, "exceeded max entities (%c) in %s\n", c, path); exit(EXIT_FAILURE); } entities[entity_count].kind = ENTITY_KIND_FAUCET; entities[entity_count].x = x; entities[entity_count].y = y; entities[entity_count].entity.faucet.flow = 100000; entity_count ++; } else { fprintf(stderr, "syntax error (%c) in %s\n", c, path); exit(EXIT_FAILURE); } } } int c = fgetc(f); if (c != '\n') { fprintf(stderr, "syntax error (%c) in %s\n", c, path); exit(EXIT_FAILURE); } } } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_ALPHA | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(800, 500); glutCreateWindow("WT"); glutDisplayFunc(display); glutReshapeFunc(reshape); glutIdleFunc(idle); read_level("level.txt"); last_frame_time = glutGet(GLUT_ELAPSED_TIME); glutMainLoop(); return EXIT_SUCCESS; }