чување 4e7cb54194b91d1e601d6de115d855f47567d785
родитељ 363cc05dc5403a9c2252edc1ae2ebb428be3ae3f
Аутор: Страхиња Радић <sr@strahinja.org>
Датум: Mon, 17 Aug 2026 19:10:50 +0000
Add -s (find shortest path); build files update; update copyright year
Diffstat:
измењених датотека: 12, додавања: 1546(+), брисања: 635(-)
diff --git a/Makefile b/Makefile
@@ -2,6 +2,7 @@
include config.mk
+.SUFFIXES:
.SUFFIXES: .c .o .h.in .h
.h.in.h:
diff --git a/README b/README
@@ -13,7 +13,13 @@ See the file INSTALL in this repository.
Usage
-----
-$ lav-sdl labyrinth_file.dat
+Brute force mode (the default):
+
+ lav-sdl labyrinth_file.dat
+
+Shortest path mode:
+
+ lav-sdl -s labyrinth_file.dat
Format of data file
@@ -33,7 +39,7 @@ License
-------
lav-sdl - SDL maze solver
-Copyright (C) 2023-2025 Strahinya Radich
+Copyright (C) 2023-2026 Strahinya Radich
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/config.Haiku b/config.Haiku
@@ -0,0 +1,22 @@
+CC = clang
+CFLAGS = -I. -Os -Wextra -Wall -pedantic -std=c99
+CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \
+ -D_XOPEN_SOURCE=700 -DNDEBUG #-I/usr/local/include
+DESTDIR = /boot/home/config/non-packaged
+INSTALL = install
+LIBS = -lSDL3_image -lSDL3 -lm
+SRC = lav-sdl.c
+HEADERS = local.h
+OBJS = lav-sdl.o
+PREFIX =
+PROG = lav-sdl
+
+# Derived macros
+BINDIR = $(DESTDIR)$(PREFIX)/bin
+# TODO: Test
+DATADIR = $(DESTDIR)$(PREFIX)/share/$(PROG)
+DOCDIR = $(DESTDIR)$(PREFIX)/documentation/$(PROG)
+MANPREFIX = $(DESTDIR)$(PREFIX)/documentation/man
+#MANPAGE = $(PROG).1
+# TODO: Test
+#USERDIR = $(DATADIR)
diff --git a/config.Linux b/config.Linux
@@ -0,0 +1,16 @@
+CFLAGS = -Os -Wextra -Wall -pedantic -std=c99
+CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \
+ -D_XOPEN_SOURCE=700 -DNDEBUG -I/usr/local/include
+INSTALL = install
+LIBS = -lSDL3_image -lSDL3 -lm
+SRC = lav-sdl.c
+HEADERS = local.h
+OBJS = lav-sdl.o
+PREFIX = /usr/local
+PROG = lav-sdl
+
+# Derived macros
+BINDIR = $(DESTDIR)$(PREFIX)/bin
+DATADIR = $(DESTDIR)$(PREFIX)/share/$(PROG)
+DOCDIR = $(DESTDIR)$(PREFIX)/share/doc/$(PROG)
+MANPREFIX = $(DESTDIR)$(PREFIX)/share/man
diff --git a/config.OpenBSD b/config.OpenBSD
@@ -0,0 +1,17 @@
+CFLAGS = -Os -Wextra -Wall -pedantic -std=c99
+CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \
+ -D_XOPEN_SOURCE=700 -D_BSD_SOURCE -DNDEBUG \
+ -I/usr/local/include
+INSTALL = install
+LIBS = -lSDL3_image -lSDL3 -lm
+SRC = lav-sdl.c
+HEADERS = local.h
+OBJS = lav-sdl.o
+PREFIX = /usr/local
+PROG = lav-sdl
+
+# Derived macros
+BINDIR = $(DESTDIR)$(PREFIX)/bin
+DATADIR = $(DESTDIR)$(PREFIX)/share/$(PROG)
+DOCDIR = $(DESTDIR)$(PREFIX)/share/doc/$(PROG)
+MANPREFIX = $(DESTDIR)$(PREFIX)/man
diff --git a/config.mk b/config.mk
@@ -1,26 +0,0 @@
-#CC = cc
-CFLAGS = -Os -Wall -pedantic -std=c99
-_CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \
- -D_XOPEN_SOURCE=700
-INCLUDE = -I/usr/include
-# OpenBSD
-#_CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \
-# -D_XOPEN_SOURCE=700 -D_BSD_SOURCE
-#INCLUDE = -I/usr/local/include
-INSTALL = install
-LIBS = -lSDL3_image -lSDL3 -lm
-SRC = lav-sdl.c
-HEADERS = local.h
-OBJS = lav-sdl.o
-PREFIX = /usr/local
-PROG = lav-sdl
-
-# Derived macros
-BINDIR = $(PREFIX)/bin
-CPPFLAGS = $(_CPPFLAGS) $(INCLUDE)
-DATADIR = $(PREFIX)/share/$(PROG)
-DOCDIR = $(PREFIX)/share/doc/$(PROG)
-MANPREFIX = $(PREFIX)/share/man
-# OpenBSD
-#MANPREFIX = $(PREFIX)/man
-#MANPAGE = $(PROG).1
diff --git a/lav-sdl.c b/lav-sdl.c
@@ -1,10 +1,12 @@
/* This program is licensed under the terms of GNU GPL v3 or (at your option)
- * any later version. Copyright (C) 2023-2025 Strahinya Radich.
+ * any later version. Copyright (C) 2023-2026 Strahinya Radich.
* See the file LICENSE for exact copyright and license details. */
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
+#include <assert.h>
#include <errno.h>
+#include <limits.h>
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
@@ -18,6 +20,12 @@
#define FPS 60
#define ANIM_STEPS (ANIM_SPEED * FPS / 1000)
+#define BOLD_START '\1'
+#define BOLD_END '\2'
+#define CENTER_LINE '\3'
+#define DIM_START '\4'
+#define DIM_END '\2'
+#define CELL_SCALE_FACTOR 1.0f
#define BUFSIZE 4096
#define CELL_SIZE 32
#define DEFAULT_WIDTH 1280
@@ -31,10 +39,16 @@
#define PROGNAME "lav-sdl"
#define TER12_WIDTH 6
#define TER12_HEIGHT 12
+#define TEXT_OPACITY_DIM 0.3f
+#define TEXT_OPACITY_NORMAL 0.6f
+#define TEXT_OPACITY_BOLD 1.0f
+#define TEXT_SCALE_FACTOR 2.0f
#define WIN_TITLE "Lavirint (SDL version)"
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
-#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define INDEX(x, y) ((y) * state->grid_width + (x))
+#define INDEXFI(x, y) (((int)floor(y)) * state->grid_width + ((int)floor(x)))
/* clang-format off */
enum {
@@ -43,9 +57,12 @@ enum {
CELL_FAIL,
CELL_OK,
CELL_START,
- CELL_END
+ CELL_END,
+ CELL_PATH
};
+static char* cell_image[] = {"[ ]", "[H]", "[/]", "[o]", "[>]", "[v]", "[.]"};
+
enum {
STATE_SEARCHING,
STATE_SEARCHING_PAUSED,
@@ -61,92 +78,170 @@ const char* state_descriptions[] = {
};
const char* help_text[] = {
- "lav-sdl - SDL maze solver",
- "=========================",
+ "\1\3lav-sdl - SDL maze solver\2",
+ "\1\3=========================\2",
"",
- "F1 - Show this help screen",
- "Esc - Hide this help screen",
- "Space - Pause/resume simulation",
- "C-Q - Exit",
+ "\1F1\2 - Show this help screen",
+ "\1Esc\2 - Hide this help screen",
+ "\1Space\2 - Pause/resume simulation",
+ "\1C-Q\2 - Exit",
"",
- "---",
+ "\4---\2",
"",
- "This program is licensed under the terms of GNU GPL v3 or (at your",
- "option) any later version. Copyright (C) 2023-2025 Strahinya Radich.",
- "See the file LICENSE for exact copyright and license details.",
+ "\4This program is licensed under the terms of GNU GPL v3 or (at your\2",
+ "\4option) any later version. Copyright (C) 2023-2026 Strahinya Radich.\2",
+ "\4See the file LICENSE for exact copyright and license details.\2",
NULL
};
/* clang-format on */
struct AnimationState {
+ SDL_FPoint start;
SDL_FPoint position;
- SDL_FPoint destination;
+ SDL_FPoint end;
SDL_FPoint delta;
int active;
int step;
int final_step;
};
-struct Labyrinth {
- char** cells;
- int width;
- int height;
+struct Cell {
+ int empty; /* logical variables */
+ int popping;
+ int path;
+ int starting;
+ int ending; /* logical variables end */
+ SDL_FPoint coords;
+ int contents;
+ struct Cell* down;
+ struct Cell* left;
+ struct Cell* right;
+ struct Cell* up;
+ struct Cell* path_next; /* next cell in shortest path */
+ struct Cell* path_prev; /* previous cell in shortest path */
+ int path_dist; /* length of shortest path so far */
+};
+
+/* clang-format off */
+struct State {
+ struct AnimationState animation;
+ SDL_Texture* background;
+ float cell_scale_factor;
+ int check_score;
+ SDL_Point dialog_dims;
+ SDL_Point dialog_text_origin;
+ int dialog_shown;
+ const char** dialog_text;
+ int display_width; /* Physical, detected */
+ int display_height;
+ int full_screen;
+ struct Cell* grid;
+ int grid_height;
+ int grid_width;
+ struct HiScoreEntry* hall_of_fame;
+ SDL_Point hero, start, end;
+ void (*input_callback)(struct State*);
+ int input_col;
+ int input_instant_reset;
+ int input_shown;
+ int location_pointer;
+ SDL_FPoint* location_stack;
+ int mode;
+ SDL_FPoint mouse;
+ int moveno;
+ struct Cell* path_start;
+ struct Cell* path_end;
+ SDL_FPoint pointer;
+ int query_shown;
+ void (*query_callback)(struct State*);
+ int redraw;
+ SDL_Renderer* renderer;
+ int running;
+ int screen_width; /* Logical */
+ int screen_height;
+ unsigned long int score;
+ int search_shortest;
+ SDL_FPoint selection_start, selection_end;
+ SDL_Texture* sprites;
+ int state;
+ SDL_Texture* ter12_texture;
+ float text_scale_factor;
+ SDL_Window* window;
};
-static struct AnimationState animation;
-static SDL_Point hero, start, end;
-static struct Labyrinth labyrinth;
-static int location_pointer = 0;
-static SDL_Point* location_stack = NULL;
-static SDL_FPoint pointer;
-static int display_width = DEFAULT_WIDTH; /* Physical, detected */
-static int display_height = DEFAULT_HEIGHT;
-static int help_shown = 0;
-static int redraw = 1;
-static SDL_Renderer* renderer = NULL;
-static int running = 1;
-static int screen_width = DEFAULT_WIDTH; /* Logical */
-static int screen_height = DEFAULT_HEIGHT;
-static SDL_Texture* sprites = NULL;
-static int state = STATE_SEARCHING_PAUSED;
-static SDL_Texture* ter12_texture = NULL;
-static SDL_Texture* background = NULL;
-static SDL_Window* window = NULL;
-static float text_scale_factor = 2.0f;
+/* clang-format on */
+void assign_pointFF(const SDL_FPoint* from, SDL_FPoint* to);
void assign_pointIF(const SDL_Point* from, SDL_FPoint* to);
void assign_point_xyIF(const int x, const int y, SDL_FPoint* to);
-void calculate_delta(void);
-void calculate_screen_size(void);
-void cleanup(void);
-void draw(void);
-void draw_background(SDL_FRect* bg_rect, SDL_FRect* cell_rect,
- SDL_FRect* dest_rect);
-void draw_help_dialog(void);
-void draw_text(const int x, const int y, const int font_width,
- const int font_height, SDL_Texture* font_tex, const char* text, ...);
-void get_hero_sprite_xy(float* x, float* y);
-void get_sprite_xy(const char cell, float* x, float* y);
-void handle_event(SDL_Event* event);
-void labyrinth_coord_to_screen_coord(const int x, const int y,
- const float startx, const float starty, float* to_x, float* to_y);
+void assign_point_xyII(const int x, const int y, SDL_Point* to);
+void calculate_delta(struct State* state);
+void calculate_display_size(struct State* state);
+void calculate_grid_origin(const struct State* state, float* sx, float* sy);
+void cleanup(struct State* state);
+void do_exit(const int code, struct State* state);
+void draw(struct State* state);
+int find_shortest_path(struct Cell* c, struct Cell* source, const int dist);
+void get_hero_sprite_xy(struct State* state, float* x, float* y);
+void get_sprite_xy(const int cell, float* x, float* y);
+void grid_coord_to_screen_coordF(const struct State* state, const float x,
+ const float y, const float startx, const float starty, float* to_x,
+ float* to_y);
+void grid_coord_to_screen_coordI(const struct State* state, const int x,
+ const int y, const float startx, const float starty, float* to_x,
+ float* to_y);
+void handle_event(struct State* state, SDL_Event* event);
+void handle_key_down(struct State* state, SDL_Event* event);
+void init_animation(struct AnimationState* animation);
+void init_cell(struct Cell* c, const int full, const SDL_FPoint* coords);
+void init_state(struct State* state);
void labyrinth_coord_to_screen_coordF(const float x, const float y,
const float startx, const float starty, float* to_x, float* to_y);
-int load_textures(void);
-int load_labyrinth(const char* pathname);
-int location_pop(SDL_Point* location);
-void location_push(const SDL_Point* location);
-void next_step(void);
-int passable(const int x, const int y);
+void labyrinth_coord_to_screen_coordI(const int x, const int y,
+ const float startx, const float starty, float* to_x, float* to_y);
+int load_labyrinth(struct State* state, const char* pathname);
+int load_textures(struct State* state);
+void location_delete(struct State* state, const int index);
+int location_index(const struct State* state, const SDL_FPoint* location);
+int location_pop(struct State* state, SDL_Point* location);
+void location_pushF(struct State* state, const SDL_FPoint* location);
+void location_pushI(struct State* state, const SDL_Point* location);
+void mark_path(struct Cell* c);
+void next_step(struct State* state);
+int passable(struct State* state, const int x, const int y);
+int point_equalI(struct SDL_Point* p1, struct SDL_Point* p2);
+void prepare_dialog(struct State* state, const int w, const int h,
+ const char** text, const int dx, const int show);
void print_debug(const char* fmt, ...);
void print_error(const char* fmt, ...);
-void reset(void);
-void step_animation(void);
-SDL_Surface* try_load(const char* filename);
+void print_matrix(const struct State* state);
+void print_path(struct Cell* c);
+void render_background(struct State* state, SDL_FRect* bg_rect);
+void render_dialog(struct State* state);
+void render_hero(struct State* state, const float startx, const float starty);
+void render_status(struct State* state);
+void render_text(struct State* state, const int x, const int y,
+ const int font_width, const int font_height, SDL_Texture* font_tex,
+ const char* text, ...);
+void render_things(struct State* state, const float startx, const float starty);
+void reset(struct State* state);
+void reset_path(struct State* state);
+void step_animation(struct State* state);
+int strlen_visible(const char* s);
+SDL_Texture* try_load(SDL_Renderer* renderer, const char* filename);
+
+void
+assign_pointFF(const SDL_FPoint* from, SDL_FPoint* to)
+{
+ assert((from != NULL) && (to != NULL));
+ to->x = (float)from->x;
+ to->y = (float)from->y;
+}
void
assign_pointIF(const SDL_Point* from, SDL_FPoint* to)
{
+ assert((from != NULL) && (to != NULL));
to->x = (float)from->x;
to->y = (float)from->y;
}
@@ -154,329 +249,190 @@ assign_pointIF(const SDL_Point* from, SDL_FPoint* to)
void
assign_point_xyIF(const int x, const int y, SDL_FPoint* to)
{
+ assert(to != NULL);
+ to->x = (float)x;
+ to->y = (float)y;
+}
+
+void
+assign_point_xyII(const int x, const int y, SDL_Point* to)
+{
+ assert(to != NULL);
to->x = (float)x;
to->y = (float)y;
}
void
-calculate_delta(void)
+calculate_delta(struct State* state)
{
- animation.delta.x
- = fabsf(animation.destination.x - animation.position.x)
- / ANIM_STEPS;
- if (animation.destination.x < animation.position.x)
- animation.delta.x *= -1;
- animation.delta.y
- = fabsf(animation.destination.y - animation.position.y)
- / ANIM_STEPS;
- if (animation.destination.y < animation.position.y)
- animation.delta.y *= -1;
+ SDL_FPoint* start = NULL;
+ SDL_FPoint* end = NULL;
+ SDL_FPoint* delta = NULL;
+
+ assert(state != NULL);
+
+ start = &state->animation.start;
+ end = &state->animation.end;
+ delta = &state->animation.delta;
+
+ print_debug(
+ "calculate_delta: start: (%0.2f, %0.2f), end: (%0.2f, %0.2f)",
+ start->x, start->y, end->x, end->y);
+
+ delta->x = fabsf(end->x - start->x) / ANIM_STEPS;
+ if (end->x < start->x)
+ delta->x *= -1;
+ delta->y = fabsf(end->y - start->y) / ANIM_STEPS;
+ if (end->y < start->y)
+ delta->y *= -1;
+ print_debug("calculate_delta: (%0.2f, %0.2f)", delta->x, delta->y);
}
void
-calculate_screen_size(void)
+calculate_display_size(struct State* state)
{
- if (!SDL_GetRenderOutputSize(renderer, &display_width, &display_height))
+ assert(state != NULL);
+
+ if (!SDL_GetRenderOutputSize(state->renderer, &state->display_width,
+ &state->display_height))
{
- print_error("SDL_GetRenderOutputSize failed");
- exit(1);
+ SDL_LogError(SDL_LOG_CATEGORY_ERROR,
+ "SDL_GetRenderOutputSize failed");
+ do_exit(1, state);
}
- print_debug("output size: (%d, %d)", screen_width, screen_height);
+ print_debug("output display size: (%d, %d)", state->display_width,
+ state->display_height);
+}
+
+void
+calculate_grid_origin(const struct State* state, float* sx, float* sy)
+{
+ int cx, cy;
+ float csf;
+
+ assert((state != NULL) && (sx != NULL) && (sy != NULL));
+
+ csf = state->cell_scale_factor;
+ cx = state->screen_width / 2;
+ cy = state->screen_height / 2;
+
+ *sx = cx - (csf * state->grid_width * CELL_SIZE) / 2;
+ *sy = cy - (csf * state->grid_height * CELL_SIZE) / 2;
}
void
-cleanup(void)
+cleanup(struct State* state)
{
- free(location_stack);
-
- if (labyrinth.cells)
- for (int y = 0; y < LABYRINTH_MAXHEIGHT; y++)
- free(labyrinth.cells[y]);
- free(labyrinth.cells);
-
- if (background)
- SDL_DestroyTexture(background);
- if (ter12_texture)
- SDL_DestroyTexture(ter12_texture);
- if (sprites)
- SDL_DestroyTexture(sprites);
- if (renderer)
- SDL_DestroyRenderer(renderer);
- if (window)
- SDL_DestroyWindow(window);
-
- // IMG_Quit();
+ assert(state != NULL);
+
+ free(state->location_stack);
+
+ if (state->background)
+ SDL_DestroyTexture(state->background);
+ if (state->ter12_texture)
+ SDL_DestroyTexture(state->ter12_texture);
+ if (state->sprites)
+ SDL_DestroyTexture(state->sprites);
+ if (state->renderer)
+ SDL_DestroyRenderer(state->renderer);
+ if (state->window)
+ SDL_DestroyWindow(state->window);
+
SDL_Quit();
}
void
-draw(void)
+do_exit(const int code, struct State* state)
{
- SDL_FRect bg_rect, cell_rect;
+ cleanup(state);
+ exit(code);
+}
+void
+draw(struct State* state)
+{
+ SDL_FRect bg_rect;
SDL_FRect dest_rect;
- int x, y;
- int cx, cy;
- float crx, cry;
- float drx, dry;
float startx, starty;
+ float csf;
- cx = screen_width / 2;
- cy = screen_height / 2;
+ assert(state != NULL);
+ assert(state->cell_scale_factor != 0.0f);
- startx = cx - labyrinth.width / 2 * CELL_SIZE;
- starty = cy - labyrinth.height / 2 * CELL_SIZE;
+ csf = state->cell_scale_factor;
+ calculate_grid_origin(state, &startx, &starty);
bg_rect.x = 0;
bg_rect.y = 0;
- bg_rect.w = CELL_SIZE * labyrinth.width;
- bg_rect.h = CELL_SIZE * labyrinth.height;
-
- SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
- SDL_RenderClear(renderer);
+ bg_rect.w = csf * (CELL_SIZE * state->grid_width + 1);
+ bg_rect.h = csf * (CELL_SIZE * state->grid_height + 1);
- cell_rect.w = CELL_SIZE;
- cell_rect.h = CELL_SIZE;
+ SDL_SetRenderDrawColor(state->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
+ SDL_RenderClear(state->renderer);
/* Render background */
- if (!background)
- draw_background(&bg_rect, &cell_rect, &dest_rect);
+ if (!state->background)
+ render_background(state, &bg_rect);
/* Draw buffered copy of background */
dest_rect.x = startx;
dest_rect.y = starty;
dest_rect.w = bg_rect.w;
dest_rect.h = bg_rect.h;
- SDL_RenderTexture(renderer, background, &bg_rect, &dest_rect);
-
- /* Draw nuggets/cobwebs */
- dest_rect.w = CELL_SIZE;
- dest_rect.h = CELL_SIZE;
- for (y = 0; y < labyrinth.height; y++)
- for (x = 0; x < labyrinth.width; x++)
- {
- if ((labyrinth.cells[y][x] != CELL_OK
- && labyrinth.cells[y][x] != CELL_FAIL)
- || (x == start.x && y == start.y)
- || (x == end.x && y == end.y))
- continue;
+ SDL_RenderTexture(state->renderer, state->background, &bg_rect,
+ &dest_rect);
- get_sprite_xy(labyrinth.cells[y][x], &crx, &cry);
- cell_rect.x = crx;
- cell_rect.y = cry;
- labyrinth_coord_to_screen_coord(x, y, startx, starty,
- &drx, &dry);
- dest_rect.x = drx;
- dest_rect.y = dry;
- SDL_RenderTexture(renderer, sprites, &cell_rect,
- &dest_rect);
- }
+ /* Render things */
+ render_things(state, startx, starty);
- /* Draw hero */
- dest_rect.w = CELL_SIZE;
- dest_rect.h = CELL_SIZE;
- get_hero_sprite_xy(&crx, &cry);
- cell_rect.x = crx;
- cell_rect.y = cry;
- if (animation.active)
- labyrinth_coord_to_screen_coordF(animation.position.x,
- animation.position.y, startx, starty, &drx, &dry);
- else
- labyrinth_coord_to_screen_coord(hero.x, hero.y, startx, starty,
- &drx, &dry);
- dest_rect.x = drx;
- dest_rect.y = dry;
- SDL_RenderTexture(renderer, sprites, &cell_rect, &dest_rect);
+ /* Render hero */
+ render_hero(state, startx, starty);
/* Render status */
- // draw_text(startx, starty + bg_rect.h, TER12_WIDTH, TER12_HEIGHT,
- draw_text(0, screen_height - text_scale_factor * TER12_HEIGHT,
- TER12_WIDTH, TER12_HEIGHT, ter12_texture,
- "Column: %2d, Row: %2d F1 = Help Status: %s", hero.x + 1,
- hero.y + 1, state_descriptions[state]);
-
- if (help_shown)
- draw_help_dialog();
-
- SDL_RenderPresent(renderer);
-}
-
-void
-draw_background(SDL_FRect* bg_rect, SDL_FRect* cell_rect, SDL_FRect* dest_rect)
-{
- const SDL_DisplayMode* mode;
- SDL_DisplayID display;
- float crx, cry;
- float drx, dry;
-
- if ((display = SDL_GetDisplayForWindow(window)) == 0)
- {
- print_error("SDL_GetDisplayForWindow failed: %s",
- SDL_GetError());
- exit(1);
- }
-
- if ((mode = SDL_GetCurrentDisplayMode(display)) == NULL)
- {
- print_error("SDL_GetCurrentDisplayMode failed: %s",
- SDL_GetError());
- exit(1);
- }
-
- background = SDL_CreateTexture(renderer, mode->format,
- SDL_TEXTUREACCESS_TARGET, bg_rect->w, bg_rect->h);
- if (!background)
- {
- print_error("SDL_CreateTexture failed: %s",
- SDL_GetError());
- exit(1);
- }
- if (!SDL_SetRenderTarget(renderer, background))
- {
- print_error("SDL_SetRenderTarget failed: %s", SDL_GetError());
- exit(1);
- }
-
- SDL_SetRenderDrawColor(renderer, 0x22, 0x22, 0x22, SDL_ALPHA_OPAQUE);
- SDL_RenderFillRect(renderer, bg_rect);
-
- dest_rect->w = CELL_SIZE;
- dest_rect->h = CELL_SIZE;
- for (int y = 0; y < labyrinth.height; y++)
- for (int x = 0; x < labyrinth.width; x++)
- {
- if (!((labyrinth.cells[y][x] != CELL_OK
- && labyrinth.cells[y][x] != CELL_FAIL)
- || (x == start.x && y == start.y)
- || (x == end.x && y == end.y)))
- continue;
-
- get_sprite_xy(labyrinth.cells[y][x], &crx, &cry);
- cell_rect->x = crx;
- cell_rect->y = cry;
- labyrinth_coord_to_screen_coord(x, y, 0.0f, 0.0f, &drx,
- &dry);
- dest_rect->x = drx;
- dest_rect->y = dry;
- SDL_RenderTexture(renderer, sprites, cell_rect,
- dest_rect);
- }
-
- SDL_SetRenderTarget(renderer, NULL);
-}
-
-void
-draw_help_dialog(void)
-{
- const char** phelp_text = help_text;
- int sx = screen_width / 2 - HELP_DIALOG_WIDTH / 2;
- int sy = screen_height / 2 - HELP_DIALOG_HEIGHT / 2;
- SDL_FRect dialog_rect;
-
- dialog_rect.x = sx;
- dialog_rect.y = sy;
- dialog_rect.w = HELP_DIALOG_WIDTH;
- dialog_rect.h = HELP_DIALOG_HEIGHT;
-
- SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
- SDL_SetRenderDrawColor(renderer, 0, 0, 0, .75 * 255);
- SDL_RenderFillRect(renderer, &dialog_rect);
+ render_status(state);
- SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
- SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
- SDL_RenderRect(renderer, &dialog_rect);
- SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
- dialog_rect.x--;
- dialog_rect.y--;
- dialog_rect.w += 2;
- dialog_rect.h += 2;
- SDL_RenderRect(renderer, &dialog_rect);
- dialog_rect.x--;
- dialog_rect.y--;
- dialog_rect.w += 2;
- dialog_rect.h += 2;
- SDL_RenderRect(renderer, &dialog_rect);
+ if (state->dialog_shown)
+ render_dialog(state);
- while (*phelp_text)
- {
- draw_text(sx + text_scale_factor * (2 * TER12_WIDTH),
- sy
- + (phelp_text - help_text + 1)
- * text_scale_factor * (TER12_HEIGHT + 2),
- TER12_WIDTH, TER12_HEIGHT, ter12_texture, *phelp_text);
- phelp_text++;
- }
+ SDL_RenderPresent(state->renderer);
}
-void
-draw_text(const int x, const int y, const int font_width, const int font_height,
- SDL_Texture* font_tex, const char* text, ...)
+int
+find_shortest_path(struct Cell* c, struct Cell* source, const int dist)
{
- char buf[PRINT_BUFSIZE];
- va_list args;
- const char* pbuf = NULL;
- int xstart = 0;
- int ystart = 0;
- SDL_FRect source_rect;
- SDL_FRect dest_rect;
- int index = 0;
- int chars_per_row = 0;
- char first_char = ' ';
- char last_char = '~';
- float tex_w, tex_h;
-
- if (!text || !font_tex)
- return;
+ int result = 0;
- va_start(args, text);
- vsnprintf(buf, sizeof(buf), text, args);
- va_end(args);
+ if (!c || c->path_dist <= dist)
+ return 0;
- source_rect.w = font_width;
- source_rect.h = font_height;
+ if (!c->empty && !c->starting && !c->ending)
+ return 0;
- dest_rect.x = x;
- dest_rect.y = y;
- dest_rect.w = text_scale_factor * font_width;
- dest_rect.h = text_scale_factor * font_height;
+ if (c->starting)
+ c->path_dist = dist;
- if (!SDL_GetTextureSize(font_tex, &tex_w, &tex_h))
+ if (c->path_dist > dist)
{
- print_error("SDL_GetTextureSize failed: %s", SDL_GetError());
- exit(1);
+ c->path_dist = dist;
+ c->path_prev = source;
}
- chars_per_row = tex_w / (font_width + 1);
-
- pbuf = buf;
- // SDL_SetRenderScale(renderer, scale_factor, scale_factor);
- while (*pbuf)
- {
- if (*pbuf >= first_char && *pbuf <= last_char)
- {
- index = (int)(*pbuf - first_char);
-
- xstart = 1 + index % chars_per_row * (font_width + 1);
- ystart = 1 + index / chars_per_row * (font_height + 1);
- }
- else
- break;
-
- source_rect.x = xstart;
- source_rect.y = ystart;
-
- SDL_RenderTexture(renderer, font_tex, &source_rect, &dest_rect);
+ if (c->ending)
+ return 1;
- dest_rect.x += text_scale_factor * font_width;
+ result |= find_shortest_path(c->left, c, dist + 1);
+ result |= find_shortest_path(c->up, c, dist + 1);
+ result |= find_shortest_path(c->right, c, dist + 1);
+ result |= find_shortest_path(c->down, c, dist + 1);
- pbuf++;
- }
- // SDL_SetRenderScale(renderer, 1.0f, 1.0f);
+ return result;
}
void
-get_hero_sprite_xy(float* x, float* y)
+get_hero_sprite_xy(struct State* state, float* x, float* y)
{
- switch (state)
+ assert((state != NULL) && (x != NULL) && (y != NULL));
+
+ switch (state->state)
{
case STATE_SUCCESS:
*x = 1 + 4 * (CELL_SIZE + 1);
@@ -494,7 +450,7 @@ get_hero_sprite_xy(float* x, float* y)
}
void
-get_sprite_xy(const char cell, float* x, float* y)
+get_sprite_xy(const int cell, float* x, float* y)
{
switch (cell)
{
@@ -527,64 +483,49 @@ get_sprite_xy(const char cell, float* x, float* y)
}
void
-handle_event(SDL_Event* event)
+grid_coord_to_screen_coordF(const struct State* state, const float x,
+ const float y, const float startx, const float starty, float* to_x,
+ float* to_y)
+{
+ assert((state != NULL) && (to_x != NULL) && (to_y != NULL));
+ *to_x = startx + x * state->cell_scale_factor * CELL_SIZE;
+ *to_y = starty + y * state->cell_scale_factor * CELL_SIZE;
+}
+
+void
+grid_coord_to_screen_coordI(const struct State* state, const int x, const int y,
+ const float startx, const float starty, float* to_x, float* to_y)
+{
+ assert((state != NULL) && (to_x != NULL) && (to_y != NULL));
+ *to_x = startx + x * state->cell_scale_factor * CELL_SIZE;
+ *to_y = starty + y * state->cell_scale_factor * CELL_SIZE;
+}
+
+void
+handle_event(struct State* state, SDL_Event* event)
{
if (!event)
return;
switch (event->type)
{
case SDL_EVENT_QUIT:
- running = 0;
+ state->running = 0;
break;
case SDL_EVENT_WINDOW_RESIZED:
- redraw = 1;
- calculate_screen_size();
+ state->redraw = 1;
+ calculate_display_size(state);
break;
case SDL_EVENT_KEY_DOWN:
print_debug("Got Keydown, key = %X, mod = %X", event->key.key,
event->key.mod);
-
- switch (event->key.key)
- {
- case SDLK_F1:
- help_shown = 1;
- redraw = 1;
- break;
- case SDLK_ESCAPE:
- help_shown = 0;
- redraw = 1;
- break;
- case SDLK_SPACE:
- if (state == STATE_SEARCHING)
- {
- print_debug("State: paused");
- state = STATE_SEARCHING_PAUSED;
- }
- else if (state == STATE_SEARCHING_PAUSED)
- {
- print_debug("State: searching");
- state = STATE_SEARCHING;
- }
- else
- {
- print_debug("Resetting");
- reset();
- }
- break;
- case SDLK_Q:
- if (event->key.mod & SDL_KMOD_CTRL)
- running = 0;
- break;
- default:
- print_debug("switch fallthrough");
- }
+ handle_key_down(state, event);
break;
case SDL_EVENT_MOUSE_MOTION:
- pointer.x = event->motion.x;
- pointer.y = event->motion.y;
- if (pointer.x <= 0 || pointer.y <= 0
- || pointer.x + 1 >= display_width
- || pointer.y + 1 >= display_height)
+ state->pointer.x = event->motion.x;
+ state->pointer.y = event->motion.y;
+ if (state->pointer.x <= 0 || state->pointer.y <= 0
+ || state->pointer.x + 1 >= state->display_width
+ || state->pointer.y + 1 >= state->display_height)
SDL_ShowCursor();
else
SDL_HideCursor();
@@ -593,94 +534,191 @@ handle_event(SDL_Event* event)
}
void
-labyrinth_coord_to_screen_coord(const int x, const int y, const float startx,
- const float starty, float* to_x, float* to_y)
+handle_key_down(struct State* state, SDL_Event* event)
{
- *to_x = startx + x * CELL_SIZE;
- *to_y = starty + y * CELL_SIZE;
+ switch (event->key.key)
+ {
+ case SDLK_F1:
+ if (state->dialog_shown)
+ break;
+ prepare_dialog(state, HELP_DIALOG_WIDTH, HELP_DIALOG_HEIGHT,
+ help_text, 0, 1);
+ break;
+ case SDLK_ESCAPE:
+ state->dialog_shown = 0;
+ state->dialog_text = NULL;
+ state->redraw = 1;
+ break;
+ case SDLK_SPACE:
+ if (state->state == STATE_SEARCHING)
+ {
+ print_debug("State: paused");
+ state->state = STATE_SEARCHING_PAUSED;
+ }
+ else if (state->state == STATE_SEARCHING_PAUSED)
+ {
+ print_debug("State: searching");
+ state->state = STATE_SEARCHING;
+ }
+ else
+ {
+ print_debug("Resetting");
+ reset(state);
+ }
+ break;
+ case SDLK_Q:
+ if (event->key.mod & SDL_KMOD_CTRL)
+ state->running = 0;
+ break;
+ default:
+ print_debug("switch fallthrough");
+ }
}
void
-labyrinth_coord_to_screen_coordF(const float x, const float y,
- const float startx, const float starty, float* to_x, float* to_y)
+init_animation(struct AnimationState* animation)
{
- *to_x = startx + x * CELL_SIZE;
- *to_y = starty + y * CELL_SIZE;
+ assert(animation != NULL);
+ animation->start.x = -1;
+ animation->start.y = -1;
+ animation->position.x = -1;
+ animation->position.y = -1;
+ animation->end.x = -1;
+ animation->end.y = -1;
+ animation->delta.x = 0;
+ animation->delta.y = 0;
+ animation->active = 0;
+ animation->step = 0;
+ animation->final_step = 0;
}
-int
-load_textures(void)
+void
+init_cell(struct Cell* c, const int full, const SDL_FPoint* coords)
{
- SDL_Surface* surface = NULL;
- SDL_ScaleMode scale_mode = SDL_SCALEMODE_PIXELART;
-
- surface = try_load(SPRITES_PNG);
- if (!surface)
- return -1;
- sprites = SDL_CreateTextureFromSurface(renderer, surface);
- if (!sprites)
+ assert(c != NULL);
+ c->empty = 1;
+ c->starting = 0;
+ c->ending = 0;
+ c->popping = 0;
+ c->path = 0;
+ if (full)
{
- print_error("SDL_CreateTextureFromSurface failed: %s",
- SDL_GetError());
- exit(1);
+ assert(coords != NULL);
+ c->coords.x = coords->x;
+ c->coords.y = coords->y;
+ c->down = c->left = c->right = c->up = NULL;
}
- SDL_SetTextureScaleMode(sprites, scale_mode);
- SDL_DestroySurface(surface);
+ c->contents = CELL_EMPTY;
+ c->path_next = NULL;
+ c->path_prev = NULL;
+ c->path_dist = INT_MAX;
+}
- surface = try_load(TER12_PNG);
- if (!surface)
- return -1;
- ter12_texture = SDL_CreateTextureFromSurface(renderer, surface);
- if (!ter12_texture)
- {
- print_error("SDL_CreateTextureFromSurface failed: %s",
- SDL_GetError());
- exit(1);
- }
- SDL_SetTextureScaleMode(ter12_texture, scale_mode);
- SDL_DestroySurface(surface);
+void
+init_state(struct State* state)
+{
+ assert(state != NULL);
+ init_animation(&state->animation);
+ state->background = NULL;
+ state->grid = NULL;
+ state->cell_scale_factor = CELL_SCALE_FACTOR;
+ state->check_score = 1;
+ assign_point_xyII(-1, -1, &state->dialog_dims);
+ assign_point_xyII(0, 0, &state->dialog_text_origin);
+ state->dialog_shown = 0;
+ state->dialog_text = NULL;
+ state->display_width = DEFAULT_WIDTH;
+ state->display_height = DEFAULT_HEIGHT;
+ state->full_screen = 1;
+ state->hall_of_fame = NULL;
+ state->input_callback = NULL;
+ state->input_col = 0;
+ state->input_instant_reset = 0;
+ state->input_shown = 0;
+ state->location_pointer = 0;
+ state->location_stack = NULL;
+ state->moveno = 1;
+ assign_point_xyIF(0, 0, &state->mouse);
+ state->path_start = NULL;
+ state->path_end = NULL;
+ state->query_shown = 0;
+ state->query_callback = NULL;
+ state->redraw = 1;
+ state->renderer = NULL;
+ state->running = 1;
+ state->screen_width = DEFAULT_WIDTH;
+ state->screen_height = DEFAULT_HEIGHT;
+ state->score = 0;
+ state->search_shortest = 0;
+ assign_point_xyIF(-1, -1, &state->selection_start);
+ assign_point_xyIF(-1, -1, &state->selection_end);
+ state->sprites = NULL;
+ state->state = STATE_SEARCHING_PAUSED;
+ state->ter12_texture = NULL;
+ state->text_scale_factor = TEXT_SCALE_FACTOR;
+ state->window = NULL;
+}
- return 0;
+void
+labyrinth_coord_to_screen_coordF(const float x, const float y,
+ const float startx, const float starty, float* to_x, float* to_y)
+{
+ *to_x = startx + x * CELL_SIZE;
+ *to_y = starty + y * CELL_SIZE;
+}
+
+void
+labyrinth_coord_to_screen_coordI(const int x, const int y, const float startx,
+ const float starty, float* to_x, float* to_y)
+{
+ *to_x = startx + x * CELL_SIZE;
+ *to_y = starty + y * CELL_SIZE;
}
int
-load_labyrinth(const char* pathname)
+load_labyrinth(struct State* state, const char* pathname)
{
- FILE* input = NULL;
+ FILE* input = NULL;
+ struct Cell* c = NULL;
+ SDL_FPoint coords;
int x, y;
int firstline = 1;
+ assert((state != NULL) && (pathname != NULL));
+
input = fopen(pathname, "rt");
if (!input)
return -1;
- labyrinth.cells = (char**)calloc(LABYRINTH_MAXHEIGHT, sizeof(char*));
- if (!labyrinth.cells)
+ state->grid = malloc(
+ sizeof(struct Cell) * LABYRINTH_MAXWIDTH * LABYRINTH_MAXHEIGHT);
+ if (!state->grid)
{
- print_error("calloc failed");
- exit(1);
+ print_error("malloc failed");
+ do_exit(1, state);
}
- for (y = 0; y < LABYRINTH_MAXHEIGHT; y++)
+ state->grid_width = LABYRINTH_MAXWIDTH;
+ state->grid_height = LABYRINTH_MAXHEIGHT;
+ state->location_stack = malloc(
+ sizeof(SDL_FPoint) * LABYRINTH_MAXWIDTH * LABYRINTH_MAXHEIGHT);
+ if (!state->location_stack)
{
- labyrinth.cells[y] = (char*)calloc(LABYRINTH_MAXWIDTH, 1);
- if (!labyrinth.cells[y])
- {
- print_error("calloc failed");
- exit(1);
- }
+ print_error("malloc failed");
+ do_exit(1, state);
}
- fscanf(input, "%d %d\n", &start.x, &start.y);
+
+ fscanf(input, "%d %d\n", &state->start.x, &state->start.y);
/* 1-based coordinates for compatibility */
- start.x--;
- start.y--;
- fscanf(input, "%d %d\n", &end.x, &end.y);
- end.x--;
- end.y--;
-
- x = 0;
- y = 0;
- labyrinth.width = 0;
- labyrinth.height = 0;
+ state->start.x--;
+ state->start.y--;
+ fscanf(input, "%d %d\n", &state->end.x, &state->end.y);
+ state->end.x--;
+ state->end.y--;
+
+ x = 0;
+ y = 0;
+ state->grid_width = 0;
+ state->grid_height = 0;
while (!feof(input))
{
char ch = fgetc(input);
@@ -694,20 +732,26 @@ load_labyrinth(const char* pathname)
print_error("Labyrinth overflow at %d, %d"
" (ch == %c)",
x, y, ch);
- exit(1);
+ do_exit(1, state);
}
- labyrinth.cells[y][x]
- = (ch == '0' ? CELL_EMPTY : CELL_WALL);
+ c = &state->grid[INDEX(x, y)];
+
+ coords.x = x;
+ coords.y = y;
+ init_cell(c, 1, &coords);
+
+ c->contents = (ch == '0' ? CELL_EMPTY : CELL_WALL);
+ c->empty = c->contents == CELL_EMPTY;
x++;
if (firstline)
- labyrinth.width++;
+ state->grid_width++;
}
else if (ch == '\n')
{
x = 0;
y++;
firstline = 0;
- labyrinth.height++;
+ state->grid_height++;
}
else if (strchr(" \r", ch))
/* Ignore CR for compatibility */;
@@ -717,10 +761,33 @@ load_labyrinth(const char* pathname)
ch, ch);
}
- hero.x = start.x;
- hero.y = start.y;
- labyrinth.cells[start.y][start.x] = CELL_START;
- labyrinth.cells[end.y][end.x] = CELL_END;
+ for (y = 0; y < state->grid_height; y++)
+ {
+ for (x = 0; x < state->grid_width; x++)
+ {
+ c = &state->grid[INDEX(x, y)];
+ c->coords.x = x;
+ c->coords.y = y;
+
+ c->left = c->up = c->right = c->down = NULL;
+ if (x > 0)
+ c->left = &state->grid[INDEX(x - 1, y)];
+ if (y > 0)
+ c->up = &state->grid[INDEX(x, y - 1)];
+ if (x < state->grid_width - 1)
+ c->right = &state->grid[INDEX(x + 1, y)];
+ if (y < state->grid_height - 1)
+ c->down = &state->grid[INDEX(x, y + 1)];
+ }
+ }
+
+ state->hero.x = state->start.x;
+ state->hero.y = state->start.y;
+ state->grid[INDEX(state->start.x, state->start.y)].contents
+ = CELL_START;
+ state->grid[INDEX(state->start.x, state->start.y)].starting = 1;
+ state->grid[INDEX(state->end.x, state->end.y)].contents = CELL_END;
+ state->grid[INDEX(state->end.x, state->end.y)].ending = 1;
fclose(input);
@@ -728,120 +795,370 @@ load_labyrinth(const char* pathname)
}
int
-location_pop(SDL_Point* location)
+load_textures(struct State* state)
{
- if (location_pointer == 0)
- return 1;
- location_pointer--;
- location->x = location_stack[location_pointer].x;
- location->y = location_stack[location_pointer].y;
- return 0;
-}
+ SDL_ScaleMode scale_mode = SDL_SCALEMODE_PIXELART;
-void
-location_push(const SDL_Point* location)
-{
- location_stack[location_pointer].x = location->x;
- location_stack[location_pointer].y = location->y;
- location_pointer++;
-}
+ assert(state != NULL);
+
+ state->sprites = try_load(state->renderer, SPRITES_PNG);
+ if (!state->sprites)
+ return -1;
+ SDL_SetTextureScaleMode(state->sprites, scale_mode);
+
+ state->ter12_texture = try_load(state->renderer, TER12_PNG);
+ if (!state->ter12_texture)
+ return -1;
+ SDL_SetTextureScaleMode(state->ter12_texture, scale_mode);
+
+ return 0;
+}
void
-next_step(void)
+location_delete(struct State* state, const int index)
+{
+ assert(state != NULL);
+ if (state->location_pointer == 0 || index < 0
+ || index >= state->location_pointer)
+ return;
+ assign_pointFF(&state->location_stack[state->location_pointer - 1],
+ &state->location_stack[index]);
+ state->location_pointer--;
+}
+
+int
+location_index(const struct State* state, const SDL_FPoint* location)
+{
+ SDL_FPoint* p = NULL;
+ int i;
+
+ assert((state != NULL) && (location != NULL));
+ for (i = 0; i < state->location_pointer; i++)
+ {
+ p = &state->location_stack[i];
+ if (p->x == location->x && p->y == location->y)
+ return i;
+ }
+ return -1;
+}
+
+int
+location_pop(struct State* state, SDL_Point* location)
{
+ assert((state != NULL) && (location != NULL));
+ if (state->location_pointer == 0)
+ return 1;
+ state->location_pointer--;
+ location->x = state->location_stack[state->location_pointer].x;
+ location->y = state->location_stack[state->location_pointer].y;
+ return 0;
+}
+
+void
+location_pushF(struct State* state, const SDL_FPoint* location)
+{
+ assert((state != NULL) && (location != NULL));
+ assert(state->location_stack != NULL);
+ state->location_stack[state->location_pointer].x = location->x;
+ state->location_stack[state->location_pointer].y = location->y;
+ state->location_pointer++;
+}
+
+void
+location_pushI(struct State* state, const SDL_Point* location)
+{
+ assert((state != NULL) && (location != NULL));
+ state->location_stack[state->location_pointer].x = location->x;
+ state->location_stack[state->location_pointer].y = location->y;
+ state->location_pointer++;
+}
+
+void
+mark_path(struct Cell* c)
+{
+ if (!c)
+ return;
+
+ c->path = 1;
+ if (c->path_prev)
+ {
+ c->path_prev->path_next = c;
+ print_debug(
+ "mark_path: Next of (%0.0f, %0.0f) is (%0.0f, %0.0f)",
+ c->path_prev->coords.x, c->path_prev->coords.y,
+ c->path_prev->path_next->coords.x,
+ c->path_prev->path_next->coords.y);
+ }
+ mark_path(c->path_prev);
+}
+
+void
+next_step(struct State* state)
+{
+ struct Cell* from = NULL;
+ struct Cell* to = NULL;
SDL_Point backup;
+ int isx, isy, iex, iey;
+ int idx;
+
+ if (!state->search_shortest)
+ goto next_step_brute_force;
+
+ if (!state->animation.active)
+ {
+ if (!state->path_end)
+ {
+ state->path_start = &state->grid[INDEX(state->start.x,
+ state->start.y)];
+ state->path_end
+ = &state->grid[INDEX(state->end.x, state->end.y)];
+ if (!find_shortest_path(state->path_start, NULL, 0))
+ {
+ reset_path(state);
+ state->path_start->contents = CELL_FAIL;
+ state->path_start = NULL;
+ state->path_end = NULL;
+ state->animation.active = 0;
+ state->state = STATE_FAILURE;
+ print_debug("find_shortest_path FAILED");
+ state->redraw = 1;
+ return;
+ }
+ print_debug(
+ "find_shortest_path successful, path len = %d",
+ state->path_end->path_dist);
+ mark_path(state->path_end);
+ print_debug("M = ");
+ print_matrix(state);
+ if (!state->path_start->path_next)
+ return;
+ }
+
+ state->animation.start.x = state->path_start->coords.x;
+ state->animation.start.y = state->path_start->coords.y;
+ state->animation.end.x = state->path_start->path_next->coords.x;
+ state->animation.end.y = state->path_start->path_next->coords.y;
+ state->animation.position.x = state->path_start->coords.x;
+ state->animation.position.y = state->path_start->coords.y;
+ print_debug("Animation: (%0.0f, %0.0f) to (%0.0f, %0.0f)",
+ state->animation.start.x, state->animation.start.y,
+ state->animation.end.x, state->animation.end.y);
+ calculate_delta(state);
+ state->animation.step = 1;
+ state->animation.final_step = ANIM_STEPS;
+ state->animation.active = 1;
+ state->redraw = 1;
+ print_path(&state->grid[INDEXFI(state->animation.start.x,
+ state->animation.start.y)]);
+ }
+
+ if (state->animation.step == state->animation.final_step)
+ {
+ isx = (int)round(state->animation.start.x);
+ isy = (int)round(state->animation.start.y);
+ iex = (int)round(state->animation.end.x);
+ iey = (int)round(state->animation.end.y);
+ from = &state->grid[INDEX(isx, isy)];
+ to = &state->grid[INDEX(iex, iey)];
+
+ if (to->ending)
+ {
+ state->animation.active = 0;
+ print_debug("Ending node reached: (%0.0f, %0.0f)",
+ state->animation.end.x, state->animation.end.y);
+ }
+ else if (to->path_next)
+ {
+ state->animation.start.x = state->animation.end.x;
+ state->animation.start.y = state->animation.end.y;
+ state->animation.position.x = state->animation.start.x;
+ state->animation.position.y = state->animation.start.y;
+ state->animation.end.x = to->path_next->coords.x;
+ state->animation.end.y = to->path_next->coords.y;
+ print_debug("Next node on path: (%0.0f, %0.0f)",
+ state->animation.end.x, state->animation.end.y);
+ print_debug(
+ "Animation: (%0.0f, %0.0f) to (%0.0f, %0.0f)",
+ state->animation.start.x,
+ state->animation.start.y,
+ state->animation.end.x, state->animation.end.y);
+ calculate_delta(state);
+ state->animation.step = 1;
+ }
+ else
+ {
+ print_debug("End of path at (%0.0f, %0.0f)",
+ state->animation.end.x, state->animation.end.y);
+ state->animation.active = 0;
+ }
+
+ print_debug("grid[%d][%d] = %d <-> grid[%d][%d] = %d", iey, iex,
+ to->contents, isy, isx, from->contents);
+ idx = location_index(state, &to->coords);
+ print_debug("location_index: Index of (%0.0f, %0.0f) is %d",
+ to->coords.x, to->coords.y, idx);
+ assert(idx != -1);
+ location_delete(state, idx);
+ location_pushF(state, &from->coords);
+
+ if (to->ending)
+ {
+ reset_path(state);
+ state->path_start = NULL;
+ state->path_end = NULL;
+ }
+ }
- if (hero.x == end.x && hero.y == end.y)
+ state->animation.position.x += state->animation.delta.x;
+ state->animation.position.y += state->animation.delta.y;
+ state->animation.step++;
+ print_debug(
+ "step_animation: (%0.2f, %0.2f) => (%0.2f, %0.2f), [%d/%d] %s",
+ state->animation.position.x, state->animation.position.y,
+ state->animation.end.x, state->animation.end.y,
+ state->animation.step, state->animation.final_step,
+ state->animation.active ? "ACTIVE" : "INACTIVE");
+ return;
+
+next_step_brute_force:
+ if (state->hero.x == state->end.x && state->hero.y == state->end.y)
{
print_debug("next_step: Success");
- state = STATE_SUCCESS;
+ state->state = STATE_SUCCESS;
}
- else if (passable(hero.x - 1, hero.y))
+ else if (passable(state, state->hero.x - 1, state->hero.y))
{
print_debug("next_step: Trying x-1, y: (%d, %d), "
"pushed to stack (%d, %d)",
- hero.x - 1, hero.y, hero.x, hero.y);
- labyrinth.cells[hero.y][hero.x] = CELL_OK;
- location_push(&hero);
- assign_point_xyIF(hero.x, hero.y, &animation.position);
- assign_point_xyIF(hero.x - 1, hero.y, &animation.destination);
- calculate_delta();
- animation.step = 1;
- animation.final_step = ANIM_STEPS;
- animation.active = 1;
+ state->hero.x - 1, state->hero.y, state->hero.x,
+ state->hero.y);
+ state->grid[INDEX(state->hero.x, state->hero.y)].contents
+ = CELL_OK;
+ location_pushI(state, &state->hero);
+ assign_point_xyIF(state->hero.x, state->hero.y,
+ &state->animation.start);
+ assign_point_xyIF(state->hero.x, state->hero.y,
+ &state->animation.position);
+ assign_point_xyIF(state->hero.x - 1, state->hero.y,
+ &state->animation.end);
+ calculate_delta(state);
+ state->animation.step = 1;
+ state->animation.final_step = ANIM_STEPS;
+ state->animation.active = 1;
}
- else if (passable(hero.x, hero.y - 1))
+ else if (passable(state, state->hero.x, state->hero.y - 1))
{
print_debug("next_step: Trying x, y-1: (%d, %d), "
"pushed to stack (%d, %d)",
- hero.x, hero.y - 1, hero.x, hero.y);
- labyrinth.cells[hero.y][hero.x] = CELL_OK;
- location_push(&hero);
- assign_point_xyIF(hero.x, hero.y, &animation.position);
- assign_point_xyIF(hero.x, hero.y - 1, &animation.destination);
- calculate_delta();
- animation.step = 1;
- animation.final_step = ANIM_STEPS;
- animation.active = 1;
+ state->hero.x, state->hero.y - 1, state->hero.x,
+ state->hero.y);
+ state->grid[INDEX(state->hero.x, state->hero.y)].contents
+ = CELL_OK;
+ location_pushI(state, &state->hero);
+ assign_point_xyIF(state->hero.x, state->hero.y,
+ &state->animation.start);
+ assign_point_xyIF(state->hero.x, state->hero.y,
+ &state->animation.position);
+ assign_point_xyIF(state->hero.x, state->hero.y - 1,
+ &state->animation.end);
+ calculate_delta(state);
+ state->animation.step = 1;
+ state->animation.final_step = ANIM_STEPS;
+ state->animation.active = 1;
}
- else if (passable(hero.x + 1, hero.y))
+ else if (passable(state, state->hero.x + 1, state->hero.y))
{
print_debug("next_step: Trying x+1, y: (%d, %d), "
"pushed to stack (%d, %d)",
- hero.x + 1, hero.y, hero.x, hero.y);
- labyrinth.cells[hero.y][hero.x] = CELL_OK;
- location_push(&hero);
- assign_point_xyIF(hero.x, hero.y, &animation.position);
- assign_point_xyIF(hero.x + 1, hero.y, &animation.destination);
- calculate_delta();
- animation.step = 1;
- animation.final_step = ANIM_STEPS;
- animation.active = 1;
+ state->hero.x + 1, state->hero.y, state->hero.x,
+ state->hero.y);
+ state->grid[INDEX(state->hero.x, state->hero.y)].contents
+ = CELL_OK;
+ location_pushI(state, &state->hero);
+ assign_point_xyIF(state->hero.x, state->hero.y,
+ &state->animation.start);
+ assign_point_xyIF(state->hero.x, state->hero.y,
+ &state->animation.position);
+ assign_point_xyIF(state->hero.x + 1, state->hero.y,
+ &state->animation.end);
+ calculate_delta(state);
+ state->animation.step = 1;
+ state->animation.final_step = ANIM_STEPS;
+ state->animation.active = 1;
}
- else if (passable(hero.x, hero.y + 1))
+ else if (passable(state, state->hero.x, state->hero.y + 1))
{
print_debug("next_step: Trying x, y+1: (%d, %d), "
"pushed to stack (%d, %d)",
- hero.x, hero.y + 1, hero.x, hero.y);
- labyrinth.cells[hero.y][hero.x] = CELL_OK;
- location_push(&hero);
- assign_point_xyIF(hero.x, hero.y, &animation.position);
- assign_point_xyIF(hero.x, hero.y + 1, &animation.destination);
- calculate_delta();
- animation.step = 1;
- animation.final_step = ANIM_STEPS;
- animation.active = 1;
+ state->hero.x, state->hero.y + 1, state->hero.x,
+ state->hero.y);
+ state->grid[INDEX(state->hero.x, state->hero.y)].contents
+ = CELL_OK;
+ location_pushI(state, &state->hero);
+ assign_point_xyIF(state->hero.x, state->hero.y,
+ &state->animation.start);
+ assign_point_xyIF(state->hero.x, state->hero.y,
+ &state->animation.position);
+ assign_point_xyIF(state->hero.x, state->hero.y + 1,
+ &state->animation.end);
+ calculate_delta(state);
+ state->animation.step = 1;
+ state->animation.final_step = ANIM_STEPS;
+ state->animation.active = 1;
}
else
{
/* Back up */
- if (location_pop(&backup))
+ if (location_pop(state, &backup))
{
print_debug("State: failure");
- state = STATE_FAILURE;
+ state->state = STATE_FAILURE;
}
else
{
print_debug("Backing up, stack pop: %d, %d", backup.x,
backup.y);
- labyrinth.cells[hero.y][hero.x] = CELL_FAIL;
- assign_pointIF(&hero, &animation.position);
- assign_pointIF(&backup, &animation.destination);
- calculate_delta();
- animation.step = 1;
- animation.final_step = ANIM_STEPS;
- animation.active = 1;
+ state->grid[INDEX(state->hero.x, state->hero.y)].contents
+ = CELL_FAIL;
+ assign_pointIF(&state->hero, &state->animation.start);
+ assign_pointIF(&state->hero, &state->animation.position);
+ assign_pointIF(&backup, &state->animation.end);
+ calculate_delta(state);
+ state->animation.step = 1;
+ state->animation.final_step = ANIM_STEPS;
+ state->animation.active = 1;
}
}
- redraw = 1;
+ state->redraw = 1;
+}
+
+int
+passable(struct State* state, const int x, const int y)
+{
+ return (x >= 0 && x < state->grid_width && y >= 0
+ && y < state->grid_height
+ && (state->grid[INDEX(x, y)].contents == CELL_EMPTY
+ || state->grid[INDEX(x, y)].contents == CELL_END));
}
int
-passable(const int x, const int y)
+point_equalI(struct SDL_Point* p1, struct SDL_Point* p2)
{
- return (x >= 0 && x < labyrinth.width && y >= 0 && y < labyrinth.height
- && (labyrinth.cells[y][x] == CELL_EMPTY
- || labyrinth.cells[y][x] == CELL_END));
+ assert((p1 != NULL) && (p2 != NULL));
+ return p1->x == p2->x && p1->y == p2->y;
+}
+
+void
+prepare_dialog(struct State* state, const int w, const int h, const char** text,
+ const int dx, const int show)
+{
+ assert((state != NULL) && (text != NULL));
+ assign_point_xyII(w, h, &state->dialog_dims);
+ state->dialog_text_origin.x = dx;
+ state->dialog_text = text;
+ state->dialog_shown = show;
+ if (show)
+ state->redraw = 1;
}
void
@@ -849,91 +1166,583 @@ print_debug(const char* fmt, ...)
{
char buf[PRINT_BUFSIZE];
va_list args;
-
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
- printf("d: %s\n", buf);
- fflush(stdout);
+ SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "d: %s", buf);
}
void
print_error(const char* fmt, ...)
{
+ va_list args;
+ va_start(args, fmt);
+ SDL_LogError(SDL_LOG_CATEGORY_ERROR, fmt, args);
+ va_end(args);
+}
+
+void
+print_matrix(const struct State* state)
+{
+ const struct Cell* c = NULL;
+ int x, y;
+
+ for (y = 0; y < state->grid_height; y++)
+ {
+ for (x = 0; x < state->grid_width; x++)
+ {
+ c = &state->grid[INDEX(x, y)];
+ if (c->starting)
+ printf("%s", cell_image[CELL_START]);
+ else if (c->ending)
+ printf("%s", cell_image[CELL_END]);
+ else if (c->path)
+ printf("%s", cell_image[CELL_PATH]);
+ else if (!c->empty)
+ printf("%s", cell_image[c->contents]);
+ else
+ printf("%s", cell_image[CELL_EMPTY]);
+ // printf("(%0.0f,%0.0f) ", c->coords.x, c->coords.y);
+ /*if (c->empty)
+ printf("E ");
+ else
+ printf("F ");*/
+ }
+ printf("\n");
+ }
+}
+
+void
+print_path(struct Cell* c)
+{
+ printf("(%0.0f, %0.0f)", c->coords.x, c->coords.y);
+ if (c->path_next)
+ {
+ printf(" -> ");
+ print_path(c->path_next);
+ }
+ else
+ printf("\n");
+}
+void
+render_background(struct State* state, SDL_FRect* bg_rect)
+{
+ const SDL_DisplayMode* mode;
+ SDL_FRect cell_rect, dest_rect;
+ SDL_DisplayID display;
+ float csf;
+ float crx, cry;
+ float drx, dry;
+ float sx, sy;
+ float ex, ey;
+ int x, y;
+
+ assert((state != NULL) && (bg_rect != NULL));
+ assert(state->cell_scale_factor != 0.0f);
+
+ csf = state->cell_scale_factor;
+ sx = state->start.x;
+ sy = state->start.y;
+ ex = state->end.x;
+ ey = state->end.y;
+
+ if ((display = SDL_GetDisplayForWindow(state->window)) == 0)
+ {
+ print_error("SDL_GetDisplayForWindow failed: %s",
+ SDL_GetError());
+ do_exit(1, state);
+ }
+
+ if ((mode = SDL_GetCurrentDisplayMode(display)) == NULL)
+ {
+ print_error("SDL_GetCurrentDisplayMode failed: %s",
+ SDL_GetError());
+ do_exit(1, state);
+ }
+
+ state->background = SDL_CreateTexture(state->renderer, mode->format,
+ SDL_TEXTUREACCESS_TARGET, bg_rect->w, bg_rect->h);
+ if (!state->background)
+ {
+ print_error("SDL_CreateTexture failed: %s", SDL_GetError());
+ do_exit(1, state);
+ }
+ if (!SDL_SetRenderTarget(state->renderer, state->background))
+ {
+ print_error("SDL_SetRenderTarget failed: %s", SDL_GetError());
+ do_exit(1, state);
+ }
+
+ SDL_SetRenderDrawColor(state->renderer, 0x22, 0x22, 0x22,
+ SDL_ALPHA_OPAQUE);
+ SDL_RenderFillRect(state->renderer, bg_rect);
+
+ cell_rect.w = CELL_SIZE;
+ cell_rect.h = CELL_SIZE;
+ dest_rect.w = csf * CELL_SIZE;
+ dest_rect.h = csf * CELL_SIZE;
+ for (y = 0; y < state->grid_height; y++)
+ for (x = 0; x < state->grid_width; x++)
+ {
+ if (!((state->grid[INDEX(x, y)].contents != CELL_OK
+ && state->grid[INDEX(x, y)].contents
+ != CELL_FAIL)
+ || (x == sx && y == sy)
+ || (x == ex && y == ey)))
+ continue;
+
+ get_sprite_xy(state->grid[INDEX(x, y)].contents, &crx,
+ &cry);
+ cell_rect.x = crx;
+ cell_rect.y = cry;
+ grid_coord_to_screen_coordI(state, x, y, 0.0f, 0.0f,
+ &drx, &dry);
+ dest_rect.x = drx;
+ dest_rect.y = dry;
+ SDL_RenderTexture(state->renderer, state->sprites,
+ &cell_rect, &dest_rect);
+ }
+
+ SDL_SetRenderTarget(state->renderer, NULL);
+}
+
+/* state is not const because of renderer, etc */
+void
+render_dialog(struct State* state)
+{
+ const char** ptext = NULL;
+ SDL_FRect dialog_rect /*, input_rect, cursor_rect*/;
+ float tsf;
+ int w, h;
+ int sx, sy, dx, _sx;
+
+ assert(state != NULL);
+ assert(state->dialog_text != NULL);
+ assert(state->text_scale_factor != 0.0f);
+
+ ptext = state->dialog_text;
+ w = state->dialog_dims.x;
+ h = state->dialog_dims.y;
+ sx = state->screen_width / 2 - w / 2;
+ sy = state->screen_height / 2 - h / 2;
+ dx = state->dialog_text_origin.x;
+ tsf = state->text_scale_factor;
+
+ dialog_rect.x = sx;
+ dialog_rect.y = sy;
+ dialog_rect.w = w;
+ dialog_rect.h = h;
+
+ SDL_SetRenderDrawBlendMode(state->renderer, SDL_BLENDMODE_BLEND);
+ SDL_SetRenderDrawColor(state->renderer, 0, 0, 0, .75 * 255);
+ SDL_RenderFillRect(state->renderer, &dialog_rect);
+
+ SDL_SetRenderDrawBlendMode(state->renderer, SDL_BLENDMODE_NONE);
+ SDL_SetRenderDrawColor(state->renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
+ SDL_RenderRect(state->renderer, &dialog_rect);
+ SDL_SetRenderDrawColor(state->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
+ dialog_rect.x--;
+ dialog_rect.y--;
+ dialog_rect.w += 2;
+ dialog_rect.h += 2;
+ SDL_RenderRect(state->renderer, &dialog_rect);
+ dialog_rect.x--;
+ dialog_rect.y--;
+ dialog_rect.w += 2;
+ dialog_rect.h += 2;
+ SDL_RenderRect(state->renderer, &dialog_rect);
+
+ while (*ptext)
+ {
+ _sx = sx + dx + tsf * (2 * TER12_WIDTH);
+ if (strchr(*ptext, CENTER_LINE))
+ _sx += w / 2 - dx
+ - tsf * TER12_WIDTH
+ * (2 + strlen_visible(*ptext) / 2);
+ render_text(state, _sx,
+ sy
+ + (ptext - state->dialog_text + 1) * tsf
+ * (TER12_HEIGHT + 2),
+ TER12_WIDTH, TER12_HEIGHT, state->ter12_texture, "%s",
+ *ptext);
+ ptext++;
+ }
+}
+
+void
+render_hero(struct State* state, const float startx, const float starty)
+{
+ SDL_FRect dest_rect, cell_rect;
+ float csf;
+ float crx, cry;
+ float drx, dry;
+
+ assert(state != NULL);
+
+ csf = state->cell_scale_factor;
+ cell_rect.w = CELL_SIZE;
+ cell_rect.h = CELL_SIZE;
+ dest_rect.w = csf * CELL_SIZE;
+ dest_rect.h = csf * CELL_SIZE;
+ get_hero_sprite_xy(state, &crx, &cry);
+ cell_rect.x = crx;
+ cell_rect.y = cry;
+ if (state->animation.active)
+ labyrinth_coord_to_screen_coordF(state->animation.position.x,
+ state->animation.position.y, startx, starty, &drx,
+ &dry);
+ else
+ labyrinth_coord_to_screen_coordI(state->hero.x, state->hero.y,
+ startx, starty, &drx, &dry);
+ dest_rect.x = drx;
+ dest_rect.y = dry;
+ SDL_RenderTexture(state->renderer, state->sprites, &cell_rect,
+ &dest_rect);
+}
+
+void
+render_status(struct State* state)
+{
+ render_text(state, 0,
+ state->screen_height - state->text_scale_factor * TER12_HEIGHT,
+ TER12_WIDTH, TER12_HEIGHT, state->ter12_texture,
+ "Column: \1%2d\2, Row: \1%2d\2"
+ " \1F1\2 = Help"
+ " Status: \1%s\2",
+ state->hero.x + 1, state->hero.y + 1,
+ state_descriptions[state->state]);
+}
+
+void
+render_text(struct State* state, const int x, const int y, const int font_width,
+ const int font_height, SDL_Texture* font_tex, const char* text, ...)
+{
char buf[PRINT_BUFSIZE];
va_list args;
+ const char* pbuf = NULL;
+ SDL_FRect source_rect;
+ SDL_FRect dest_rect;
+ float tsf;
+ int xstart = 0;
+ int ystart = 0;
+ int index = 0;
+ int chars_per_row = 0;
+ char first_char = ' ';
+ char last_char = '~';
+ float tex_w, tex_h;
+ float opacity = TEXT_OPACITY_NORMAL;
- va_start(args, fmt);
- vsnprintf(buf, sizeof(buf), fmt, args);
+ assert((font_tex != NULL) && (text != NULL));
+ assert(state->text_scale_factor != 0.0f);
+ assert(font_width != -1);
+
+ tsf = state->text_scale_factor;
+
+ va_start(args, text);
+ vsnprintf(buf, sizeof(buf), text, args);
va_end(args);
- fprintf(stderr, "%s: %s\n", PROGNAME, buf);
- fflush(stderr);
+
+ source_rect.w = font_width;
+ source_rect.h = font_height;
+
+ dest_rect.x = x;
+ dest_rect.y = y;
+ dest_rect.w = tsf * font_width;
+ dest_rect.h = tsf * font_height;
+
+ if (!SDL_GetTextureSize(font_tex, &tex_w, &tex_h))
+ {
+ SDL_LogError(SDL_LOG_CATEGORY_ERROR,
+ "SDL_GetTextureSize failed: %s", SDL_GetError());
+ do_exit(1, state);
+ }
+
+ SDL_SetTextureBlendMode(font_tex, SDL_BLENDMODE_BLEND);
+
+ chars_per_row = tex_w / (font_width + 1);
+
+ pbuf = buf;
+ while (*pbuf)
+ {
+ if (*pbuf >= first_char && *pbuf <= last_char)
+ {
+ index = (int)(*pbuf - first_char);
+
+ xstart = 1 + index % chars_per_row * (font_width + 1);
+ ystart = 1 + index / chars_per_row * (font_height + 1);
+ }
+ else if (*pbuf == CENTER_LINE)
+ {
+ pbuf++;
+ continue;
+ }
+ else if (*pbuf == BOLD_START)
+ {
+ opacity = TEXT_OPACITY_BOLD;
+ pbuf++;
+ continue;
+ }
+ else if (*pbuf == BOLD_END)
+ {
+ opacity = TEXT_OPACITY_NORMAL;
+ pbuf++;
+ continue;
+ }
+ else if (*pbuf == DIM_START)
+ {
+ opacity = TEXT_OPACITY_DIM;
+ pbuf++;
+ continue;
+ }
+ else
+ break;
+
+ source_rect.x = xstart;
+ source_rect.y = ystart;
+
+ if (!SDL_SetTextureAlphaModFloat(font_tex, opacity))
+ SDL_LogError(SDL_LOG_CATEGORY_ERROR,
+ "SDL_SetTextureAlphaModFloat is not supported");
+ SDL_RenderTexture(state->renderer, font_tex, &source_rect,
+ &dest_rect);
+
+ dest_rect.x += tsf * font_width;
+
+ pbuf++;
+ }
+}
+
+void
+render_things(struct State* state, const float startx, const float starty)
+{
+ SDL_FRect cell_rect, dest_rect;
+ int x, y;
+ float csf;
+ float crx, cry;
+ float drx, dry;
+
+ csf = state->cell_scale_factor;
+ cell_rect.w = CELL_SIZE;
+ cell_rect.h = CELL_SIZE;
+ dest_rect.w = csf * CELL_SIZE;
+ dest_rect.h = csf * CELL_SIZE;
+ for (y = 0; y < state->grid_height; y++)
+ for (x = 0; x < state->grid_width; x++)
+ {
+ if ((state->grid[INDEX(x, y)].contents != CELL_OK
+ && state->grid[INDEX(x, y)].contents
+ != CELL_FAIL)
+ || (x == state->start.x && y == state->start.y)
+ || (x == state->end.x && y == state->end.y))
+ continue;
+
+ get_sprite_xy(state->grid[INDEX(x, y)].contents, &crx,
+ &cry);
+ cell_rect.x = crx;
+ cell_rect.y = cry;
+ labyrinth_coord_to_screen_coordI(x, y, startx, starty,
+ &drx, &dry);
+ dest_rect.x = drx;
+ dest_rect.y = dry;
+ SDL_RenderTexture(state->renderer, state->sprites,
+ &cell_rect, &dest_rect);
+ }
}
void
-reset(void)
+reset(struct State* state)
{
- for (int y = 0; y < labyrinth.height; y++)
- for (int x = 0; x < labyrinth.width; x++)
- if (labyrinth.cells[y][x] == CELL_FAIL
- || labyrinth.cells[y][x] == CELL_OK)
- labyrinth.cells[y][x] = CELL_EMPTY;
- hero.x = start.x;
- hero.y = start.y;
- state = STATE_SEARCHING_PAUSED;
- location_pointer = 0;
- redraw = 1;
+ int x, y;
+ assert(state != NULL);
+
+ if (!state->search_shortest)
+ state->location_pointer = 0;
+
+ for (y = 0; y < state->grid_height; y++)
+ for (x = 0; x < state->grid_width; x++)
+ if (state->grid[INDEX(x, y)].contents == CELL_FAIL
+ || state->grid[INDEX(x, y)].contents == CELL_OK)
+ state->grid[INDEX(x, y)].contents = CELL_EMPTY;
+ state->hero.x = state->start.x;
+ state->hero.y = state->start.y;
+ state->state = STATE_SEARCHING_PAUSED;
+ state->redraw = 1;
}
void
-step_animation(void)
+reset_path(struct State* state)
+{
+ struct Cell* c = NULL;
+ struct Cell* grid = NULL;
+ int x, y;
+
+ assert(state != NULL);
+
+ grid = state->grid;
+ for (y = 0; y < LABYRINTH_MAXHEIGHT; y++)
+ for (x = 0; x < LABYRINTH_MAXWIDTH; x++)
+ {
+ c = &grid[INDEX(x, y)];
+ c->path_next = NULL;
+ c->path_prev = NULL;
+ c->path_dist = INT_MAX;
+ c->starting = 0;
+ c->ending = 0;
+ c->path = 0;
+ }
+}
+void
+step_animation(struct State* state)
+{
+ struct Cell* from = NULL;
+ struct Cell* to = NULL;
+ int isx, isy, iex, iey;
+
+ assert((state != NULL)
+ && !(state->animation.delta.x == 0.0f
+ && state->animation.delta.y == 0.0f));
+
+ if (state->state == STATE_SEARCHING_PAUSED)
+ return;
+
+ if (!state->search_shortest)
+ goto step_animation_brute_force;
+
+ if (state->animation.step == state->animation.final_step)
+ {
+ isx = (int)round(state->animation.start.x);
+ isy = (int)round(state->animation.start.y);
+ iex = (int)round(state->animation.end.x);
+ iey = (int)round(state->animation.end.y);
+ from = &state->grid[INDEX(isx, isy)];
+ to = &state->grid[INDEX(iex, iey)];
+
+ if (!from->starting)
+ from->contents = CELL_OK;
+
+ if (to->ending)
+ {
+ state->animation.active = 0;
+ print_debug("Ending node reached: (%0.0f, %0.0f)",
+ state->animation.end.x, state->animation.end.y);
+ }
+ else if (to->path_next)
+ {
+ state->animation.start.x = state->animation.end.x;
+ state->animation.start.y = state->animation.end.y;
+ state->animation.position.x = state->animation.start.x;
+ state->animation.position.y = state->animation.start.y;
+ state->animation.end.x = to->path_next->coords.x;
+ state->animation.end.y = to->path_next->coords.y;
+
+ print_debug("Next node on path: (%0.0f, %0.0f)",
+ state->animation.end.x, state->animation.end.y);
+ print_debug(
+ "Animation: (%0.0f, %0.0f) to (%0.0f, %0.0f)",
+ state->animation.start.x,
+ state->animation.start.y,
+ state->animation.end.x, state->animation.end.y);
+ calculate_delta(state);
+ state->animation.step = 1;
+ }
+ else
+ {
+ print_debug("End of path at (%0.0f, %0.0f)",
+ state->animation.end.x, state->animation.end.y);
+ state->animation.active = 0;
+ }
+
+ print_debug("grid[%d][%d] = %d <-> grid[%d][%d] = %d", iey, iex,
+ to->contents, isy, isx, from->contents);
+ state->hero.x = to->coords.x;
+ state->hero.y = to->coords.y;
+
+ if (to->ending)
+ {
+ state->state = STATE_SUCCESS;
+ state->redraw = 1;
+ }
+ }
+
+step_animation_brute_force:
+ print_debug("step_animation: (%0.2f, %0.2f) => (%0.2f, %0.2f)",
+ state->animation.position.x, state->animation.position.y,
+ state->animation.position.x + state->animation.delta.x,
+ state->animation.position.y + state->animation.delta.y);
+ state->animation.position.x += state->animation.delta.x;
+ state->animation.position.y += state->animation.delta.y;
+ state->animation.step++;
+
+ if (state->search_shortest)
+ return;
+
+ if (state->animation.step == state->animation.final_step)
+ {
+ state->animation.active = 0;
+ state->hero.x = (int)state->animation.end.x;
+ state->hero.y = (int)state->animation.end.y;
+ }
+}
+
+int
+strlen_visible(const char* s)
{
- animation.position.x += animation.delta.x;
- animation.position.y += animation.delta.y;
- animation.step++;
- if (animation.step == animation.final_step)
+ int result = 0;
+ const char* ps = NULL;
+
+ assert(s != NULL);
+ ps = s;
+ while (*ps)
{
- animation.active = 0;
- hero.x = (int)animation.destination.x;
- hero.y = (int)animation.destination.y;
+ if (*ps != CENTER_LINE && *ps != BOLD_START && *ps != BOLD_END
+ && *ps != DIM_START)
+ result++;
+ ps++;
}
+ print_debug("strlen_visible(%s)=%d\n", s, result);
+ return result;
}
-SDL_Surface*
-try_load(const char* filename)
+SDL_Texture*
+try_load(SDL_Renderer* renderer, const char* filename)
{
+ SDL_Texture* tex = NULL;
char tex_pathname[BUFSIZE];
- SDL_Surface* surface = NULL;
+ assert((renderer != NULL) && (filename != NULL));
snprintf(tex_pathname, BUFSIZE, "%s/%s", DATADIR, filename);
- surface = IMG_Load(tex_pathname);
- if (!surface)
+ tex = IMG_LoadTexture(renderer, tex_pathname);
+ if (!tex)
{
- print_debug("try_load: IMG_Load failed (%s): %s", tex_pathname,
+ print_error("try_load: IMG_LoadTexture failed: %s",
SDL_GetError());
snprintf(tex_pathname, BUFSIZE, "./%s", filename);
print_debug("try_load: trying %s", tex_pathname);
- surface = IMG_Load(tex_pathname);
- if (!surface)
+ tex = IMG_LoadTexture(renderer, tex_pathname);
+ if (!tex)
{
- print_error("try_load: IMG_Load failed (%s): %s",
- tex_pathname, SDL_GetError());
+ SDL_LogError(SDL_LOG_CATEGORY_ERROR,
+ "try_load: IMG_LoadTexture failed: %s",
+ SDL_GetError());
exit(1);
}
+ print_debug("try_load: success (%s)", tex_pathname);
}
- return surface;
+ return tex;
}
int
main(int argc, char** argv)
{
SDL_Event event;
+ struct State state;
float px, py;
unsigned int last_time, current_time;
char datafile_pathname[BUFSIZE];
+ int optind = 1;
- atexit(&cleanup);
+ init_state(&state);
if (!SDL_Init(SDL_INIT_VIDEO))
{
@@ -941,97 +1750,99 @@ main(int argc, char** argv)
exit(1);
}
+ SDL_SetLogPriorities(SDL_LOG_PRIORITY_DEBUG);
if (!SDL_CreateWindowAndRenderer(WIN_TITLE, DEFAULT_WIDTH,
DEFAULT_HEIGHT,
- SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS, &window,
- &renderer))
+ SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS,
+ &state.window, &state.renderer))
{
print_error("SDL_CreateWindowAndRenderer failed: %s",
SDL_GetError());
- exit(1);
+ do_exit(1, &state);
}
- // SDL_SetWindowFullscreenMode(window, NULL);
- // SDL_SetWindowFullscreen(window, 1);
SDL_SetHintWithPriority(SDL_HINT_RENDER_VSYNC, "1", SDL_HINT_OVERRIDE);
- SDL_SetRenderLogicalPresentation(renderer, DEFAULT_WIDTH,
+ SDL_SetRenderLogicalPresentation(state.renderer, DEFAULT_WIDTH,
DEFAULT_HEIGHT, SDL_LOGICAL_PRESENTATION_LETTERBOX);
- /*SDL_SetRenderLogicalPresentation(renderer, DEFAULT_WIDTH,
- DEFAULT_HEIGHT, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE);*/
- /*float factor = (float)714/720;
- print_debug("factor = %0.2f", factor);
- SDL_SetRenderScale(renderer, 1.0f, factor);*/
- calculate_screen_size();
+ calculate_display_size(&state);
- if (load_textures() < 0)
- exit(1);
+ if (load_textures(&state) < 0)
+ do_exit(1, &state);
if (argc > 1)
{
- if (load_labyrinth(*(argv + 1)) < 0)
+ if (!strcmp(argv[1], "-s"))
+ {
+ state.search_shortest = 1;
+ optind++;
+ if (optind >= argc)
+ goto load_default_file;
+ }
+
+ if (load_labyrinth(&state, argv[optind]) < 0)
{
print_error("load_labyrinth failed: %s (%s)",
- strerror(errno), *(argv + 1));
- exit(1);
+ strerror(errno), argv[optind]);
+ do_exit(1, &state);
}
}
else
{
+ load_default_file:
snprintf(datafile_pathname, BUFSIZE, "%s/%s", DATADIR,
DEFAULT_DATAFILE);
- if (load_labyrinth(datafile_pathname) < 0)
+ if (load_labyrinth(&state, datafile_pathname) < 0)
{
print_debug("fopen failed: %s (%s)", strerror(errno),
datafile_pathname);
snprintf(datafile_pathname, BUFSIZE, "./%s",
DEFAULT_DATAFILE);
print_debug("trying %s", datafile_pathname);
- if (load_labyrinth(datafile_pathname) < 0)
+ if (load_labyrinth(&state, datafile_pathname) < 0)
{
print_error("fopen failed: %s (%s)",
strerror(errno), datafile_pathname);
- exit(1);
+ do_exit(1, &state);
}
}
}
- location_stack
- = (SDL_Point*)calloc(LOCATION_STACK_MAX, sizeof(SDL_Point));
- animation.active = 0;
+ state.location_stack = calloc(LOCATION_STACK_MAX, sizeof(SDL_Point));
+ state.animation.active = 0;
SDL_HideCursor();
SDL_GetMouseState(&px, &py);
- pointer.x = px;
- pointer.y = py;
- last_time = SDL_GetTicks();
- while (running)
+ state.pointer.x = px;
+ state.pointer.y = py;
+ last_time = SDL_GetTicks();
+ while (state.running)
{
current_time = SDL_GetTicks();
- if (animation.active)
+ if (state.animation.active)
{
if (current_time > last_time + 1000 / 60)
{
- draw();
- step_animation();
+ draw(&state);
+ step_animation(&state);
last_time = current_time;
}
}
else
{
- if (state == STATE_SEARCHING)
+ if (state.state == STATE_SEARCHING)
{
- next_step();
+ next_step(&state);
}
- if (redraw)
+ if (state.redraw)
{
- draw();
- redraw = 0;
+ draw(&state);
+ state.redraw = 0;
}
}
while (SDL_PollEvent(&event))
- handle_event(&event);
+ handle_event(&state, &event);
}
return 0;
diff --git a/lib/makedate b/lib/makedate
@@ -4,7 +4,38 @@ FALLBACKDATE=${FALLBACKDATE:-unknown}
LC_ALL=C
export LC_ALL
-if [ -d .got ] && command -v got >/dev/null 2>&1; then
+die()
+{
+ printf "%s\n" "$@" >&2
+ exit 1
+}
+
+exists()
+{
+ command -v $* >/dev/null 2>&1
+}
+
+_getent()
+{
+ if exists getent; then
+ command getent "$@" 2>/dev/null
+ return
+ fi
+ case "$1" in
+ passwd|group)
+ if [ -z "$2" ]; then
+ cat /etc/"$1"
+ else
+ awk -F: -vu="$2" '{ if ($3 == u) print }' /etc/"$1"
+ fi
+ ;;
+ *) ;;
+ esac
+}
+
+exists diff || die "The build script $0 requires diff(1), none found"
+
+if [ -d .got ] && exists got; then
got log |
awk '
/^date: / {
@@ -25,9 +56,12 @@ if [ -d .got ] && command -v got >/dev/null 2>&1; then
print month_names[$2] " " $3 ", " $5
exit
}' >date.new
-elif [ -d .git ] && command -v git >/dev/null 2>&1; then
- user=$(getent passwd "$(unalias ls >/dev/null 2>&1 || true; \
- command ls -lnd . | awk '{print $3}')" | awk -F: '{print $1}')
+elif [ -d .git ] && exists git; then
+
+ user=$(printf "%s\n" "$(_getent passwd \
+ "$(unalias ls >/dev/null 2>&1 || true; \
+ command ls -nd . | awk '{print $3}')")" |
+ awk -F: '{print $1}')
if [ -z "$user" ]; then
printf "$0: error: Cannot determine owner of %s" "$(pwd)" >&2
exit 1
diff --git a/lib/makeversion b/lib/makeversion
@@ -1,8 +1,41 @@
#!/bin/sh -x
# vim: set ft=bash:
FALLBACKVER=${FALLBACKVER:-unknown}
+LC_ALL=C
+export LC_ALL
-if [ -d .got ] && command -v got >/dev/null 2>&1; then
+die()
+{
+ printf "%s\n" "$@" >&2
+ exit 1
+}
+
+exists()
+{
+ command -v $* >/dev/null 2>&1
+}
+
+_getent()
+{
+ if exists getent; then
+ command getent "$@" 2>/dev/null
+ return
+ fi
+ case "$1" in
+ passwd|group)
+ if [ -z "$2" ]; then
+ cat /etc/"$1"
+ else
+ awk -F: -vu="$2" '{ if ($3 == u) print }' /etc/"$1"
+ fi
+ ;;
+ *) ;;
+ esac
+}
+
+exists diff || die "The build script $0 requires diff(1), none found"
+
+if [ -d .got ] && exists got; then
lastcommit=$(got log |
awk '/^commit/ {print substr($2,1,7); exit}')
got tag -l |
@@ -30,11 +63,12 @@ if [ -d .got ] && command -v got >/dev/null 2>&1; then
}
printf "\n"
}' >version.new
-elif [ -d .git ] && command -v git >/dev/null 2>&1; then
- LC_ALL=C
- export LC_ALL
- user=$(getent passwd "$(unalias ls >/dev/null 2>&1 || true; \
- command ls -lnd . | awk '{print $3}')" | awk -F: '{print $1}')
+elif [ -d .git ] && exists git; then
+
+ user=$(printf "%s\n" "$(_getent passwd \
+ "$(unalias ls >/dev/null 2>&1 || true; \
+ command ls -nd . | awk '{print $3}')")" |
+ awk -F: '{print $1}')
if [ -z "$user" ]; then
printf "$0: error: Cannot determine owner of %s" "$(pwd)" >&2
exit 1
diff --git a/lib/mkpack b/lib/mkpack
@@ -4,7 +4,7 @@ DISTDIR=$(grep '^PROG[[:space:]]\{1,\}=' config.mk |
sed -E 's,.*=[[:space:]]+([[:alpha:]]+),\1,')-$(cat version)
mkdir "$DISTDIR"
if [ -d .got ] && command -v got >/dev/null 2>&1; then
- files=$(got tree | sed -e 's,[@/*$]$,,g')
+ files=$(got tree -R | sed -e 's,[@/*$]$,,g')
elif [ -d .git ] && command -v git >/dev/null 2>&1; then
files=$(git ls-files)
else
@@ -14,7 +14,7 @@ fi
for f in $files; do
d="$DISTDIR"/"$(dirname "$f")"
mkdir -p "$d"
- cp "$f" "$d"/
+ [ -f "$f" ] && cp "$f" "$d"/
done
tar -cf - "$DISTDIR" | xz -9v - > "$DISTDIR".tar.xz
rm -fr "$DISTDIR"
diff --git a/lib/replacepat b/lib/replacepat
@@ -1,10 +1,6 @@
#!/bin/sh
# vim: set ft=bash:
-infile="$1"
-outfile="$2"
-DATE=$(cat date 2>/dev/null)
-VERSION=$(cat version 2>/dev/null)
-
-sed -e 's|%VERSION%|'"$VERSION"'|g' -e 's|%DATE%|'"$DATE"'|g' \
- -e 's|%DATADIR%|'"$DATADIR"'|g' "$infile" \
- > "$outfile"
+DATE=$(cat date)
+VERSION=$(cat version)
+sed -e 's|%VERSION%|'"$VERSION"'|g' -e 's|%DATE%|'"$DATE"'|g' \
+ -e 's|%DATADIR%|'"$DATADIR"'|g' "$1" > "$2"
diff --git a/local.h.in b/local.h.in
@@ -1,5 +1,5 @@
/* This program is licensed under the terms of GNU GPL v3 or (at your option)
- * any later version. Copyright (C) 2023-2025 Страхиња Радић.
+ * any later version. Copyright (C) 2023-2026 Страхиња Радић.
* See the file LICENSE for exact copyright and license details. */
#define DATADIR "%DATADIR%"