--- /dev/null
+TARGET := rogue
+BIN_DIR := .
+BUILD_DIR := ./build
+SRC_DIR := ./src
+
+SRC := $(shell find $(SRC_DIR) -name '*.c')
+OBJ := $(SRC:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
+DEP := $(OBJ:.o=.d)
+
+CFLAGS := -Wall -Wextra -Wpedantic -pedantic
+CPPFLAGS := $(addprefix -I,$(shell find $(SRC_DIR) -type d)) -MMD -MP
+LDFLAGS :=
+LDLIBS :=
+
+.SECONDEXPANSION:
+$(BIN_DIR)/$(TARGET): $(OBJ)
+ $(CC) $(LDFLAGS) $(LDLIBS) $^ -o $@
+
+$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
+ mkdir -p $(BUILD_DIR)
+ $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
+
+-include $(DEP)
+
+.PHONY: clean
+clean:
+ $(RM) -r $(BUILD_DIR)
+ $(RM) $(BIN_DIR)/$(TARGET)
+
--- /dev/null
+#include <stdlib.h>
+
+#include "board.h"
+
+board* make_board(int mines, int width, int height)
+{
+ board *b = malloc(sizeof(board));
+
+ b->board_x = width;
+ b->board_y = height;
+ b->mines = mines;
+ b->grid = calloc(width * height, sizeof(tile));
+ b->neighbors = calloc(width * height, sizeof(char));
+
+ for (int n = 0; n < b->mines;)
+ {
+ int x = rand() % b->board_x;
+ int y = rand() % b->board_y;
+ if (!(b->grid[INDEX(b, x, y)] & MINED))
+ {
+ b->grid[INDEX(b, x, y)] |= MINED;
+ ++n;
+ }
+ }
+
+ for (int x = 0; x < b->board_x; ++x)
+ for (int y = 0; y < b->board_y; ++y)
+ {
+ char c = '0';
+
+ if (x + 1 < b->board_x)
+ {
+ if (y + 1 < b->board_y)
+ c += b->grid[INDEX(b, x+1, y+1)] & MINED ? 1 : 0;
+ c += b->grid[INDEX(b, x+1, y)] & MINED ? 1 : 0;
+ if (y > 0)
+ c += b->grid[INDEX(b, x+1, y-1)] & MINED ? 1 : 0;
+ }
+ if (y + 1 < b->board_y)
+ c += b->grid[INDEX(b, x, y+1)] & MINED ? 1 : 0;
+ if (y > 0)
+ c += b->grid[INDEX(b, x, y-1)] & MINED ? 1 : 0;
+ if (x > 0)
+ {
+ if (y + 1 < b->board_y)
+ c += b->grid[INDEX(b, x-1, y+1)] & MINED ? 1 : 0;
+ c += b->grid[INDEX(b, x-1, y)] & MINED ? 1 : 0;
+ if (y > 0)
+ c += b->grid[INDEX(b, x-1, y-1)] & MINED ? 1 : 0;
+ }
+
+ if (c == '0')
+ c = '.';
+
+ b->neighbors[INDEX(b, x, y)] = c;
+ }
+
+ return b;
+}
+
+void destruct_board(board *b)
+{
+ free(b->grid);
+ free(b->neighbors);
+ free(b);
+}
+
+void clear_tile(board *b, int x, int y)
+{
+ if (x < 0 || x >= b->board_x
+ || y < 0 || y >= b->board_y)
+ return;
+
+ tile t = b->grid[INDEX(b, x, y)];
+ char n = b->neighbors[INDEX(b, x, y)];
+
+ /* if (n == '.' && !(t & MINED)) */
+ if (1)
+ {
+ b->grid[INDEX(b, b->cursor_x, b->cursor_y)] |= CLEARED;
+
+ if (x + 1 < b->board_x)
+ {
+ /* if (y + 1 < b->board_y) */
+ /* clear_tile(b, x+1, y+1); */
+ /* clear_tile(b, x+1, y); */
+ /* if (y > 0) */
+ /* clear_tile(b, x+1, y-1); */
+ }
+ /* if (y + 1 < b->board_y) */
+ /* clear_tile(b, x, y+1); */
+ if (y > 0)
+ clear_tile(b, x, y-1);
+ if (x > 0)
+ {
+ /* if (y + 1 < b->board_y) */
+ /* clear_tile(b, x-1, y+1); */
+ clear_tile(b, x-1, y);
+ if (y > 0)
+ clear_tile(b, x-1, y-1);
+ }
+ }
+}
+
--- /dev/null
+#ifndef BOARD_H
+#define BOARD_H
+
+#define CLEARED 0000001
+#define MINED 0000002
+#define FLAGGED 0000004
+
+#define INDEX(B, X, Y) (((Y) * B->board_y) + (X))
+
+typedef unsigned char tile;
+
+typedef struct board {
+ int board_x;
+ int board_y;
+ int cursor_x;
+ int cursor_y;
+ int mines;
+ tile *grid;
+ char *neighbors;
+} board;
+
+board* make_board(int mines, int width, int height);
+void destruct_board(board *b);
+void clear_tile(board *b, int x, int y);
+
+#endif
+
--- /dev/null
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "term.h"
+#include "board.h"
+
+typedef enum
+{
+ nop,
+ quit,
+ up,
+ down,
+ left,
+ right,
+ flag,
+ clear,
+} action;
+
+action proc_key(char c)
+{
+ action a;
+ switch (c)
+ {
+ case 'f':
+ a = flag;
+ break;
+ case ' ':
+ a = clear;
+ break;
+ case 'h':
+ a = left;
+ break;
+ case 'j':
+ a = down;
+ break;
+ case 'k':
+ a = up;
+ break;
+ case 'l':
+ a = right;
+ break;
+ case CTRL_KEY('q'):
+ a = quit;
+ break;
+ default:
+ a = nop;
+ }
+ return a;
+}
+
+void take_action(action a, board *b)
+{
+ switch(a)
+ {
+ case nop:
+ break;
+ case quit:
+ exit(0);
+ break;
+ case up:
+ if (b->cursor_y > 0)
+ b->cursor_y -= 1;
+ break;
+ case down:
+ if (b->cursor_y + 1 < b->board_y)
+ b->cursor_y += 1;
+ break;
+ case left:
+ if (b->cursor_x > 0)
+ b->cursor_x -= 1;
+ break;
+ case right:
+ if (b->cursor_x + 1 < b->board_x)
+ b->cursor_x += 1;
+ break;
+ case flag:
+ b->grid[INDEX(b, b->cursor_x, b->cursor_y)] ^= FLAGGED;
+ break;
+ case clear:
+ clear_tile(b, b->cursor_x, b->cursor_y);
+ break;
+ }
+}
+
+void draw_grid(board *b)
+{
+ for (int y = 0; y < b->board_y; ++y)
+ {
+ for (int x = 0; x < b->board_x; ++x)
+ {
+ if (x == b->cursor_x && y == b->cursor_y)
+ WRITE_CTRL("[7m");
+
+ tile t = b->grid[INDEX(b, x, y)];
+ if (t & FLAGGED)
+ {
+ write(STDOUT_FILENO, "?", 1);
+ }
+ else if (t & CLEARED)
+ {
+ if (t & MINED)
+ {
+ write(STDOUT_FILENO, "*", 1);
+ }
+ else
+ {
+ write(STDOUT_FILENO, b->neighbors + INDEX(b, x, y), 1);
+ }
+ }
+ else
+ {
+ write(STDOUT_FILENO, "#", 1);
+ }
+
+ if (x == b->cursor_x && y == b->cursor_y)
+ WRITE_CTRL("[27m");
+ }
+ write(STDOUT_FILENO, "\n\r", 2);
+ }
+
+ write(STDOUT_FILENO, "\n\rDebug: ", 9);
+
+ char buf[16];
+ sprintf(buf, "(%d,%d) %d", b->cursor_x, b->cursor_y, INDEX(b, b->cursor_x, b->cursor_y));
+ write(STDOUT_FILENO, buf, 16);
+
+ write(STDOUT_FILENO, "\n\r\n\r", 4);
+
+ int x = b->cursor_x;
+ int y = b->cursor_y;
+
+ if (x + 1 < b->board_x)
+ {
+ if (y + 1 < b->board_y)
+ write(STDOUT_FILENO, "1", 1);
+ write(STDOUT_FILENO, "2", 1);
+ if (y > 0)
+ write(STDOUT_FILENO, "3", 1);
+ }
+ if (y + 1 < b->board_y)
+ write(STDOUT_FILENO, "4", 1);
+ if (y > 0)
+ write(STDOUT_FILENO, "5", 1);
+ if (x > 0)
+ {
+ if (y + 1 < b->board_y)
+ write(STDOUT_FILENO, "6", 1);
+ write(STDOUT_FILENO, "7", 1);
+ if (y > 0)
+ write(STDOUT_FILENO, "8", 1);
+ }
+}
+
+int main(int argc, char** argv)
+{
+ init_term();
+ srand(time(NULL));
+
+ board *b = make_board(10, 10, 10);
+
+ char c = '\0';
+ action a = nop;
+
+ while (true)
+ {
+ clear_screen();
+ draw_grid(b);
+
+ c = read_key();
+ a = proc_key(c);
+ take_action(a, b);
+ }
+
+ destruct_board(b);
+
+ return argc;
+}
--- /dev/null
+#include <errno.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "term.h"
+#include "util.h"
+
+struct termios orig_termios;
+void reset_term()
+{
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
+ WRITE_CTRL("[?47l");
+ WRITE_CTRL("8");
+}
+
+void init_term()
+{
+ tcgetattr(STDIN_FILENO, &orig_termios);
+ atexit(reset_term);
+
+ struct termios raw = orig_termios;
+ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+ raw.c_oflag &= ~(OPOST);
+ raw.c_cflag |= (CS8);
+ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ raw.c_cc[VMIN] = 0;
+ raw.c_cc[VTIME] = 1;
+
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
+
+ WRITE_CTRL("[2J");
+ WRITE_CTRL("[H");
+ WRITE_CTRL("7");
+ WRITE_CTRL("[?47h");
+}
+
+char read_key()
+{
+ int nread;
+ char c;
+ /* nread = read(STDIN_FILENO, &c, 1); */
+ while ((nread = read(STDIN_FILENO, &c, 1)) != 1)
+ if (nread == -1 && errno != EAGAIN) die("read");
+
+ return c;
+}
+
+void clear_screen()
+{
+ WRITE_CTRL("[2J");
+ WRITE_CTRL("[H");
+}
+
--- /dev/null
+#ifndef TERM_H
+#define TERM_H
+
+#define ESC(S) "\x1b" S
+#define WRITE_CTRL(S) write(STDOUT_FILENO, ESC(S), sizeof (S))
+#define CTRL_KEY(K) ((K) & 0x1f)
+
+void reset_term();
+void init_term();
+char read_key();
+void clear_screen();
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+void die(const char* s)
+{
+ perror(s);
+ exit(1);
+}
--- /dev/null
+#ifndef UTIL_H
+#define UTIL_H
+
+void die(const char* s);
+
+#endif
+