From b5b4a5d66deefa3858bedf7bc58e6c96340e829b Mon Sep 17 00:00:00 2001 From: Jacob Janzen Date: Wed, 14 Feb 2024 12:01:03 -0600 Subject: major refactor --- .gitignore | 3 +- Makefile | 30 ++++-- cavegen.c | 92 ------------------- cavegen.h | 31 ------- compile_commands.json | 54 +++++++++++ ht.c | 178 ------------------------------------ ht.h | 20 ---- include/cavegen.h | 26 ++++++ include/common.h | 9 ++ include/display.h | 40 ++++++++ include/entity.h | 13 +++ include/ht.h | 29 ++++++ main.c | 246 +++++++------------------------------------------- src/cavegen.c | 92 +++++++++++++++++++ src/display.c | 201 +++++++++++++++++++++++++++++++++++++++++ src/ht.c | 224 +++++++++++++++++++++++++++++++++++++++++++++ 16 files changed, 745 insertions(+), 543 deletions(-) delete mode 100644 cavegen.c delete mode 100644 cavegen.h create mode 100644 compile_commands.json delete mode 100644 ht.c delete mode 100644 ht.h create mode 100644 include/cavegen.h create mode 100644 include/common.h create mode 100644 include/display.h create mode 100644 include/entity.h create mode 100644 include/ht.h create mode 100644 src/cavegen.c create mode 100644 src/display.c create mode 100644 src/ht.c diff --git a/.gitignore b/.gitignore index 20ae36e..8cb141f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,5 @@ tmp/ *.o -/main +build/ .ccls-cache -main.dSYM/ diff --git a/Makefile b/Makefile index 6ed373f..61be5b0 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,27 @@ -all: main +CC=clang +SRCDIR=src +BUILDDIR=build +TARGET=$(BUILDDIR)/main -CFLAGS=-g +INCLUDE_PATHS=-Iinclude +LDFLAGS=-lcurses -main: main.c cavegen.o ht.o - $(CC) main.c cavegen.o ht.o -lcurses -o main -D_XOPEN_SOURCE_EXTENDED $(CFLAGS) +SRCS=$(wildcard $(SRCDIR)/*.c) +OBJS=$(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/%.o,$(SRCS)) -cavegen.o: cavegen.c - $(CC) cavegen.c -c -o cavegen.o $(CFLAGS) +all: $(TARGET) -ht.o: ht.c - $(CC) ht.c -c -o ht.o $(CFLAGS) +$(BUILDDIR)/%.o: $(SRCDIR)/%.c + mkdir -p $(BUILDDIR) + $(CC) -c $(CFLAGS) $(INCLUDE_PATHS) $^ -o $@ + +$(BUILDDIR)/main.o: main.c + mkdir -p $(BUILDDIR) + $(CC) -c $(CFLAGS) $(INCLUDE_PATHS) $^ -o $@ + +$(TARGET): $(OBJS) $(BUILDDIR)/main.o + mkdir -p $(BUILDDIR) + $(CC) -o $@ $(LDFLAGS) $^ clean: - rm -rf main cavegen.o + rm -rf $(BUILDDIR) diff --git a/cavegen.c b/cavegen.c deleted file mode 100644 index 1365e9d..0000000 --- a/cavegen.c +++ /dev/null @@ -1,92 +0,0 @@ -#include "cavegen.h" - -#include -#include -#include - -#define MAX_STEPS 200 -#define NUM_WALKERS 100 - -enum direction { - UP, - LEFT, - RIGHT, - DOWN, - NUM_DIRS, -}; - -void create_cave(struct map *map) -{ - map->width = WIDTH; - map->height = HEIGHT; - - // create a map consisting entirely of walls - map->map = malloc(sizeof(enum tile_type) * HEIGHT * WIDTH); - for (int i = 0; i < HEIGHT; ++i) { - for (int j = 0; j < WIDTH; ++j) { - map->map[i * WIDTH + j] = WALL; - } - } - - // start in the middle of the screen - int start_x = WIDTH / 2; - int start_y = HEIGHT / 2; - - // make the starting point GROUND - map->map[start_y * WIDTH + start_x] = GROUND; - - // setup the open tiles - struct point *open_tiles = malloc(sizeof(struct point) * HEIGHT * WIDTH); - - int num_open_tiles = 1; - open_tiles[0].x = start_x; - open_tiles[0].y = start_y; - - for (int i = 0; i < NUM_WALKERS; ++i) { - // get a random open point - struct point curr_point = open_tiles[rand() % num_open_tiles]; - - int x_pos = curr_point.x; - int y_pos = curr_point.y; - - // iterate until the walk exits the array or MAX_STEPS is reached - for (int j = 1; j < MAX_STEPS - 1 && x_pos < WIDTH - 1 && x_pos >= 1 && - y_pos < HEIGHT - 1 && y_pos >= 1; - ++j) { - // add new open point if the current point is still a wall - if (map->map[y_pos * WIDTH + x_pos] == WALL) { - open_tiles[num_open_tiles].x = x_pos; - open_tiles[num_open_tiles].y = y_pos; - ++num_open_tiles; - } - - map->map[y_pos * WIDTH + x_pos] = GROUND; // assign ground - - // move in a random direction - enum direction dir = rand() % NUM_DIRS; - switch (dir) { - case UP : --y_pos; break; - case LEFT : --x_pos; break; - case RIGHT : ++x_pos; break; - case DOWN : ++y_pos; break; - default : exit(EXIT_FAILURE); // should not occur - } - } - } - - // assign the up stair and remove from open tiles - int in = rand() % num_open_tiles; - map->entry_point = open_tiles[in]; - open_tiles[in] = open_tiles[num_open_tiles - 1]; - --num_open_tiles; - map->map[map->entry_point.y * WIDTH + map->entry_point.x] = UP_STAIR; - - // assign the down stair and remove from open tiles - in = rand() % num_open_tiles; - struct point down = open_tiles[in]; - open_tiles[in] = open_tiles[num_open_tiles - 1]; - --num_open_tiles; - map->map[down.y * WIDTH + down.x] = DOWN_STAIR; - - free(open_tiles); -} diff --git a/cavegen.h b/cavegen.h deleted file mode 100644 index 05d2fd2..0000000 --- a/cavegen.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef CAVEGEN_H_ -#define CAVEGEN_H_ - -#include - -#define HEIGHT 100 -#define WIDTH 180 - -enum tile_type { - WALL, - GROUND, - UP_STAIR, - DOWN_STAIR, -}; - -struct point { - int x; - int y; -}; - -struct map { - enum tile_type *map; - struct point entry_point; - - int width; - int height; -}; - -void create_cave(struct map *map); - -#endif // CAVEGEN_H_ diff --git a/compile_commands.json b/compile_commands.json new file mode 100644 index 0000000..d26e416 --- /dev/null +++ b/compile_commands.json @@ -0,0 +1,54 @@ +[ + { + "arguments": [ + "/usr/bin/clang", + "-c", + "-Iinclude", + "-o", + "build/cavegen.o", + "src/cavegen.c" + ], + "directory": "/Users/jjanzen/Documents/projects/mapgen", + "file": "/Users/jjanzen/Documents/projects/mapgen/src/cavegen.c", + "output": "/Users/jjanzen/Documents/projects/mapgen/build/cavegen.o" + }, + { + "arguments": [ + "/usr/bin/clang", + "-c", + "-Iinclude", + "-o", + "build/display.o", + "src/display.c" + ], + "directory": "/Users/jjanzen/Documents/projects/mapgen", + "file": "/Users/jjanzen/Documents/projects/mapgen/src/display.c", + "output": "/Users/jjanzen/Documents/projects/mapgen/build/display.o" + }, + { + "arguments": [ + "/usr/bin/clang", + "-c", + "-Iinclude", + "-o", + "build/ht.o", + "src/ht.c" + ], + "directory": "/Users/jjanzen/Documents/projects/mapgen", + "file": "/Users/jjanzen/Documents/projects/mapgen/src/ht.c", + "output": "/Users/jjanzen/Documents/projects/mapgen/build/ht.o" + }, + { + "arguments": [ + "/usr/bin/clang", + "-c", + "-Iinclude", + "-o", + "build/main.o", + "main.c" + ], + "directory": "/Users/jjanzen/Documents/projects/mapgen", + "file": "/Users/jjanzen/Documents/projects/mapgen/main.c", + "output": "/Users/jjanzen/Documents/projects/mapgen/build/main.o" + } +] diff --git a/ht.c b/ht.c deleted file mode 100644 index e5e0f0f..0000000 --- a/ht.c +++ /dev/null @@ -1,178 +0,0 @@ -#include "ht.h" - -#include -#include -#include - -#define SIZE 1024 - -struct node { - char *key; - void *val; - struct node *next; -}; - -struct hash_table { - struct node **vals; - int max_size; - int size; - - int curr_index; - struct node *curr_node; - bool iterating; -}; - -static unsigned long djb2_hash(char *str) -{ - unsigned long hash = 5381; - int c; - - // hash * 33 + c - while ((c = *str++)) hash = ((hash << 5) + hash) + c; - - return hash; -} - -ht_t *ht_create(void) -{ - ht_t *h = malloc(sizeof(ht_t)); - h->max_size = SIZE; - h->size = 0; - h->vals = malloc(sizeof(struct node) * h->max_size); - h->curr_index = 0; - h->curr_node = NULL; - h->iterating = false; - - return h; -} - -void ht_destroy(ht_t *h) -{ - // only destroy if h isn't NULL - if (h) { - // only destroy vals if it isn't NULL - if (h->vals) { - // iterate through the hash values - for (int i = 0; i < h->max_size; ++i) { - // iterate through the linked list and remove the value - struct node *curr = h->vals[i]; - struct node *prev; - while (curr) { - prev = curr; - curr = curr->next; - free(prev->key); - free(prev); - } - } - free(h->vals); - } - free(h); - } -} - -void *ht_find(ht_t *h, char *key) -{ - unsigned int hash = djb2_hash(key) % h->max_size; - - // exit early if hash isn't in table - if (!h->vals[hash]) - return NULL; - - // iterate through values stored at `hash`; exit early if found - struct node *curr = h->vals[hash]; - while (curr) { - if (strcmp(key, curr->key) == 0) - return curr->val; - curr = curr->next; - } - - return NULL; -} - -void ht_insert(ht_t *h, char *key, void *val) -{ - unsigned int hash = djb2_hash(key) % h->max_size; - - // create a node - int len = strlen(key); - struct node *new = malloc(sizeof(struct node)); - new->key = malloc(sizeof(char) * (len + 1)); - for (int i = 0; i < len; ++i) { - new->key[i] = key[i]; - } - new->key[len] = 0; - new->val = val; - - // insert node at beginning of list - if (h->vals[hash]) { - new->next = h->vals[hash]; - } - h->vals[hash] = new; - - ++(h->size); -} - -void ht_delete(ht_t *h, char *key) -{ - unsigned int hash = djb2_hash(key) % h->max_size; - - if (!h->vals[hash]) - return; // exit if the hash is not found - - // remove the key from the front of the list if it's there - if (strcmp(key, h->vals[hash]->key) == 0) { - struct node *temp = h->vals[hash]; - h->vals[hash] = h->vals[hash]->next; - free(temp->key); - free(temp); - } else { - // iterate through list and remove once a match is found - struct node *prev = h->vals[hash]; - struct node *curr = h->vals[hash]->next; - while (curr) { - if (strcmp(key, curr->key) == 0) { - prev->next = curr->next; - free(curr->key); - free(curr); - return; - } - - prev = curr; - curr = curr->next; - } - } -} - -int ht_size(ht_t *h) -{ - return h->size; -} - -void ht_iter_init(ht_t *h) -{ - h->curr_index = 0; - h->curr_node = h->vals[0]; -} - -void *ht_iter_next(ht_t *h) -{ - void *out; - - // return NULL if we've reached the end - if (!h->curr_node && h->curr_index >= h->max_size) - return NULL; - - // look for next index with node if current node is NULL - if (!h->curr_node) { - while (!h->curr_node) { - h->curr_node = h->vals[h->curr_index++]; - if (h->curr_index >= h->max_size) - return NULL; - } - } - - // get the value and move to the next node - out = h->curr_node->val; - h->curr_node = h->curr_node->next; - return out; -} diff --git a/ht.h b/ht.h deleted file mode 100644 index e2582c6..0000000 --- a/ht.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef HT_H_ -#define HT_H_ - -#include - -typedef struct hash_table ht_t; - -ht_t *ht_create(void); -void ht_destroy(ht_t *h); - -void *ht_find(ht_t *h, char *key); -void ht_insert(ht_t *h, char *key, void *val); -void ht_delete(ht_t *h, char *key); - -int ht_size(ht_t *h); - -void ht_iter_init(ht_t *h); -void *ht_iter_next(ht_t *h); - -#endif // HT_H_ diff --git a/include/cavegen.h b/include/cavegen.h new file mode 100644 index 0000000..b6ed1e4 --- /dev/null +++ b/include/cavegen.h @@ -0,0 +1,26 @@ +#ifndef CAVEGEN_H_ +#define CAVEGEN_H_ + +#include "common.h" + +#define HEIGHT 100 +#define WIDTH 180 + +enum tile_type { + WALL, + GROUND, + UP_STAIR, + DOWN_STAIR, +}; + +struct map { + enum tile_type *map; + struct point entry_point; + + int width; + int height; +}; + +void create_cave(struct map *map); + +#endif // CAVEGEN_H_ diff --git a/include/common.h b/include/common.h new file mode 100644 index 0000000..11b11d4 --- /dev/null +++ b/include/common.h @@ -0,0 +1,9 @@ +#ifndef COMMON_H_ +#define COMMON_H_ + +struct point { + int x; + int y; +}; + +#endif // COMMON_H_ diff --git a/include/display.h b/include/display.h new file mode 100644 index 0000000..c4f5249 --- /dev/null +++ b/include/display.h @@ -0,0 +1,40 @@ +#ifndef DISPLAY_H_ +#define DISPLAY_H_ + +#include + +#include "cavegen.h" +#include "entity.h" +#include "ht.h" + +#define MAIN_PANEL_WIDTH 100 +#define MAIN_PANEL_HEIGHT 41 +#define INSTRUCTION_PANEL_WIDTH 32 +#define INSTRUCTION_PANEL_HEIGHT 39 +#define MESSAGE_PANEL_WIDTH 100 +#define MESSAGE_PANEL_HEIGHT 3 +#define STATUS_PANEL_WIDTH 32 +#define STATUS_PANEL_HEIGHT 5 + +typedef struct windows display_t; + +enum action { + ACTION_NONE, + ACTION_EXIT, + ACTION_DOWN, + ACTION_UP, + ACTION_LEFT, + ACTION_RIGHT, + ACTION_STAIR_DOWN, + ACTION_STAIR_UP, + NUM_ACTIONS, +}; + +display_t *display_init(void); +void display_map(display_t *disp, struct map *map, ht_t *entities); +void display_instructions(display_t *disp); +void display_message(display_t *disp, char *msg); +void display_status(display_t *disp, struct entity *entity); +enum action display_process_input(void); + +#endif // DISPLAY_H_ diff --git a/include/entity.h b/include/entity.h new file mode 100644 index 0000000..b003efe --- /dev/null +++ b/include/entity.h @@ -0,0 +1,13 @@ +#ifndef ENTITY_H_ +#define ENTITY_H_ + +#include "common.h" + +struct entity { + struct point p; + char *disp_ch; + bool solid; + bool visible; +}; + +#endif // ENTITY_H_ diff --git a/include/ht.h b/include/ht.h new file mode 100644 index 0000000..ef15540 --- /dev/null +++ b/include/ht.h @@ -0,0 +1,29 @@ +#ifndef HT_H_ +#define HT_H_ + +#include + +typedef struct hash_table ht_t; + +struct kvp { + char *key; + void *val; +}; + +// construct and destructor +ht_t *ht_create(int size); +void ht_destroy(ht_t *h); + +// accessors +void *ht_find(ht_t *h, char *key); +void ht_insert(ht_t *h, char *key, void *val); +void ht_delete(ht_t *h, char *key); + +// queries +int ht_size(ht_t *h); + +// iterator +void ht_iter_init(ht_t *h); +struct kvp ht_iter_next(ht_t *h); + +#endif // HT_H_ diff --git a/main.c b/main.c index a693ae0..2d1e6c0 100644 --- a/main.c +++ b/main.c @@ -4,169 +4,11 @@ #include #include "cavegen.h" +#include "common.h" +#include "display.h" +#include "entity.h" #include "ht.h" -#define MAIN_PANEL_WIDTH 100 -#define MAIN_PANEL_HEIGHT 41 -#define INSTRUCTION_PANEL_WIDTH 32 -#define INSTRUCTION_PANEL_HEIGHT 39 -#define MESSAGE_PANEL_WIDTH 100 -#define MESSAGE_PANEL_HEIGHT 3 -#define STATUS_PANEL_WIDTH 32 -#define STATUS_PANEL_HEIGHT 5 - -#define MAX_ENTITIES 100 - -struct entity { - struct point p; - char *disp_ch; - bool solid; - bool visible; -}; - -struct windows { - WINDOW *main; - WINDOW *inst; - WINDOW *msgs; - WINDOW *stat; -}; - -WINDOW *create_newwin(int height, int width, int starty, int startx) - -{ - WINDOW *local_win = newwin(height, width, starty, startx); - box(local_win, 0, 0); - wrefresh(local_win); - - return local_win; -} - -void initialize(void) -{ - setlocale(LC_ALL, ""); // allow extended ASCII - - initscr(); // initialize curses - - // exit on unsupported consoles - if (LINES < MAIN_PANEL_HEIGHT + MESSAGE_PANEL_HEIGHT || - COLS < MAIN_PANEL_WIDTH + INSTRUCTION_PANEL_WIDTH || !has_colors()) { - endwin(); - fprintf( - stderr, - "a color terminal is required with at least %dx%d characters\n", - INSTRUCTION_PANEL_WIDTH + MAIN_PANEL_WIDTH, - MAIN_PANEL_HEIGHT + MESSAGE_PANEL_HEIGHT - ); - exit(1); - } - - // configure curses if startup was successful - raw(); // disable line buffering - keypad(stdscr, TRUE); // enable reading function keys - noecho(); // don't print input - curs_set(0); // disable the cursor - start_color(); // enable colours - - // setup colours - init_pair(1, COLOR_WHITE, COLOR_BLACK); - init_pair(2, COLOR_BLACK, COLOR_RED); - wattron(stdscr, COLOR_PAIR(1)); - refresh(); -} - -void display_map(WINDOW *win, struct map *map, ht_t *entities) -{ - // print map - struct entity *camera = ht_find(entities, "camera"); - - for (int i = 1; i < MAIN_PANEL_HEIGHT - 1; ++i) { - for (int j = 1; j < MAIN_PANEL_WIDTH - 1; ++j) { - int map_i = i - 1 + camera->p.y; - int map_j = j - 1 + camera->p.x; - - if (map_i > map->height || map_j > map->width || map_i < 0 || - map_j < 0) { - mvwaddch(win, i, j, ' '); - } else { - switch (map->map[map_i * map->width + map_j]) { - case GROUND : mvwaddch(win, i, j, '.'); break; - case UP_STAIR : mvwaddch(win, i, j, '<'); break; - case DOWN_STAIR : - wattron(win, COLOR_PAIR(2)); - mvwaddch(win, i, j, '>'); - wattroff(win, COLOR_PAIR(2)); - break; - case WALL : - if (map_i > 0 && - map->map[(map_i - 1) * map->width + map_j] != WALL) { - mvwprintw(win, i, j, "█"); - } else if (map_i < map->width - 1 && map->map[(map_i + 1) * map->width + map_j] != WALL) { - mvwprintw(win, i, j, "█"); - } else if (map_j > 0 && map->map[map_i * map->width + map_j - 1] != WALL) { - mvwprintw(win, i, j, "█"); - } else if (map_j < map->width - 1 && map->map[map_i * map->width + map_j + 1] != WALL) { - mvwprintw(win, i, j, "█"); - } else { - mvwaddch(win, i, j, ' '); - } - break; - default : mvwaddch(win, i, j, ' '); - } - } - } - } - - // print entities - ht_iter_init(entities); - struct entity *e; - while ((e = ht_iter_next(entities))) { - if (e->visible) { - mvwprintw( - win, e->p.y - camera->p.y + 1, e->p.x - camera->p.x + 1, - e->disp_ch - ); - } - } - - wrefresh(win); -} - -void display_instructions(WINDOW *win) -{ - mvwprintw(win, 1, 2, "h - move left"); - mvwprintw(win, 2, 2, "j - move down"); - mvwprintw(win, 3, 2, "k - move up"); - mvwprintw(win, 4, 2, "l - move right"); - mvwprintw(win, 5, 2, "> - move down staircase"); - mvwprintw(win, 6, 2, "< - exit via staircase"); - wrefresh(win); -} - -void display_message(WINDOW *win, char *msg) -{ - for (int i = 1; i < MESSAGE_PANEL_WIDTH - 1; ++i) { - mvwaddch(win, 1, i, ' '); - } - - mvwprintw(win, 1, 1, msg); - wrefresh(win); -} - -void display_status(WINDOW *win, struct entity *entity) -{ - for (int i = 1; i < STATUS_PANEL_HEIGHT - 1; ++i) { - for (int j = 1; j < STATUS_PANEL_WIDTH - 1; ++j) { - mvwaddch(win, i, j, ' '); - } - } - - mvwprintw(win, 1, 2, "HP:"); - mvwprintw(win, 2, 2, "STAMINA:"); - mvwprintw(win, 3, 2, "MANA:"); - - wrefresh(win); -} - bool entity_set_pos(struct entity *e, struct point p, struct map *map) { if (e->solid) { @@ -183,59 +25,38 @@ bool entity_set_pos(struct entity *e, struct point p, struct map *map) return true; } -void create_windows(struct windows *wins) -{ - wins->inst = create_newwin( - INSTRUCTION_PANEL_HEIGHT, INSTRUCTION_PANEL_WIDTH, - (LINES - INSTRUCTION_PANEL_HEIGHT - STATUS_PANEL_HEIGHT) / 2 + 1, - (COLS - MAIN_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH) / 2 + - MAIN_PANEL_WIDTH - 1 - ); - wins->msgs = create_newwin( - MESSAGE_PANEL_HEIGHT, MESSAGE_PANEL_WIDTH, - (LINES - MAIN_PANEL_HEIGHT - MESSAGE_PANEL_HEIGHT) / 2 + - MAIN_PANEL_HEIGHT, - (COLS - MESSAGE_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH) / 2 - ); - wins->stat = create_newwin( - STATUS_PANEL_HEIGHT, STATUS_PANEL_WIDTH, - (LINES - INSTRUCTION_PANEL_HEIGHT - STATUS_PANEL_HEIGHT) / 2 + - INSTRUCTION_PANEL_HEIGHT, - (COLS - MAIN_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH) / 2 + - MAIN_PANEL_WIDTH - 1 - ); - wins->main = create_newwin( - MAIN_PANEL_HEIGHT, MAIN_PANEL_WIDTH, - (LINES - MAIN_PANEL_HEIGHT - MESSAGE_PANEL_HEIGHT) / 2 + 1, - (COLS - MAIN_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH) / 2 - ); -} - -bool process_input(int ch, ht_t *entities, struct map *map) +bool game_update( + display_t *disp, enum action action, ht_t *entities, struct map *map +) { struct entity *player = ht_find(entities, "player"); struct entity *camera = ht_find(entities, "camera"); struct point newp = player->p; struct point newp_cam = camera->p; - switch (ch) { - case 'k' : + switch (action) { + case ACTION_EXIT : return true; + case ACTION_UP : --newp.y; --newp_cam.y; + display_message(disp, "moving up"); break; - case 'j' : + case ACTION_DOWN : ++newp.y; ++newp_cam.y; + display_message(disp, "moving down"); break; - case 'h' : + case ACTION_LEFT : --newp.x; --newp_cam.x; + display_message(disp, "moving left"); break; - case 'l' : + case ACTION_RIGHT : ++newp.x; ++newp_cam.x; + display_message(disp, "moving right"); break; - case '>' : + case ACTION_STAIR_DOWN : if (map->map[player->p.y * map->width + player->p.x] == DOWN_STAIR) { free(map->map); create_cave(map); @@ -243,13 +64,20 @@ bool process_input(int ch, ht_t *entities, struct map *map) newp = map->entry_point; newp_cam.x = map->entry_point.x - MAIN_PANEL_WIDTH / 2 + 1; newp_cam.y = map->entry_point.y - MAIN_PANEL_HEIGHT / 2 + 1; + display_message(disp, "moving down stairs"); + } else { + display_message(disp, "no stairs to go down"); } break; - case '<' : + case ACTION_STAIR_UP : if (map->map[player->p.y * WIDTH + player->p.x] == UP_STAIR) { + display_message(disp, "moving up stairs"); return true; + } else { + display_message(disp, "no stairs to go up"); } break; + default : display_message(disp, "unrecognized command"); break; } if (entity_set_pos(player, newp, map)) @@ -263,18 +91,14 @@ int main(void) unsigned int seed = time(NULL); srand(seed); - initialize(); - - // create windows - struct windows windows; - create_windows(&windows); + display_t *disp = display_init(); // create the map struct map map; create_cave(&map); // create the entity map - ht_t *entities = ht_create(); + ht_t *entities = ht_create(1); // create the camera struct entity camera; @@ -299,17 +123,17 @@ int main(void) entity_set_pos(&camera, cam_p, &map); // start displaying things - display_map(windows.main, &map, entities); - display_instructions(windows.inst); - display_status(windows.stat, &player); - display_message(windows.msgs, ""); + display_map(disp, &map, entities); + display_instructions(disp); + display_status(disp, &player); + display_message(disp, ""); int ch; bool done = false; - while (!done && (ch = getch()) != KEY_F(1)) { - if (process_input(ch, entities, &map)) - break; - display_map(windows.main, &map, entities); + while (!done) { + enum action action = display_process_input(); + done = game_update(disp, action, entities, &map); + display_map(disp, &map, entities); } free(map.map); diff --git a/src/cavegen.c b/src/cavegen.c new file mode 100644 index 0000000..1365e9d --- /dev/null +++ b/src/cavegen.c @@ -0,0 +1,92 @@ +#include "cavegen.h" + +#include +#include +#include + +#define MAX_STEPS 200 +#define NUM_WALKERS 100 + +enum direction { + UP, + LEFT, + RIGHT, + DOWN, + NUM_DIRS, +}; + +void create_cave(struct map *map) +{ + map->width = WIDTH; + map->height = HEIGHT; + + // create a map consisting entirely of walls + map->map = malloc(sizeof(enum tile_type) * HEIGHT * WIDTH); + for (int i = 0; i < HEIGHT; ++i) { + for (int j = 0; j < WIDTH; ++j) { + map->map[i * WIDTH + j] = WALL; + } + } + + // start in the middle of the screen + int start_x = WIDTH / 2; + int start_y = HEIGHT / 2; + + // make the starting point GROUND + map->map[start_y * WIDTH + start_x] = GROUND; + + // setup the open tiles + struct point *open_tiles = malloc(sizeof(struct point) * HEIGHT * WIDTH); + + int num_open_tiles = 1; + open_tiles[0].x = start_x; + open_tiles[0].y = start_y; + + for (int i = 0; i < NUM_WALKERS; ++i) { + // get a random open point + struct point curr_point = open_tiles[rand() % num_open_tiles]; + + int x_pos = curr_point.x; + int y_pos = curr_point.y; + + // iterate until the walk exits the array or MAX_STEPS is reached + for (int j = 1; j < MAX_STEPS - 1 && x_pos < WIDTH - 1 && x_pos >= 1 && + y_pos < HEIGHT - 1 && y_pos >= 1; + ++j) { + // add new open point if the current point is still a wall + if (map->map[y_pos * WIDTH + x_pos] == WALL) { + open_tiles[num_open_tiles].x = x_pos; + open_tiles[num_open_tiles].y = y_pos; + ++num_open_tiles; + } + + map->map[y_pos * WIDTH + x_pos] = GROUND; // assign ground + + // move in a random direction + enum direction dir = rand() % NUM_DIRS; + switch (dir) { + case UP : --y_pos; break; + case LEFT : --x_pos; break; + case RIGHT : ++x_pos; break; + case DOWN : ++y_pos; break; + default : exit(EXIT_FAILURE); // should not occur + } + } + } + + // assign the up stair and remove from open tiles + int in = rand() % num_open_tiles; + map->entry_point = open_tiles[in]; + open_tiles[in] = open_tiles[num_open_tiles - 1]; + --num_open_tiles; + map->map[map->entry_point.y * WIDTH + map->entry_point.x] = UP_STAIR; + + // assign the down stair and remove from open tiles + in = rand() % num_open_tiles; + struct point down = open_tiles[in]; + open_tiles[in] = open_tiles[num_open_tiles - 1]; + --num_open_tiles; + map->map[down.y * WIDTH + down.x] = DOWN_STAIR; + + free(open_tiles); +} diff --git a/src/display.c b/src/display.c new file mode 100644 index 0000000..0b08390 --- /dev/null +++ b/src/display.c @@ -0,0 +1,201 @@ +#include "display.h" + +#include +#include +#include + +#include "entity.h" + +struct windows { + WINDOW *main; + WINDOW *inst; + WINDOW *msgs; + WINDOW *stat; +}; + +static WINDOW *create_newwin(int height, int width, int y, int x) +{ + WINDOW *local_win = newwin(height, width, y, x); + box(local_win, 0, 0); + wrefresh(local_win); + + return local_win; +} + +static display_t *create_windows(void) +{ + display_t *wins = malloc(sizeof(display_t)); + + wins->inst = create_newwin( + INSTRUCTION_PANEL_HEIGHT, INSTRUCTION_PANEL_WIDTH, + (LINES - INSTRUCTION_PANEL_HEIGHT - STATUS_PANEL_HEIGHT) / 2 + 1, + (COLS - MAIN_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH) / 2 + + MAIN_PANEL_WIDTH - 1 + ); + wins->msgs = create_newwin( + MESSAGE_PANEL_HEIGHT, MESSAGE_PANEL_WIDTH, + (LINES - MAIN_PANEL_HEIGHT - MESSAGE_PANEL_HEIGHT) / 2 + + MAIN_PANEL_HEIGHT, + (COLS - MESSAGE_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH) / 2 + ); + wins->stat = create_newwin( + STATUS_PANEL_HEIGHT, STATUS_PANEL_WIDTH, + (LINES - INSTRUCTION_PANEL_HEIGHT - STATUS_PANEL_HEIGHT) / 2 + + INSTRUCTION_PANEL_HEIGHT, + (COLS - MAIN_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH) / 2 + + MAIN_PANEL_WIDTH - 1 + ); + wins->main = create_newwin( + MAIN_PANEL_HEIGHT, MAIN_PANEL_WIDTH, + (LINES - MAIN_PANEL_HEIGHT - MESSAGE_PANEL_HEIGHT) / 2 + 1, + (COLS - MAIN_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH) / 2 + ); + + return wins; +} + +display_t *display_init(void) +{ + setlocale(LC_ALL, ""); // allow extended ASCII + + initscr(); // initialize curses + + // exit on unsupported consoles + if (LINES < MAIN_PANEL_HEIGHT + MESSAGE_PANEL_HEIGHT || + COLS < MAIN_PANEL_WIDTH + INSTRUCTION_PANEL_WIDTH || !has_colors()) { + endwin(); + fprintf( + stderr, + "a color terminal is required with at least %dx%d characters\n", + INSTRUCTION_PANEL_WIDTH + MAIN_PANEL_WIDTH, + MAIN_PANEL_HEIGHT + MESSAGE_PANEL_HEIGHT + ); + return NULL; + } + + // configure curses if startup was successful + raw(); // disable line buffering + keypad(stdscr, TRUE); // enable reading function keys + noecho(); // don't print input + curs_set(0); // disable the cursor + start_color(); // enable colours + + // setup colours + init_pair(1, COLOR_WHITE, COLOR_BLACK); + init_pair(2, COLOR_BLACK, COLOR_RED); + wattron(stdscr, COLOR_PAIR(1)); + refresh(); + + return create_windows(); +} + +void display_map(display_t *disp, struct map *map, ht_t *entities) +{ + // print map + struct entity *camera = ht_find(entities, "camera"); + + for (int i = 1; i < MAIN_PANEL_HEIGHT - 1; ++i) { + for (int j = 1; j < MAIN_PANEL_WIDTH - 1; ++j) { + int map_i = i - 1 + camera->p.y; + int map_j = j - 1 + camera->p.x; + + if (map_i > map->height || map_j > map->width || map_i < 0 || + map_j < 0) { + mvwaddch(disp->main, i, j, ' '); + } else { + switch (map->map[map_i * map->width + map_j]) { + case GROUND : mvwaddch(disp->main, i, j, '.'); break; + case UP_STAIR : mvwaddch(disp->main, i, j, '<'); break; + case DOWN_STAIR : + wattron(disp->main, COLOR_PAIR(2)); + mvwaddch(disp->main, i, j, '>'); + wattroff(disp->main, COLOR_PAIR(2)); + break; + case WALL : + if (map_i > 0 && + map->map[(map_i - 1) * map->width + map_j] != WALL) { + mvwprintw(disp->main, i, j, "█"); + } else if (map_i < map->width - 1 && map->map[(map_i + 1) * map->width + map_j] != WALL) { + mvwprintw(disp->main, i, j, "█"); + } else if (map_j > 0 && map->map[map_i * map->width + map_j - 1] != WALL) { + mvwprintw(disp->main, i, j, "█"); + } else if (map_j < map->width - 1 && map->map[map_i * map->width + map_j + 1] != WALL) { + mvwprintw(disp->main, i, j, "█"); + } else { + mvwaddch(disp->main, i, j, ' '); + } + break; + default : mvwaddch(disp->main, i, j, ' '); + } + } + } + } + + // print entities + ht_iter_init(entities); + struct kvp kvp = ht_iter_next(entities); + while (kvp.key) { + struct entity *e = kvp.val; + if (e->visible) { + mvwprintw( + disp->main, e->p.y - camera->p.y + 1, e->p.x - camera->p.x + 1, + e->disp_ch + ); + } + + kvp = ht_iter_next(entities); + } + + wrefresh(disp->main); +} + +void display_instructions(display_t *disp) +{ + mvwprintw(disp->inst, 1, 2, "h - move left"); + mvwprintw(disp->inst, 2, 2, "j - move down"); + mvwprintw(disp->inst, 3, 2, "k - move up"); + mvwprintw(disp->inst, 4, 2, "l - move right"); + mvwprintw(disp->inst, 5, 2, "> - move down staircase"); + mvwprintw(disp->inst, 6, 2, "< - exit via staircase"); + wrefresh(disp->inst); +} + +void display_message(display_t *disp, char *msg) +{ + for (int i = 1; i < MESSAGE_PANEL_WIDTH - 1; ++i) { + mvwaddch(disp->msgs, 1, i, ' '); + } + + mvwprintw(disp->msgs, 1, 1, msg); + wrefresh(disp->msgs); +} + +void display_status(display_t *disp, struct entity *entity) +{ + for (int i = 1; i < STATUS_PANEL_HEIGHT - 1; ++i) { + for (int j = 1; j < STATUS_PANEL_WIDTH - 1; ++j) { + mvwaddch(disp->stat, i, j, ' '); + } + } + + mvwprintw(disp->stat, 1, 2, "HP:"); + mvwprintw(disp->stat, 2, 2, "STAMINA:"); + mvwprintw(disp->stat, 3, 2, "MANA:"); + + wrefresh(disp->stat); +} + +enum action display_process_input(void) +{ + int ch = getch(); + switch (ch) { + case 'k' : return ACTION_UP; + case 'j' : return ACTION_DOWN; + case 'h' : return ACTION_LEFT; + case 'l' : return ACTION_RIGHT; + case '>' : return ACTION_STAIR_DOWN; + case '<' : return ACTION_STAIR_UP; + case KEY_F(1) : return ACTION_EXIT; + default : return ACTION_NONE; + } +} diff --git a/src/ht.c b/src/ht.c new file mode 100644 index 0000000..39093eb --- /dev/null +++ b/src/ht.c @@ -0,0 +1,224 @@ +#include "ht.h" + +#include +#include +#include + +#define SIZE 1024 + +struct node { + char *key; + void *val; + struct node *next; +}; + +struct hash_table { + struct node **vals; + int max_size; + int size; + + int curr_index; + struct node *curr_node; + bool iterating; +}; + +static void rehash(ht_t *h, int newsize) +{ + ht_t *new_h = ht_create(newsize); + + ht_iter_init(h); + + struct kvp kvp = ht_iter_next(h); + while (kvp.key) { + ht_insert(new_h, kvp.key, kvp.val); + kvp = ht_iter_next(h); + } + + // only destroy vals if it isn't NULL + if (h->vals) { + // iterate through the hash values + for (int i = 0; i < h->max_size; ++i) { + // iterate through the linked list and remove the value + struct node *curr = h->vals[i]; + struct node *prev; + while (curr) { + prev = curr; + curr = curr->next; + free(prev->key); + free(prev); + } + } + free(h->vals); + } + + h->max_size = newsize; + h->vals = new_h->vals; + h->curr_index = 0; + h->curr_node = NULL; + h->iterating = false; +} + +static unsigned long djb2_hash(char *str) +{ + unsigned long hash = 5381; + int c; + + // hash * 33 + c + while ((c = *str++)) hash = ((hash << 5) + hash) + c; + + return hash; +} + +ht_t *ht_create(int max_size) +{ + ht_t *h = malloc(sizeof(ht_t)); + h->max_size = max_size; + h->size = 0; + h->vals = malloc(sizeof(struct node) * max_size); + h->curr_index = 0; + h->curr_node = NULL; + h->iterating = false; + + return h; +} + +void ht_destroy(ht_t *h) +{ + // only destroy if h isn't NULL + if (h) { + // only destroy vals if it isn't NULL + if (h->vals) { + // iterate through the hash values + for (int i = 0; i < h->max_size; ++i) { + // iterate through the linked list and remove the value + struct node *curr = h->vals[i]; + struct node *prev; + while (curr) { + prev = curr; + curr = curr->next; + free(prev->key); + free(prev); + } + } + free(h->vals); + } + free(h); + } +} + +void *ht_find(ht_t *h, char *key) +{ + unsigned int hash = djb2_hash(key) % h->max_size; + + // exit early if hash isn't in table + if (!h->vals[hash]) + return NULL; + + // iterate through values stored at `hash`; exit early if found + struct node *curr = h->vals[hash]; + while (curr) { + if (strcmp(key, curr->key) == 0) + return curr->val; + curr = curr->next; + } + + return NULL; +} + +void ht_insert(ht_t *h, char *key, void *val) +{ + if (h->size > h->max_size * 0.75) + rehash(h, h->max_size * 2); + + unsigned int hash = djb2_hash(key) % h->max_size; + + // create a node + int len = strlen(key); + struct node *new = malloc(sizeof(struct node)); + new->key = malloc(sizeof(char) * (len + 1)); + for (int i = 0; i < len; ++i) { + new->key[i] = key[i]; + } + new->key[len] = 0; + new->val = val; + + // insert node at beginning of list + if (h->vals[hash]) { + new->next = h->vals[hash]; + } + h->vals[hash] = new; + + ++(h->size); +} + +void ht_delete(ht_t *h, char *key) +{ + if (h->size < h->max_size * 0.25) + rehash(h, h->max_size * 0.5); + + unsigned int hash = djb2_hash(key) % h->max_size; + + if (!h->vals[hash]) + return; // exit if the hash is not found + + // remove the key from the front of the list if it's there + if (strcmp(key, h->vals[hash]->key) == 0) { + struct node *temp = h->vals[hash]; + h->vals[hash] = h->vals[hash]->next; + free(temp->key); + free(temp); + } else { + // iterate through list and remove once a match is found + struct node *prev = h->vals[hash]; + struct node *curr = h->vals[hash]->next; + while (curr) { + if (strcmp(key, curr->key) == 0) { + prev->next = curr->next; + free(curr->key); + free(curr); + return; + } + + prev = curr; + curr = curr->next; + } + } +} + +int ht_size(ht_t *h) +{ + return h->size; +} + +void ht_iter_init(ht_t *h) +{ + h->curr_index = 0; + h->curr_node = h->vals[0]; +} + +struct kvp ht_iter_next(ht_t *h) +{ + struct kvp out = { + .key = NULL, + .val = NULL, + }; + + // return NULL if we've reached the end + if (!h->curr_node && h->curr_index >= h->max_size) + return out; + + // look for next index with node if current node is NULL + if (!h->curr_node) { + while (!h->curr_node) { + h->curr_node = h->vals[h->curr_index++]; + if (h->curr_index >= h->max_size) + return out; + } + } + + // get the value and move to the next node + out.key = h->curr_node->key; + out.val = h->curr_node->val; + h->curr_node = h->curr_node->next; + return out; +} -- cgit v1.2.3