Back to shtanton's homepage
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/all.c427
-rw-r--r--src/levels.c37
-rw-r--r--src/tick.c335
-rw-r--r--src/types.c90
4 files changed, 484 insertions, 405 deletions
diff --git a/src/all.c b/src/all.c
index 4cca8ae..3d192ba 100644
--- a/src/all.c
+++ b/src/all.c
@@ -1,103 +1,12 @@
+#ifndef INCLUDE_ALL_C
+#define INCLUDE_ALL_C
+
#include <stddef.h>
#include <stdint.h>
-#define MEM_SIZE (1<<24)
-#define GRIDWIDTH 16
-#define GRIDHEIGHT 16
-#define TICK_LENGTH 200
-
-typedef struct {
- unsigned char r, g, b, a;
-} Color;
-
-typedef struct {
- int x, y, w, h;
- Color fill;
- Color border;
-} DrawElement;
-
-typedef struct {
- int len;
- DrawElement els[2 * GRIDWIDTH * GRIDHEIGHT * 4];
-} DrawList;
-
-typedef struct {
- char *start;
- char *end;
-} Arena;
-
-#define new(a, c, t) ((t *) alloc(a, c, sizeof(t), _Alignof(t)))
-#define affirm(c) while (!(c)) *(volatile int *)0 = 0
-
-static void xmemcpy(void *dst, void *src, ptrdiff_t size) {
- for (ptrdiff_t i = 0; i < size; i++) {
- ((char *) dst)[i] = ((char *) src)[i];
- }
-}
-
-static void *alloc(Arena *a, ptrdiff_t count, ptrdiff_t size, ptrdiff_t align) {
- ptrdiff_t pad = -(size_t) a->start & (align - 1);
- affirm(count < (a->end - a->start - pad) / size);
- char *r = a->start + pad;
- a->start += pad + size * count;
- for (ptrdiff_t i = 0; i < count * size; i++) {
- r[i] = 0;
- }
-
- return r;
-}
-
-typedef struct {
- int width, height;
-} UI;
-
-typedef struct {
- uint64_t lastTick;
- char playing;
- int grid[GRIDWIDTH * GRIDHEIGHT];
-} State;
-
-// Mirror these in src/index.html.in
-enum {
- INPUT_NONE,
- INPUT_CLICK,
- INPUT_RCLICK,
- INPUT_PAUSE_PLAY,
-};
-
-typedef struct {
- State state;
- UI ui;
- int input;
- int mousex, mousey;
-} Game;
-
-enum {
- EMPTY,
- BLACK,
- RED,
- YELLOW,
- RED_UP,
- RED_DOWN,
- RED_LEFT,
- RED_RIGHT,
- BLUE,
- BLUE_UP,
- BLUE_DOWN,
- BLUE_LEFT,
- BLUE_RIGHT,
- N_COLORS,
-};
-
-static int isRed(int cell) {
- return (
- cell == RED ||
- cell == RED_LEFT ||
- cell == RED_RIGHT ||
- cell == RED_UP ||
- cell == RED_DOWN
- );
-}
+#include "types.c"
+#include "levels.c"
+#include "tick.c"
static const Color colors[N_COLORS][4] = {
{
@@ -213,6 +122,15 @@ static DrawList *render(State *state, UI *ui, Arena *a) {
}
}
+ drawList->els[drawList->len++] = (DrawElement) {
+ .x = cellWidth * state->goalx,
+ .y = cellHeight * state->goaly,
+ .w = cellWidth,
+ .h = cellHeight,
+ .fill = {255, 0, 0, 63},
+ .border = {255, 0, 0, 255},
+ };
+
return drawList;
}
@@ -238,313 +156,8 @@ static void update(Game *game, uint64_t now, Arena a) {
if (game->state.playing && game->state.lastTick + TICK_LENGTH <= now) {
game->state.lastTick = now;
- State *lastState = new(&a, 1, State);
- xmemcpy(lastState, game, sizeof(State));
-
- for (int x = 0; x < GRIDWIDTH; x++) {
- for (int y = 0; y < GRIDHEIGHT; y++) {
- switch (lastState->grid[x + y * GRIDWIDTH]) {
- int reds, red;
- int blues, blue;
- case BLACK:
- reds = 0;
- if (
- x < GRIDWIDTH - 1 && (
- lastState->grid[x + 1 + y * GRIDWIDTH] == RED_LEFT ||
- lastState->grid[x + 1 + y * GRIDWIDTH] == RED
- )
- ) {
- reds++;
- if (
- x < GRIDWIDTH - 2 &&
- y > 0 &&
- y < GRIDHEIGHT - 1 &&
- lastState->grid[x + 2 + y * GRIDWIDTH] == YELLOW &&
- lastState->grid[x + 1 + (y - 1) * GRIDWIDTH] == YELLOW &&
- lastState->grid[x + 1 + (y + 1) * GRIDWIDTH] == YELLOW
- ) {
- red = RED_LEFT;
- } else {
- red = lastState->grid[x + 1 + y * GRIDWIDTH];
- }
- }
-
- if (
- x > 0 && (
- lastState->grid[x - 1 + y * GRIDWIDTH] == RED_RIGHT ||
- lastState->grid[x - 1 + y * GRIDWIDTH] == RED
- )
- ) {
- reds++;
- if (
- x > 1 &&
- y > 0 &&
- y < GRIDHEIGHT - 1 &&
- lastState->grid[x - 2 + y * GRIDWIDTH] == YELLOW &&
- lastState->grid[x - 1 + (y - 1) * GRIDWIDTH] == YELLOW &&
- lastState->grid[x - 1 + (y + 1) * GRIDWIDTH] == YELLOW
- ) {
- red = RED_RIGHT;
- } else {
- red = lastState->grid[x - 1 + y * GRIDWIDTH];
- }
- }
-
- if (
- y > 0 && (
- lastState->grid[x + (y - 1) * GRIDWIDTH] == RED_DOWN ||
- lastState->grid[x + (y - 1) * GRIDWIDTH] == RED
- )
- ) {
- reds++;
- if (
- x > 0 &&
- x < GRIDWIDTH - 1 &&
- y > 1 &&
- lastState->grid[x + (y - 2) * GRIDWIDTH] == YELLOW &&
- lastState->grid[x - 1 + (y - 1) * GRIDWIDTH] == YELLOW &&
- lastState->grid[x + 1 + (y - 1) * GRIDWIDTH] == YELLOW
- ) {
- red = RED_DOWN;
- } else {
- red = lastState->grid[x + (y - 1) * GRIDWIDTH];
- }
- }
-
- if (
- y < GRIDHEIGHT - 1 && (
- lastState->grid[x + (y + 1) * GRIDWIDTH] == RED_UP ||
- lastState->grid[x + (y + 1) * GRIDWIDTH] == RED
- )
- ) {
- reds++;
- if (
- x > 0 &&
- x < GRIDWIDTH - 1 &&
- y < GRIDHEIGHT - 2 &&
- lastState->grid[x + (y + 2) * GRIDWIDTH] == YELLOW &&
- lastState->grid[x - 1 + (y + 1) * GRIDWIDTH] == YELLOW &&
- lastState->grid[x + 1 + (y + 1) * GRIDWIDTH] == YELLOW
- ) {
- red = RED_UP;
- } else {
- red = lastState->grid[x + (y + 1) * GRIDWIDTH];
- }
- }
-
- if (reds == 1) {
- game->state.grid[x + y * GRIDWIDTH] = red;
- } else if (reds >= 2) {
- game->state.grid[x + y * GRIDWIDTH] = RED;
- }
- break;
- case RED:
- game->state.grid[x + y * GRIDWIDTH] = YELLOW;
- break;
- case RED_LEFT:
- if (
- x > 0 &&
- lastState->grid[x - 1 + y * GRIDWIDTH] == BLACK
- ) {
- game->state.grid[x + y * GRIDWIDTH] = YELLOW;
- } else {
- game->state.grid[x + y * GRIDWIDTH] = RED_UP;
- }
- break;
- case RED_RIGHT:
- if (
- x < GRIDWIDTH - 1 &&
- lastState->grid[x + 1 + y * GRIDWIDTH] == BLACK
- ) {
- game->state.grid[x + y * GRIDWIDTH] = YELLOW;
- } else {
- game->state.grid[x + y * GRIDWIDTH] = RED_DOWN;
- }
- break;
- case RED_UP:
- if (
- y > 0 &&
- lastState->grid[x + (y - 1) * GRIDWIDTH] == BLACK
- ) {
- game->state.grid[x + y * GRIDWIDTH] = YELLOW;
- } else {
- game->state.grid[x + y * GRIDWIDTH] = RED_RIGHT;
- }
- break;
- case RED_DOWN:
- if (
- y < GRIDHEIGHT - 1 &&
- lastState->grid[x + (y + 1) * GRIDWIDTH] == BLACK
- ) {
- game->state.grid[x + y * GRIDWIDTH] = YELLOW;
- } else {
- game->state.grid[x + y * GRIDWIDTH] = RED_LEFT;
- }
- break;
- case YELLOW:
- if (
- (x > 0 && lastState->grid[x - 1 + y * GRIDWIDTH] == BLUE_RIGHT) ||
- (x < GRIDWIDTH - 1 && lastState->grid[x + 1 + y * GRIDWIDTH] == BLUE_LEFT) ||
- (y > 0 && lastState->grid[x + (y - 1) * GRIDWIDTH] == BLUE_DOWN) ||
- (y < GRIDHEIGHT - 1 && lastState->grid[x + (y + 1) * GRIDWIDTH] == BLUE_UP)
- ) {
- game->state.grid[x + y * GRIDWIDTH] = RED;
- } else {
- game->state.grid[x + y * GRIDWIDTH] = BLACK;
- }
- break;
- case BLUE:
- blues = 0;
- if (
- x > 0 && (
- lastState->grid[x - 1 + y * GRIDWIDTH] == RED ||
- lastState->grid[x - 1 + y * GRIDWIDTH] == RED_LEFT ||
- lastState->grid[x - 1 + y * GRIDWIDTH] == RED_RIGHT ||
- lastState->grid[x - 1 + y * GRIDWIDTH] == RED_DOWN ||
- lastState->grid[x - 1 + y * GRIDWIDTH] == RED_UP
- )
- ) {
- blues++;
- blue = BLUE_RIGHT;
- }
- if (
- x < GRIDWIDTH - 1 && (
- lastState->grid[x + 1 + y * GRIDWIDTH] == RED ||
- lastState->grid[x + 1 + y * GRIDWIDTH] == RED_LEFT ||
- lastState->grid[x + 1 + y * GRIDWIDTH] == RED_RIGHT ||
- lastState->grid[x + 1 + y * GRIDWIDTH] == RED_DOWN ||
- lastState->grid[x + 1 + y * GRIDWIDTH] == RED_UP
- )
- ) {
- blues++;
- blue = BLUE_LEFT;
- }
- if (
- y > 0 && (
- lastState->grid[x + (y - 1) * GRIDWIDTH] == RED ||
- lastState->grid[x + (y - 1) * GRIDWIDTH] == RED_LEFT ||
- lastState->grid[x + (y - 1) * GRIDWIDTH] == RED_RIGHT ||
- lastState->grid[x + (y - 1) * GRIDWIDTH] == RED_DOWN ||
- lastState->grid[x + (y - 1) * GRIDWIDTH] == RED_UP
- )
- ) {
- blues++;
- blue = BLUE_DOWN;
- }
- if (
- y < GRIDHEIGHT - 1 && (
- lastState->grid[x + (y + 1) * GRIDWIDTH] == RED ||
- lastState->grid[x + (y + 1) * GRIDWIDTH] == RED_LEFT ||
- lastState->grid[x + (y + 1) * GRIDWIDTH] == RED_RIGHT ||
- lastState->grid[x + (y + 1) * GRIDWIDTH] == RED_DOWN ||
- lastState->grid[x + (y + 1) * GRIDWIDTH] == RED_UP
- )
- ) {
- blues++;
- blue = BLUE_UP;
- }
- if (blues == 1) {
- game->state.grid[x + y * GRIDWIDTH] = blue;
- }
- break;
- case BLUE_LEFT:
- if (
- (x > 0 && isRed(lastState->grid[x - 1 + y * GRIDWIDTH])) ||
- (x < GRIDWIDTH - 1 && isRed(lastState->grid[x + 1 + y * GRIDWIDTH])) ||
- (y > 0 && isRed(lastState->grid[x + (y - 1) * GRIDWIDTH])) ||
- (y < GRIDHEIGHT - 1 && isRed(lastState->grid[x + (y + 1) * GRIDWIDTH]))
- ) {
- game->state.grid[x + y * GRIDWIDTH] = BLUE;
- } else if (
- x > 0 &&
- lastState->grid[x - 1 + y * GRIDWIDTH] == EMPTY
- ) {
- game->state.grid[x + y * GRIDWIDTH] = EMPTY;
- } else {
- game->state.grid[x + y * GRIDWIDTH] = BLUE;
- }
- break;
- case BLUE_RIGHT:
- if (
- (x > 0 && isRed(lastState->grid[x - 1 + y * GRIDWIDTH])) ||
- (x < GRIDWIDTH - 1 && isRed(lastState->grid[x + 1 + y * GRIDWIDTH])) ||
- (y > 0 && isRed(lastState->grid[x + (y - 1) * GRIDWIDTH])) ||
- (y < GRIDHEIGHT - 1 && isRed(lastState->grid[x + (y + 1) * GRIDWIDTH]))
- ) {
- game->state.grid[x + y * GRIDWIDTH] = BLUE;
- } else if (
- x < GRIDWIDTH - 1 &&
- lastState->grid[x + 1 + y * GRIDWIDTH] == EMPTY
- ) {
- game->state.grid[x + y * GRIDWIDTH] = EMPTY;
- } else {
- game->state.grid[x + y * GRIDWIDTH] = BLUE;
- }
- break;
- case BLUE_UP:
- if (
- (x > 0 && isRed(lastState->grid[x - 1 + y * GRIDWIDTH])) ||
- (x < GRIDWIDTH - 1 && isRed(lastState->grid[x + 1 + y * GRIDWIDTH])) ||
- (y > 0 && isRed(lastState->grid[x + (y - 1) * GRIDWIDTH])) ||
- (y < GRIDHEIGHT - 1 && isRed(lastState->grid[x + (y + 1) * GRIDWIDTH]))
- ) {
- game->state.grid[x + y * GRIDWIDTH] = BLUE;
- } else if (
- y > 0 &&
- lastState->grid[x + (y - 1) * GRIDWIDTH] == EMPTY
- ) {
- game->state.grid[x + y * GRIDWIDTH] = EMPTY;
- } else {
- game->state.grid[x + y * GRIDWIDTH] = BLUE;
- }
- break;
- case BLUE_DOWN:
- if (
- (x > 0 && isRed(lastState->grid[x - 1 + y * GRIDWIDTH])) ||
- (x < GRIDWIDTH - 1 && isRed(lastState->grid[x + 1 + y * GRIDWIDTH])) ||
- (y > 0 && isRed(lastState->grid[x + (y - 1) * GRIDWIDTH])) ||
- (y < GRIDHEIGHT - 1 && isRed(lastState->grid[x + (y + 1) * GRIDWIDTH]))
- ) {
- game->state.grid[x + y * GRIDWIDTH] = BLUE;
- } else if (
- y < GRIDHEIGHT - 1 &&
- lastState->grid[x + (y + 1) * GRIDWIDTH] == EMPTY
- ) {
- game->state.grid[x + y * GRIDWIDTH] = EMPTY;
- } else {
- game->state.grid[x + y * GRIDWIDTH] = BLUE;
- }
- break;
- case EMPTY:
- // TODO: same as multiple reds
- if (
- x > 0 &&
- lastState->grid[x - 1 + y * GRIDWIDTH] == BLUE_RIGHT
- ) {
- game->state.grid[x + y * GRIDWIDTH] = BLUE_RIGHT;
- }
- if (
- x < GRIDWIDTH - 1 &&
- lastState->grid[x + 1 + y * GRIDWIDTH] == BLUE_LEFT
- ) {
- game->state.grid[x + y * GRIDWIDTH] = BLUE_LEFT;
- }
- if (
- y > 0 &&
- lastState->grid[x + (y - 1) * GRIDWIDTH] == BLUE_DOWN
- ) {
- game->state.grid[x + y * GRIDWIDTH] = BLUE_DOWN;
- }
- if (
- y < GRIDHEIGHT - 1 &&
- lastState->grid[x + (y + 1) * GRIDWIDTH] == BLUE_UP
- ) {
- game->state.grid[x + y * GRIDWIDTH] = BLUE_UP;
- }
- break;
- }
- }
- }
+
+ tick(game, a);
}
}
@@ -563,7 +176,9 @@ int main(int argc, char **argv) {
};
Game *game = new(&a, 1, Game);
- game->state.grid[0] = 0;
+ xmemcpy(&game->state.grid, &levels[0].grid, sizeof(game->state.grid));
+ game->state.goalx = levels[0].goalx;
+ game->state.goaly = levels[0].goaly;
game->state.playing = 0;
game->ui = (UI) {
.width = 640,
@@ -685,3 +300,5 @@ void game_update(int input, int mousex, int mousey, int now) {
}
#endif
+
+#endif
diff --git a/src/levels.c b/src/levels.c
new file mode 100644
index 0000000..22ad76a
--- /dev/null
+++ b/src/levels.c
@@ -0,0 +1,37 @@
+#include "all.c"
+
+typedef struct {
+ int grid[GRIDWIDTH * GRIDHEIGHT];
+ int goalx, goaly;
+} Level;
+
+#define _ EMPTY,
+#define B BLACK,
+#define O BLUE,
+static Level levels[] = {
+ {
+ .grid = {
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ B O _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ },
+ .goalx = 18,
+ .goaly = 7,
+ },
+};
+#undef _
+#undef B
+#undef O
diff --git a/src/tick.c b/src/tick.c
new file mode 100644
index 0000000..8b8f731
--- /dev/null
+++ b/src/tick.c
@@ -0,0 +1,335 @@
+#include "all.c"
+
+static int isRed(int cell) {
+ return (
+ cell == RED ||
+ cell == RED_LEFT ||
+ cell == RED_RIGHT ||
+ cell == RED_UP ||
+ cell == RED_DOWN
+ );
+}
+
+static int isBlue(int cell) {
+ return (
+ cell == BLUE ||
+ cell == BLUE_LEFT ||
+ cell == BLUE_RIGHT ||
+ cell == BLUE_UP ||
+ cell == BLUE_DOWN
+ );
+}
+
+static void tick(Game *game, Arena a) {
+ State *lastState = new(&a, 1, State);
+ xmemcpy(lastState, game, sizeof(State));
+
+ for (int x = 0; x < GRIDWIDTH; x++) {
+ for (int y = 0; y < GRIDHEIGHT; y++) {
+ switch (lastState->grid[x + y * GRIDWIDTH]) {
+ int reds, red;
+ int blues, blue;
+ case BLACK:
+ reds = 0;
+ if (
+ x < GRIDWIDTH - 1 && (
+ lastState->grid[x + 1 + y * GRIDWIDTH] == RED_LEFT ||
+ lastState->grid[x + 1 + y * GRIDWIDTH] == RED
+ )
+ ) {
+ reds++;
+ if (
+ x < GRIDWIDTH - 2 &&
+ y > 0 &&
+ y < GRIDHEIGHT - 1 &&
+ lastState->grid[x + 2 + y * GRIDWIDTH] == YELLOW &&
+ lastState->grid[x + 1 + (y - 1) * GRIDWIDTH] == YELLOW &&
+ lastState->grid[x + 1 + (y + 1) * GRIDWIDTH] == YELLOW
+ ) {
+ red = RED_LEFT;
+ } else {
+ red = lastState->grid[x + 1 + y * GRIDWIDTH];
+ }
+ }
+
+ if (
+ x > 0 && (
+ lastState->grid[x - 1 + y * GRIDWIDTH] == RED_RIGHT ||
+ lastState->grid[x - 1 + y * GRIDWIDTH] == RED
+ )
+ ) {
+ reds++;
+ if (
+ x > 1 &&
+ y > 0 &&
+ y < GRIDHEIGHT - 1 &&
+ lastState->grid[x - 2 + y * GRIDWIDTH] == YELLOW &&
+ lastState->grid[x - 1 + (y - 1) * GRIDWIDTH] == YELLOW &&
+ lastState->grid[x - 1 + (y + 1) * GRIDWIDTH] == YELLOW
+ ) {
+ red = RED_RIGHT;
+ } else {
+ red = lastState->grid[x - 1 + y * GRIDWIDTH];
+ }
+ }
+
+ if (
+ y > 0 && (
+ lastState->grid[x + (y - 1) * GRIDWIDTH] == RED_DOWN ||
+ lastState->grid[x + (y - 1) * GRIDWIDTH] == RED
+ )
+ ) {
+ reds++;
+ if (
+ x > 0 &&
+ x < GRIDWIDTH - 1 &&
+ y > 1 &&
+ lastState->grid[x + (y - 2) * GRIDWIDTH] == YELLOW &&
+ lastState->grid[x - 1 + (y - 1) * GRIDWIDTH] == YELLOW &&
+ lastState->grid[x + 1 + (y - 1) * GRIDWIDTH] == YELLOW
+ ) {
+ red = RED_DOWN;
+ } else {
+ red = lastState->grid[x + (y - 1) * GRIDWIDTH];
+ }
+ }
+
+ if (
+ y < GRIDHEIGHT - 1 && (
+ lastState->grid[x + (y + 1) * GRIDWIDTH] == RED_UP ||
+ lastState->grid[x + (y + 1) * GRIDWIDTH] == RED
+ )
+ ) {
+ reds++;
+ if (
+ x > 0 &&
+ x < GRIDWIDTH - 1 &&
+ y < GRIDHEIGHT - 2 &&
+ lastState->grid[x + (y + 2) * GRIDWIDTH] == YELLOW &&
+ lastState->grid[x - 1 + (y + 1) * GRIDWIDTH] == YELLOW &&
+ lastState->grid[x + 1 + (y + 1) * GRIDWIDTH] == YELLOW
+ ) {
+ red = RED_UP;
+ } else {
+ red = lastState->grid[x + (y + 1) * GRIDWIDTH];
+ }
+ }
+
+ if (reds == 1) {
+ game->state.grid[x + y * GRIDWIDTH] = red;
+ } else if (reds >= 2) {
+ game->state.grid[x + y * GRIDWIDTH] = RED;
+ }
+ break;
+ case RED:
+ game->state.grid[x + y * GRIDWIDTH] = YELLOW;
+ break;
+ case RED_LEFT:
+ if (
+ x > 0 &&
+ lastState->grid[x - 1 + y * GRIDWIDTH] == BLACK
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = YELLOW;
+ } else {
+ game->state.grid[x + y * GRIDWIDTH] = RED_UP;
+ }
+ break;
+ case RED_RIGHT:
+ if (
+ x < GRIDWIDTH - 1 &&
+ lastState->grid[x + 1 + y * GRIDWIDTH] == BLACK
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = YELLOW;
+ } else {
+ game->state.grid[x + y * GRIDWIDTH] = RED_DOWN;
+ }
+ break;
+ case RED_UP:
+ if (
+ y > 0 &&
+ lastState->grid[x + (y - 1) * GRIDWIDTH] == BLACK
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = YELLOW;
+ } else {
+ game->state.grid[x + y * GRIDWIDTH] = RED_RIGHT;
+ }
+ break;
+ case RED_DOWN:
+ if (
+ y < GRIDHEIGHT - 1 &&
+ lastState->grid[x + (y + 1) * GRIDWIDTH] == BLACK
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = YELLOW;
+ } else {
+ game->state.grid[x + y * GRIDWIDTH] = RED_LEFT;
+ }
+ break;
+ case YELLOW:
+ if (
+ (x > 0 && lastState->grid[x - 1 + y * GRIDWIDTH] == BLUE_RIGHT) ||
+ (x < GRIDWIDTH - 1 && lastState->grid[x + 1 + y * GRIDWIDTH] == BLUE_LEFT) ||
+ (y > 0 && lastState->grid[x + (y - 1) * GRIDWIDTH] == BLUE_DOWN) ||
+ (y < GRIDHEIGHT - 1 && lastState->grid[x + (y + 1) * GRIDWIDTH] == BLUE_UP)
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = RED;
+ } else {
+ game->state.grid[x + y * GRIDWIDTH] = BLACK;
+ }
+ break;
+ case BLUE:
+ blues = 0;
+ if (
+ x > 0 && (
+ lastState->grid[x - 1 + y * GRIDWIDTH] == RED ||
+ lastState->grid[x - 1 + y * GRIDWIDTH] == RED_LEFT ||
+ lastState->grid[x - 1 + y * GRIDWIDTH] == RED_RIGHT ||
+ lastState->grid[x - 1 + y * GRIDWIDTH] == RED_DOWN ||
+ lastState->grid[x - 1 + y * GRIDWIDTH] == RED_UP
+ )
+ ) {
+ blues++;
+ blue = BLUE_RIGHT;
+ }
+ if (
+ x < GRIDWIDTH - 1 && (
+ lastState->grid[x + 1 + y * GRIDWIDTH] == RED ||
+ lastState->grid[x + 1 + y * GRIDWIDTH] == RED_LEFT ||
+ lastState->grid[x + 1 + y * GRIDWIDTH] == RED_RIGHT ||
+ lastState->grid[x + 1 + y * GRIDWIDTH] == RED_DOWN ||
+ lastState->grid[x + 1 + y * GRIDWIDTH] == RED_UP
+ )
+ ) {
+ blues++;
+ blue = BLUE_LEFT;
+ }
+ if (
+ y > 0 && (
+ lastState->grid[x + (y - 1) * GRIDWIDTH] == RED ||
+ lastState->grid[x + (y - 1) * GRIDWIDTH] == RED_LEFT ||
+ lastState->grid[x + (y - 1) * GRIDWIDTH] == RED_RIGHT ||
+ lastState->grid[x + (y - 1) * GRIDWIDTH] == RED_DOWN ||
+ lastState->grid[x + (y - 1) * GRIDWIDTH] == RED_UP
+ )
+ ) {
+ blues++;
+ blue = BLUE_DOWN;
+ }
+ if (
+ y < GRIDHEIGHT - 1 && (
+ lastState->grid[x + (y + 1) * GRIDWIDTH] == RED ||
+ lastState->grid[x + (y + 1) * GRIDWIDTH] == RED_LEFT ||
+ lastState->grid[x + (y + 1) * GRIDWIDTH] == RED_RIGHT ||
+ lastState->grid[x + (y + 1) * GRIDWIDTH] == RED_DOWN ||
+ lastState->grid[x + (y + 1) * GRIDWIDTH] == RED_UP
+ )
+ ) {
+ blues++;
+ blue = BLUE_UP;
+ }
+ if (blues == 1) {
+ game->state.grid[x + y * GRIDWIDTH] = blue;
+ }
+ break;
+ case BLUE_LEFT:
+ if (
+ (x > 0 && isRed(lastState->grid[x - 1 + y * GRIDWIDTH])) ||
+ (x < GRIDWIDTH - 1 && isRed(lastState->grid[x + 1 + y * GRIDWIDTH])) ||
+ (y > 0 && isRed(lastState->grid[x + (y - 1) * GRIDWIDTH])) ||
+ (y < GRIDHEIGHT - 1 && isRed(lastState->grid[x + (y + 1) * GRIDWIDTH]))
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = BLUE;
+ } else if (
+ x > 0 &&
+ lastState->grid[x - 1 + y * GRIDWIDTH] == EMPTY
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = EMPTY;
+ } else {
+ game->state.grid[x + y * GRIDWIDTH] = BLUE;
+ }
+ break;
+ case BLUE_RIGHT:
+ if (
+ (x > 0 && isRed(lastState->grid[x - 1 + y * GRIDWIDTH])) ||
+ (x < GRIDWIDTH - 1 && isRed(lastState->grid[x + 1 + y * GRIDWIDTH])) ||
+ (y > 0 && isRed(lastState->grid[x + (y - 1) * GRIDWIDTH])) ||
+ (y < GRIDHEIGHT - 1 && isRed(lastState->grid[x + (y + 1) * GRIDWIDTH]))
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = BLUE;
+ } else if (
+ x < GRIDWIDTH - 1 &&
+ lastState->grid[x + 1 + y * GRIDWIDTH] == EMPTY
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = EMPTY;
+ } else {
+ game->state.grid[x + y * GRIDWIDTH] = BLUE;
+ }
+ break;
+ case BLUE_UP:
+ if (
+ (x > 0 && isRed(lastState->grid[x - 1 + y * GRIDWIDTH])) ||
+ (x < GRIDWIDTH - 1 && isRed(lastState->grid[x + 1 + y * GRIDWIDTH])) ||
+ (y > 0 && isRed(lastState->grid[x + (y - 1) * GRIDWIDTH])) ||
+ (y < GRIDHEIGHT - 1 && isRed(lastState->grid[x + (y + 1) * GRIDWIDTH]))
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = BLUE;
+ } else if (
+ y > 0 &&
+ lastState->grid[x + (y - 1) * GRIDWIDTH] == EMPTY
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = EMPTY;
+ } else {
+ game->state.grid[x + y * GRIDWIDTH] = BLUE;
+ }
+ break;
+ case BLUE_DOWN:
+ if (
+ (x > 0 && isRed(lastState->grid[x - 1 + y * GRIDWIDTH])) ||
+ (x < GRIDWIDTH - 1 && isRed(lastState->grid[x + 1 + y * GRIDWIDTH])) ||
+ (y > 0 && isRed(lastState->grid[x + (y - 1) * GRIDWIDTH])) ||
+ (y < GRIDHEIGHT - 1 && isRed(lastState->grid[x + (y + 1) * GRIDWIDTH]))
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = BLUE;
+ } else if (
+ y < GRIDHEIGHT - 1 &&
+ lastState->grid[x + (y + 1) * GRIDWIDTH] == EMPTY
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = EMPTY;
+ } else {
+ game->state.grid[x + y * GRIDWIDTH] = BLUE;
+ }
+ break;
+ case EMPTY:
+ // TODO: same as multiple reds
+ if (
+ x > 0 &&
+ lastState->grid[x - 1 + y * GRIDWIDTH] == BLUE_RIGHT
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = BLUE_RIGHT;
+ }
+ if (
+ x < GRIDWIDTH - 1 &&
+ lastState->grid[x + 1 + y * GRIDWIDTH] == BLUE_LEFT
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = BLUE_LEFT;
+ }
+ if (
+ y > 0 &&
+ lastState->grid[x + (y - 1) * GRIDWIDTH] == BLUE_DOWN
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = BLUE_DOWN;
+ }
+ if (
+ y < GRIDHEIGHT - 1 &&
+ lastState->grid[x + (y + 1) * GRIDWIDTH] == BLUE_UP
+ ) {
+ game->state.grid[x + y * GRIDWIDTH] = BLUE_UP;
+ }
+ break;
+ }
+ }
+ }
+
+ if (isBlue(game->state.grid[game->state.goalx + game->state.goaly * GRIDWIDTH])) {
+ // TODO: Win conditions
+ }
+}
diff --git a/src/types.c b/src/types.c
new file mode 100644
index 0000000..fe72e13
--- /dev/null
+++ b/src/types.c
@@ -0,0 +1,90 @@
+#include "all.c"
+
+#define MEM_SIZE (1<<24)
+#define GRIDWIDTH 20
+#define GRIDHEIGHT 16
+#define TICK_LENGTH 200
+
+typedef struct {
+ unsigned char r, g, b, a;
+} Color;
+
+typedef struct {
+ int x, y, w, h;
+ Color fill;
+ Color border;
+} DrawElement;
+
+typedef struct {
+ int len;
+ DrawElement els[2 * GRIDWIDTH * GRIDHEIGHT * 4];
+} DrawList;
+
+typedef struct {
+ char *start;
+ char *end;
+} Arena;
+
+enum {
+ EMPTY,
+ BLACK,
+ RED,
+ YELLOW,
+ RED_UP,
+ RED_DOWN,
+ RED_LEFT,
+ RED_RIGHT,
+ BLUE,
+ BLUE_UP,
+ BLUE_DOWN,
+ BLUE_LEFT,
+ BLUE_RIGHT,
+ N_COLORS,
+};
+
+typedef struct {
+ int width, height;
+} UI;
+
+typedef struct {
+ uint64_t lastTick;
+ char playing;
+ int grid[GRIDWIDTH * GRIDHEIGHT];
+ int goalx, goaly;
+} State;
+
+// Mirror these in src/index.html.in
+enum {
+ INPUT_NONE,
+ INPUT_CLICK,
+ INPUT_RCLICK,
+ INPUT_PAUSE_PLAY,
+};
+
+typedef struct {
+ State state;
+ UI ui;
+ int input;
+ int mousex, mousey;
+} Game;
+
+#define new(a, c, t) ((t *) alloc(a, c, sizeof(t), _Alignof(t)))
+#define affirm(c) while (!(c)) *(volatile int *)0 = 0
+
+static void xmemcpy(void *dst, void *src, ptrdiff_t size) {
+ for (ptrdiff_t i = 0; i < size; i++) {
+ ((char *) dst)[i] = ((char *) src)[i];
+ }
+}
+
+static void *alloc(Arena *a, ptrdiff_t count, ptrdiff_t size, ptrdiff_t align) {
+ ptrdiff_t pad = -(size_t) a->start & (align - 1);
+ affirm(count < (a->end - a->start - pad) / size);
+ char *r = a->start + pad;
+ a->start += pad + size * count;
+ for (ptrdiff_t i = 0; i < count * size; i++) {
+ r[i] = 0;
+ }
+
+ return r;
+}