From 8dff3cfc7a0b7c8874d01769c6ab64357c469348 Mon Sep 17 00:00:00 2001 From: Matej Dedina Date: Wed, 17 Jul 2024 09:13:00 +0200 Subject: [PATCH] major changes to gui --- program/CMakeLists.txt | 13 +- .../include/gui/interface/biggest_rectangle.h | 16 ++ program/include/gui/interface/grid.h | 18 ++ program/include/gui/interface/interface.h | 8 + program/include/gui/interface/solver.h | 39 +++ program/include/instance/settings.h | 9 +- program/source/gui/graphics.c | 51 ++-- .../source/gui/interface/biggest_rectangle.c | 89 +++++++ program/source/gui/interface/grid.c | 238 +++++++++++++++++ program/source/gui/interface/interface.c | 83 ++++++ program/source/gui/interface/solver.c | 249 ++++++++++++++++++ program/source/instance/argument.c | 62 ++--- program/source/instance/settings.c | 11 +- program/source/structures/concrete/state.c | 2 +- 14 files changed, 806 insertions(+), 82 deletions(-) create mode 100644 program/include/gui/interface/biggest_rectangle.h create mode 100644 program/include/gui/interface/grid.h create mode 100644 program/include/gui/interface/interface.h create mode 100644 program/include/gui/interface/solver.h create mode 100644 program/source/gui/interface/biggest_rectangle.c create mode 100644 program/source/gui/interface/grid.c create mode 100644 program/source/gui/interface/interface.c create mode 100644 program/source/gui/interface/solver.c diff --git a/program/CMakeLists.txt b/program/CMakeLists.txt index 1bd3c41..a60abf6 100644 --- a/program/CMakeLists.txt +++ b/program/CMakeLists.txt @@ -4,15 +4,17 @@ find_package(GLEW REQUIRED) add_executable(${PROJECT_NAME} main.c) -add_library(program "") +add_library(program -target_sources(program - PRIVATE source/gui/graphics.c source/gui/input.c - source/gui/interface.c source/gui/draw.c + source/gui/interface/grid.c + source/gui/interface/solver.c + source/gui/interface/biggest_rectangle.c + source/gui/interface/interface.c + source/algorithms/depth_first_search.c source/algorithms/backtrack.c source/algorithms/arc_consistency.c @@ -23,10 +25,9 @@ target_sources(program source/instance/settings.c source/instance/statistics.c source/instance/expect.c - + source/structures/concrete/board.c source/structures/concrete/state.c - source/slnoslav.c ) diff --git a/program/include/gui/interface/biggest_rectangle.h b/program/include/gui/interface/biggest_rectangle.h new file mode 100644 index 0000000..0a9f699 --- /dev/null +++ b/program/include/gui/interface/biggest_rectangle.h @@ -0,0 +1,16 @@ +#ifndef GUI_INTERFACE_BIGGEST_RECTANGLE_H +#define GUI_INTERFACE_BIGGEST_RECTANGLE_H + +#include + +#include + +struct nk_rect_array { + struct nk_rect * elements; + size_t size; +}; + +struct nk_rect_array create_max_overlap_rectangles (board_s board); +void destroy_max_overlap_rectangles(struct nk_rect_array * array); + +#endif //GUI_INTERFACE_BIGGEST_RECTANGLE_H diff --git a/program/include/gui/interface/grid.h b/program/include/gui/interface/grid.h new file mode 100644 index 0000000..5d329cf --- /dev/null +++ b/program/include/gui/interface/grid.h @@ -0,0 +1,18 @@ +#ifndef GUI_INTERFACE_GRID_H +#define GUI_INTERFACE_GRID_H + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_KEYSTATE_BASED_INPUT +#include + +#include + +void grid(struct nk_context * context, board_s board); + +#endif //GUI_INTERFACE_GRID_H diff --git a/program/include/gui/interface/interface.h b/program/include/gui/interface/interface.h new file mode 100644 index 0000000..7994135 --- /dev/null +++ b/program/include/gui/interface/interface.h @@ -0,0 +1,8 @@ +#ifndef GUI_INTERFACE_H +#define GUI_INTERFACE_H + +#include + +void interface(struct nk_super * super); + +#endif /* GUI_INTERFACE_H */ \ No newline at end of file diff --git a/program/include/gui/interface/solver.h b/program/include/gui/interface/solver.h new file mode 100644 index 0000000..29e0cc1 --- /dev/null +++ b/program/include/gui/interface/solver.h @@ -0,0 +1,39 @@ +#ifndef GUI_INTERFACE_SOLVER_H +#define GUI_INTERFACE_SOLVER_H + +#include + +#include + +typedef enum ds_action { + CREATE_DS, DESTROY_DS, NEXT_VALUE_DS, NEXT_VALUE_TIMED_DS, + PREV_VALUE_DS, RESET_DS, CURRENT_VALUE_DS, PREV_VALUE_TIMED_DS, + START_VALUE_DS, END_VALUE_DS, +} ds_action_e; + +#define IS_PLAY 1 + +typedef enum player_state { + PLAY_FORWARDS_E = 0b00 | IS_PLAY, + PLAY_BACKWARDS_E = 0b10 | IS_PLAY, + + STOP_UNSET_E = 0b00 << 1, + STOP_START_E = 0b01 << 1, + STOP_MIDDLE_E = 0b10 << 1, + STOP_END_E = 0b11 << 1, +} player_state_e; + +typedef enum solver_state { + SOLVE_UNSET_E, SOLVE_RUNNING_E, SOLVE_FINISHED_E, +} solver_state_e; + +typedef struct player { + player_state_e play_state; + solver_state_e solve_state; +} player_s; + +player_s * get_player_singleton(void); +void solve(struct nk_solver * data); +state_array_s state_provider(const ds_action_e action); + +#endif /* GUI_INTERFACE_SOLVER_H */ diff --git a/program/include/instance/settings.h b/program/include/instance/settings.h index 8ce0b47..9b126aa 100644 --- a/program/include/instance/settings.h +++ b/program/include/instance/settings.h @@ -2,11 +2,7 @@ #define INSTANCE_SETTINGS_H #include -#include - -typedef enum playstate { - STOP_E, PLAY_E, PAUSE_E, -} playstate_e; +#include typedef struct settings { char * filepath; @@ -16,8 +12,7 @@ typedef struct settings { bool is_arc_consistency; bool is_reduce; - playstate_e state; - uint16_t time; + clock_t time_ms; } Settings; Settings * get_settings_singleton(void); diff --git a/program/source/gui/graphics.c b/program/source/gui/graphics.c index d9a285c..afffdf9 100644 --- a/program/source/gui/graphics.c +++ b/program/source/gui/graphics.c @@ -18,8 +18,9 @@ #undef NK_GLFW_GL3_IMPLEMENTATION #include #include -#include #include +#include +#include #include #include @@ -32,31 +33,19 @@ static void error_callback(int number, const char * description) { void _glew_initialize(void); void _glfw_initialize(GLFWwindow ** window, int * width, int * height); +struct nk_super _create_super(struct nk_glfw * glfw, GLFWwindow * window); +void _destroy_super(struct nk_super * super); void gui(void) { struct nk_glfw glfw = { 0 }; int width = 0, height = 0; - struct nk_super super = { 0 }; static GLFWwindow * window; _glfw_initialize(&window, &width, &height); _glew_initialize(); + struct nk_super super = _create_super(&glfw, window); - struct nk_context * context = nk_glfw3_init(&glfw, window, NK_GLFW3_INSTALL_CALLBACKS); - struct nk_font_atlas atlas; - struct nk_font_atlas * atlas_ptr = &atlas; - nk_glfw3_font_stash_begin(&glfw, &atlas_ptr); - nk_glfw3_font_stash_end(&glfw); - - get_settings_singleton()->filepath = "test/puzzles/medium/5.kkr"; - FILE * fp = fopen(get_settings_singleton()->filepath, "rb"); - expect(fp, NO_ACTION, "file pointer variable (fp) is NULL (%p): %s", (void*)fp, strerror(errno)); - board_s board = create_board(fp); - - super.board = &board; - super.context = context; - super.atlas = &glfw.atlas; - + solve(&super.solver); while (!glfwWindowShouldClose(window)) { input(&glfw); interface(&super); @@ -71,7 +60,7 @@ void _glfw_initialize(GLFWwindow ** window, int * width, int * height) { glfwSetErrorCallback(error_callback); error_mode = ASSERT_E; - expect(glfwInit(), NO_ACTION, "GLFW failed to initialize"); + expect(glfwInit(), NO_ACTION, "[ERROR] GLFW failed to initialize"); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); @@ -85,5 +74,29 @@ void _glew_initialize(void) { glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); glewExperimental = GL_TRUE; error_mode = ASSERT_E; - expect(glewInit() == GLEW_OK, NO_ACTION, "Failed to setup GLEW"); + expect(glewInit() == GLEW_OK, NO_ACTION, "[ERROR] Failed to setup GLEW"); } + +struct nk_super _create_super(struct nk_glfw * glfw, GLFWwindow * window) { + struct nk_context * context = nk_glfw3_init(glfw, window, NK_GLFW3_INSTALL_CALLBACKS); + struct nk_font_atlas atlas; + struct nk_font_atlas * atlas_ptr = &atlas; + nk_glfw3_font_stash_begin(glfw, &atlas_ptr); + nk_glfw3_font_stash_end(glfw); + + FILE * fp = fopen(get_settings_singleton()->filepath, "rb"); + expect(fp, NO_ACTION, "[ERROR] File pointer variable (fp) is NULL (%p): %s", (void*)fp, strerror(errno)); + board_s board = create_board(fp); + fclose(fp); + + return (struct nk_super) { + .context = context, + .solver = { + .board = board, + }, + }; +} + +void _destroy_super(struct nk_super * super) { + destroy_board(&super->solver.board); +} \ No newline at end of file diff --git a/program/source/gui/interface/biggest_rectangle.c b/program/source/gui/interface/biggest_rectangle.c new file mode 100644 index 0000000..9f6534b --- /dev/null +++ b/program/source/gui/interface/biggest_rectangle.c @@ -0,0 +1,89 @@ +#include + +#include + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_KEYSTATE_BASED_INPUT +#include +#include + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +bool _is_inner_rectangle(struct nk_rect inner, struct nk_rect outer); +bool _is_outer_rectangle(struct nk_rect inner, struct nk_rect outer); +bool _replace_max_rectangle(struct nk_rect_array * array, struct nk_rect guess); + +struct nk_rect_array create_max_overlap_rectangles(board_s board) { + ulookup_t row_size = board.game.size[ROW_E], col_size = board.game.size[COLUMN_E]; + size_t * matrix_row = calloc(col_size, sizeof(size_t)); + + struct nk_rect_array array = { + .elements = malloc((((row_size * col_size) / 2) + 1) * sizeof(struct nk_rect)), + .size = 0, + }; + + for (size_t r = 0; r < row_size; r++) { + for (size_t c = 0; c < col_size; c++) { + matrix_row[c] = board.grid[r][c] == -1 ? matrix_row[c] + 1 : 0; + } + + for (size_t c = 0; c < col_size; c++) { + if (matrix_row[c] == 0) continue; + float height = matrix_row[c], width = 0, x = c; + + while (matrix_row[c] != 0 && c < col_size) { + height = MIN(height, matrix_row[c]); + width++; + c++; + } + + struct nk_rect temp = { .x = x, .y = r - (height - 1), .w = width, .h = height, }; + if (!_replace_max_rectangle(&array, temp)) array.elements[array.size++] = temp; + } + + for (size_t c = 0; c < col_size; c++) { + if (matrix_row[c] == 0) continue; + struct nk_rect temp = { .x = c, .y = r - (matrix_row[c] - 1), .w = 1, .h = matrix_row[c], }; + if (!_replace_max_rectangle(&array, temp)) array.elements[array.size++] = temp; + } + } + + return array; +} + +void destroy_max_overlap_rectangles(struct nk_rect_array * array) { + free(array->elements); + array->elements = NULL; + array->size = 0; +} + +bool _is_inner_rectangle(struct nk_rect inner, struct nk_rect outer) { + return + (inner.x >= outer.x) && (inner.y >= outer.y) && + ((inner.w + inner.x) <= (outer.w + outer.x)) && + ((inner.h + inner.y) <= (outer.h + outer.y)); +} + +bool _is_outer_rectangle(struct nk_rect inner, struct nk_rect outer) { + return _is_inner_rectangle(outer, inner); +} + +bool _replace_max_rectangle(struct nk_rect_array * array, struct nk_rect guess) { + bool replaced = false; + for (size_t s = 0; s < array->size; s++) { + if (_is_inner_rectangle(array->elements[s], guess)) { + array->elements[s] = guess; + replaced = true; + } + if (_is_outer_rectangle(array->elements[s], guess)) replaced = true; + } + + return replaced; +} diff --git a/program/source/gui/interface/grid.c b/program/source/gui/interface/grid.c new file mode 100644 index 0000000..ed15845 --- /dev/null +++ b/program/source/gui/interface/grid.c @@ -0,0 +1,238 @@ +#include + +#include + +#include +#include + +#include + +#include +#include + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#define RGB_WHITE (nk_rgb(255, 255, 255)) +#define RGB_BLACK (nk_rgb(0, 0, 0)) +#define RGB_DARK_GRAY (nk_rgb(64, 64, 64)) +#define RGB_LIGHT_GRAY (nk_rgb(192, 192, 192)) + +#define SUM_SQUARE(block_size) { \ + .x = 0.15 * ((block_size) / 2), .y = 0.15 * ((block_size) / 2), \ + .w = ((block_size) / 2) * 0.7, .h = ((block_size) / 2) * 0.7, \ +} + +#define EMPTY_SQUARE(block_size) { \ + .x = 0.1 * (block_size), .y = 0.1 * (block_size), \ + .w = (block_size) * 0.8, .h = (block_size) * 0.8, \ +} + +struct nk_rect _background(struct nk_context * context, board_s board); + +void _lines(struct nk_context * context, board_s board, struct nk_rect background); + +void _unset_blocks(struct nk_context * context, board_s board, struct nk_rect background); +void _sum_triangles(struct nk_context * context, board_s board, struct nk_rect background); +void _upper_sum_triangle(struct nk_context * context, struct nk_rect background, ulookup_t row, ulookup_t col, float block_size); +void _lower_sum_triangle(struct nk_context * context, struct nk_rect background, ulookup_t row, ulookup_t col, float block_size); +void _sum_values(struct nk_context * context, board_s board, struct nk_rect background); +void _upper_sum_value(struct nk_context * context, struct nk_rect background, struct nk_rect sum_square, ulookup_t row, ulookup_t col, float block_size, lookup_t value); +void _lower_sum_value(struct nk_context * context, struct nk_rect background, struct nk_rect sum_square, ulookup_t row, ulookup_t col, float block_size, lookup_t value); + +void _empty_values(struct nk_context * context, board_s board, struct nk_rect background); +void _single_value(struct nk_context * context, struct nk_rect background, struct nk_rect empty_square, ulookup_t row, ulookup_t col, float block_size, state_t state_value); +void _multi_value(struct nk_context * context, struct nk_rect background, struct nk_rect empty_square, ulookup_t row, ulookup_t col, float block_size, state_t state_value); + +void _draw_value(struct nk_context * context, struct nk_rect position, struct nk_color bg, struct nk_color fg, int value); + +void grid(struct nk_context * context, board_s board) { + struct nk_rect background = _background(context, board); + _lines(context, board, background); + _unset_blocks(context, board, background); + _sum_triangles(context, board, background); + _sum_values(context, board, background); + _empty_values(context, board, background); +} + +struct nk_rect _background(struct nk_context * context, board_s board) { + ulookup_t row_size = board.game.size[ROW_E], col_size = board.game.size[COLUMN_E]; + struct nk_panel * layout = context->current->layout; + float height = (layout->clip.w / MAX(row_size, col_size)) * MIN(row_size, col_size); + + nk_layout_row_dynamic(context, height, 1); + struct nk_command_buffer * canvas = &context->current->buffer; + struct nk_rect background; + + nk_widget(&background, context); + nk_fill_rect(canvas, background, 0, RGB_WHITE); + + return background; +} + +void _lines(struct nk_context * context, board_s board, struct nk_rect background) { + ulookup_t row_size = board.game.size[ROW_E], col_size = board.game.size[COLUMN_E]; + float block_size = background.w / board.game.size[COLUMN_E]; + + for(ulookup_t i = 1; i < col_size; i++) { + float x0 = background.x + (block_size * i), y0 = background.y; + float x1 = background.x + (block_size * i), y1 = background.y + background.h; + nk_stroke_line(&context->current->buffer, x0, y0, x1, y1, 0.5f, RGB_DARK_GRAY); + } + for(ulookup_t j = 1; j < row_size; j++) { + float x0 = background.x, y0 = background.y + (block_size * j); + float x1 = background.x + background.w, y1 = background.y + (block_size * j); + nk_stroke_line(&context->current->buffer, x0, y0, x1, y1, 0.5f, RGB_DARK_GRAY); + } +} + +void _unset_blocks(struct nk_context * context, board_s board, struct nk_rect background) { + ulookup_t col_size = board.game.size[COLUMN_E]; + float block_size = (background.w / col_size); + + struct nk_rect_array array = create_max_overlap_rectangles(board); + for (size_t s = 0; s < array.size; s++) { + struct nk_rect temp = array.elements[s]; + struct nk_rect trans = { + .x = (temp.x * block_size) + background.x, .y = (temp.y * block_size) + background.y, + .w = (temp.w * block_size) + 0.25, .h = (temp.h * block_size) + 0.25, + }; + nk_fill_rect(&context->current->buffer, trans, 0, RGB_BLACK); + } + destroy_max_overlap_rectangles(&array); +} + +void _sum_triangles(struct nk_context * context, board_s board, struct nk_rect background) { + ulookup_t row_size = board.game.size[ROW_E]; + ulookup_t col_size = board.game.size[COLUMN_E]; + float block_size = (background.w / col_size); + + for (ulookup_t r = 0; r < row_size; r++) { + for (ulookup_t c = 0; c < col_size; c++) { + if (board.game.grids[ROW_E][r][c] > 0) _upper_sum_triangle(context, background, r, c, block_size); + if (board.game.grids[COLUMN_E][r][c] > 0) _lower_sum_triangle(context, background, r, c, block_size); + } + } +} + +void _upper_sum_triangle(struct nk_context * context, struct nk_rect background, ulookup_t row, ulookup_t col, float block_size) { + float x0 = background.x + (col * block_size), y0 = background.y + (row * block_size); + float x1 = x0 + block_size, y1 = y0; + float x2 = x0 + block_size, y2 = y0 + block_size; + + nk_fill_triangle(&context->current->buffer, x0, y0, x1, y1, x2, y2, RGB_DARK_GRAY); + nk_stroke_line(&context->current->buffer, x0, y0, x1, y1, 0.5f, RGB_LIGHT_GRAY); + nk_stroke_line(&context->current->buffer, x0, y0, x2, y2, 0.5f, RGB_LIGHT_GRAY); + nk_stroke_line(&context->current->buffer, x1, y1, x2, y2, 0.5f, RGB_LIGHT_GRAY); +} + +void _lower_sum_triangle(struct nk_context * context, struct nk_rect background, ulookup_t row, ulookup_t col, float block_size) { + float x0 = background.x + (col * block_size), y0 = background.y + (row * block_size); + float x1 = x0, y1 = y0 + block_size; + float x2 = x0 + block_size, y2 = y0 + block_size; + + nk_fill_triangle(&context->current->buffer, x0, y0, x1, y1, x2, y2, RGB_DARK_GRAY); + nk_stroke_line(&context->current->buffer, x0, y0, x1, y1, 0.5f, RGB_LIGHT_GRAY); + nk_stroke_line(&context->current->buffer, x0, y0, x2, y2, 0.5f, RGB_LIGHT_GRAY); + nk_stroke_line(&context->current->buffer, x1, y1, x2, y2, 0.5f, RGB_LIGHT_GRAY); +} + +void _sum_values(struct nk_context * context, board_s board, struct nk_rect background) { + ulookup_t row_size = board.game.size[ROW_E]; + ulookup_t col_size = board.game.size[COLUMN_E]; + float block_size = (background.w / col_size); + + struct nk_rect sum_square = SUM_SQUARE(block_size); + for (ulookup_t r = 0; r < row_size; r++) { + for (ulookup_t c = 0; c < col_size; c++) { + lookup_t row_value = board.game.grids[ROW_E][r][c]; + lookup_t col_value = board.game.grids[COLUMN_E][r][c]; + + if (row_value > 0) _upper_sum_value(context, background, sum_square, r, c, block_size, row_value); + if (col_value > 0) _lower_sum_value(context, background, sum_square, r, c, block_size, col_value); + } + } +} + +void _upper_sum_value(struct nk_context * context, struct nk_rect background, struct nk_rect sum_square, ulookup_t row, ulookup_t col, float block_size, lookup_t value) { + struct nk_rect upper_sum = { + .x = sum_square.x + background.x + (block_size * col) + (block_size / 2), + .y = sum_square.y + background.y + (block_size * row), + .w = sum_square.w, + .h = sum_square.h, + }; + + _draw_value(context, upper_sum, RGB_DARK_GRAY, RGB_WHITE, value); +} + +void _lower_sum_value(struct nk_context * context, struct nk_rect background, struct nk_rect sum_square, ulookup_t row, ulookup_t col, float block_size, lookup_t value) { + struct nk_rect lower_sum = { + .x = sum_square.x + background.x + (block_size * col), + .y = sum_square.y + background.y + (block_size * row) + (block_size / 2), + .w = sum_square.w, + .h = sum_square.h, + }; + + _draw_value(context, lower_sum, RGB_DARK_GRAY, RGB_WHITE, value); +} + +void _empty_values(struct nk_context * context, board_s board, struct nk_rect background) { + const ulookup_t col_size = board.game.size[COLUMN_E]; + float block_size = (background.w / col_size); + struct nk_rect empty_square = EMPTY_SQUARE(block_size); + state_array_s guess = { 0 }; + + if (!(get_player_singleton()->play_state & IS_PLAY)) { + guess = state_provider(CURRENT_VALUE_DS); + } else if (get_player_singleton()->play_state == PLAY_FORWARDS_E) { + guess = state_provider(NEXT_VALUE_TIMED_DS); + } else if (get_player_singleton()->play_state == PLAY_BACKWARDS_E) { + guess = state_provider(PREV_VALUE_TIMED_DS); + } + + for (ulookup_t s = 0; s < guess.size; s++) { + ulookup_t row = board.coords[ROW_E][s]; + ulookup_t col = board.coords[COLUMN_E][s]; + state_t state = guess.elements[s]; + + if (is_one_value(state)) _single_value(context, background, empty_square, row, col, block_size, state); + else _multi_value(context, background, empty_square, row, col, block_size, state); + } +} + +void _single_value(struct nk_context * context, struct nk_rect background, struct nk_rect empty_square, ulookup_t row, ulookup_t col, float block_size, state_t state_value) { + struct nk_rect single = { + .x = empty_square.x + background.x + (block_size * col), + .y = empty_square.y + background.y + (block_size * row), + .w = empty_square.w, + .h = empty_square.h, + }; + + _draw_value(context, single, RGB_WHITE, RGB_BLACK, get_one_value(state_value)); +} + +void _multi_value(struct nk_context * context, struct nk_rect background, struct nk_rect empty_square, ulookup_t row, ulookup_t col, float block_size, state_t state_value) { + for (int i = 0; i < MAX_BLOCK_VALUES; ++i) { + if (!(state_value & (1 << i))) continue; + + struct nk_rect multi = { + .x = empty_square.x + background.x + (block_size * col) + ((i % 3) * (empty_square.w / 3)), + .y = empty_square.y + background.y + (block_size * row) + ((i / 3) * (empty_square.h / 3)), + .w = empty_square.w / 3, + .h = empty_square.h / 3, + }; + + _draw_value(context, multi, RGB_WHITE, RGB_BLACK, get_one_value(state_value & (1 << i))); + } +} + +void _draw_value(struct nk_context * context, struct nk_rect position, struct nk_color bg, struct nk_color fg, int value) { + char string_num[sizeof("-2147483647")] = { 0 }; + sprintf(string_num, "%d", value); + + float temp_height = context->style.font->height; + struct nk_user_font * font = context->style.font; + font->height = position.h; + nk_draw_text(&context->current->buffer, position, string_num, strnlen(string_num, sizeof("-2147483647")), font, bg, fg); + font->height = temp_height; +} diff --git a/program/source/gui/interface/interface.c b/program/source/gui/interface/interface.c new file mode 100644 index 0000000..a444cc2 --- /dev/null +++ b/program/source/gui/interface/interface.c @@ -0,0 +1,83 @@ +#include +#include +#include + +void _player(struct nk_context * context); +void _to_start(struct nk_context * context); +void _previus(struct nk_context * context); +void _playback(struct nk_context * context); +void _play(struct nk_context * context); +void _next(struct nk_context * context); +void _to_end(struct nk_context * context); + +void interface(struct nk_super * super) { + struct nk_context * context = super->context; + board_s board = super->solver.board; + + if (nk_begin(super->context, "KAKURO BOARD", nk_rect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT), NK_WINDOW_BORDER)) { + grid(context, board); + _player(context); + } + nk_end(super->context); +} + +void _player(struct nk_context * context) { + nk_layout_row_dynamic(context, 50, 6); + _to_start(context); + _previus(context); + _playback(context); + _play(context); + _next(context); + _to_end(context); +} + +void _to_start(struct nk_context * context) { + if (nk_button_label(context, "START")) { + state_provider(START_VALUE_DS); + } +} + +void _previus(struct nk_context * context) { + if (nk_button_label(context, "PREV")) { + get_player_singleton()->play_state = STOP_MIDDLE_E; + state_provider(PREV_VALUE_DS); + } +} + +void _playback(struct nk_context * context) { + if (get_player_singleton()->play_state != PLAY_BACKWARDS_E) { + if (nk_button_label(context, "BACK") && get_player_singleton()->play_state != STOP_START_E) { + get_player_singleton()->play_state = PLAY_BACKWARDS_E; + } + } else { + if (nk_button_label(context, "STOP") && get_player_singleton()->play_state != STOP_START_E) { + get_player_singleton()->play_state = STOP_MIDDLE_E; + } + } +} + +void _play(struct nk_context * context) { + if (get_player_singleton()->play_state != PLAY_FORWARDS_E) { + if (nk_button_label(context, "FORW") && get_player_singleton()->play_state != STOP_END_E) { + get_player_singleton()->play_state = PLAY_FORWARDS_E; + } + } else { + if (nk_button_label(context, "STOP") && get_player_singleton()->play_state != STOP_END_E) { + get_player_singleton()->play_state = STOP_MIDDLE_E; + } + } +} + +void _next(struct nk_context * context) { + if (nk_button_label(context, "NEXT")) { + get_player_singleton()->play_state = STOP_MIDDLE_E; + state_provider(NEXT_VALUE_DS); + } +} + +void _to_end(struct nk_context * context) { + if (nk_button_label(context, "END") && get_player_singleton()->solve_state == SOLVE_FINISHED_E) { + state_provider(END_VALUE_DS); + } +} + diff --git a/program/source/gui/interface/solver.c b/program/source/gui/interface/solver.c new file mode 100644 index 0000000..06e17f6 --- /dev/null +++ b/program/source/gui/interface/solver.c @@ -0,0 +1,249 @@ +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#define STACK_DATA_TYPE state_array_s +#include +#include + +#ifdef WIN32 + #include + #include + + int pipe(int pipefd[2]) { return _pipe(pipefd, 65536, _O_BINARY); } + int read(int __fd, const void * __buf, size_t __n) { return _read(__fd, __buf, __n); } + int write(int __fd, const void * __buf, size_t __n) { return _write(__fd, __buf, __n); } +#else + #include +#endif + +__attribute__((constructor)) static void create_state_queue(void) { + state_provider(CREATE_DS); +} + +__attribute__((destructor)) static void destroy_state_queue(void) { + state_provider(DESTROY_DS); +} + +typedef enum pipe_type { + READ_PIPE_E = 0, WRITE_PIPE_E = 1, +} pipe_type_e; + +int pipefd[2]; + +thrd_start_t _solver(void * solution); + +void _create_ds(stack_s * prev, stack_s * next); +void _destroy_ds(stack_s * prev, stack_s * next); +void _reset_ds(stack_s * prev, stack_s * next); +state_array_s _next_value_ds(stack_s * prev, stack_s * next); +state_array_s _next_value_timed_ds(stack_s * prev, stack_s * next); +state_array_s _prev_value_ds(stack_s * prev, stack_s * next); +state_array_s _current_value_ds(stack_s prev); +state_array_s _prev_value_timed_ds(stack_s * prev, stack_s * next); +void _start_value_ds(stack_s * prev, stack_s * next); +void _end_value_ds(stack_s * prev, stack_s * next); + +player_s * get_player_singleton(void) { + static player_s player = { + .play_state = STOP_UNSET_E, + .solve_state = SOLVE_UNSET_E, + }; + + return &player; +} + +void solve(struct nk_solver * data) { + thrd_t tid = { 0 }; + thrd_create(&tid, (thrd_start_t)_solver, (void*)data); +} + +state_array_s state_provider(const ds_action_e action) { + static stack_s prev; + static stack_s next; + + switch (action) { + case CREATE_DS : { _create_ds(&prev, &next); break; } + case DESTROY_DS : { _destroy_ds(&prev, &next); break; } + case RESET_DS : { _reset_ds(&prev, &next); break; } + case NEXT_VALUE_DS : { return _next_value_ds(&prev, &next); } + case NEXT_VALUE_TIMED_DS : { return _next_value_timed_ds(&prev, &next); } + case PREV_VALUE_DS : { return _prev_value_ds(&prev, &next); } + case CURRENT_VALUE_DS : { return _current_value_ds(prev); } + case PREV_VALUE_TIMED_DS : { return _prev_value_timed_ds(&prev, &next); } + case START_VALUE_DS : { _start_value_ds(&prev, &next); break; } + case END_VALUE_DS : { _end_value_ds(&prev, &next); break; } + default : { return (state_array_s) { 0 }; } + } + + return (state_array_s) { 0 }; +} + +thrd_start_t _solver(void * data) { + struct nk_solver * input = data; + + stack_s stack = create_stack(); + + state_array_s initial = create_state_array(input->board.game.empty_count); + set_full_state_array(&initial); + reduce(input->board, &initial); + push_stack(&stack, initial); + + get_player_singleton()->solve_state = SOLVE_RUNNING_E; + while (!is_empty_stack(stack)) { + get_stat_singleton()->dfs_iteration_count++; + + state_array_s guess = pop_stack(&stack); + + if (!look_ahead(input->board, &guess) || backtrack(input->board, guess)) { + destroy_state_array(&guess); + continue; + } + + if (is_end_state(guess)) { + expect(-1 != write(pipefd[WRITE_PIPE_E], &guess, sizeof(state_array_s)), NO_ACTION, "Write to pipe failed: %s", strerror(errno)); + break; + } + + ulookup_t index = shortest_multi_index(guess); + + state_matrix_s next = create_neighbors_state_matrix(guess, index); + for (size_t i = 0; i < next.size; i++) { + if (forward_checking(input->board, &next.elements[i], index)) push_stack(&stack, next.elements[i]); + else destroy_state_array(&next.elements[i]); + } + + set_dfs_stack_max_size(stack.size); +LOOP_END: + expect(-1 != write(pipefd[WRITE_PIPE_E], &guess, sizeof(state_array_s)), NO_ACTION, "[ERROR] Write to pipe failed: %s", strerror(errno)); + } + destroy_stack(&stack, destroy_state_array); + expect(0 == close(pipefd[WRITE_PIPE_E]), NO_ACTION, "[ERROR] Write pipe failed to close with error: %s", strerror(errno)); + get_player_singleton()->solve_state = SOLVE_FINISHED_E; + + return 0; +} + +void _create_ds(stack_s * prev, stack_s * next) { + *prev = create_stack(); + *next = create_stack(); + + error_mode = ASSERT_E; + expect(0 == pipe(pipefd), NO_ACTION, "[ERROR] Pipe initialization failed with error: %s", strerror(errno)); +} + +void _destroy_ds(stack_s * prev, stack_s * next) { + destroy_stack(prev, destroy_state_array); + destroy_stack(next, destroy_state_array); + + error_mode = ASSERT_E; + state_array_s state = { 0 }; + int read_return = read(pipefd[READ_PIPE_E], &state, sizeof(state_array_s)); + expect(-1 != read_return, NO_ACTION, "[ERROR] Read from pipe failed: %s", strerror(errno)); + while (read_return) { + push_stack(prev, state); + read_return = read(pipefd[READ_PIPE_E], &state, sizeof(state_array_s)); + expect(-1 != read_return, NO_ACTION, "[ERROR] Read from pipe failed: %s", strerror(errno)); + } + expect(0 == close(pipefd[READ_PIPE_E]), NO_ACTION, "[ERROR] Read pipe failed to close with error: %s", strerror(errno)); + expect(0 == close(pipefd[WRITE_PIPE_E]), NO_ACTION, "[ERROR] Write pipe failed to close with error: %s", strerror(errno)); +} + +void _reset_ds(stack_s * prev, stack_s * next) { + _destroy_ds(prev, next); + _create_ds(prev, next); +} + +state_array_s _next_value_ds(stack_s * prev, stack_s * next) { + if (!is_empty_stack(*next)) { + push_stack(prev, pop_stack(next)); + } else { + state_array_s state; + + int read_return = read(pipefd[READ_PIPE_E], &state, sizeof(state_array_s)); + + error_mode = ASSERT_E; + expect(-1 != read_return, NO_ACTION, "[ERROR] Read from pipe failed: %s", strerror(errno)); + + if (read_return > 0) push_stack(prev, state); + else get_player_singleton()->play_state = STOP_END_E; + } + + return _current_value_ds(*prev); +} + +state_array_s _next_value_timed_ds(stack_s * prev, stack_s * next) { + static clock_t start; + state_array_s state = { 0 }; + + if ((clock() - start) / (CLOCKS_PER_SEC / 1000) >= get_settings_singleton()->time_ms) { + state = _next_value_ds(prev, next); + start = clock(); + } else { + state = _current_value_ds(*prev); + } + + return state; +} + +state_array_s _prev_value_ds(stack_s * prev, stack_s * next) { + if (prev->size > 1) push_stack(next, pop_stack(prev)); + if (prev->size == 1) get_player_singleton()->play_state = STOP_START_E; + + return _current_value_ds(*prev); +} + +state_array_s _current_value_ds(stack_s prev) { + return is_empty_stack(prev) ? (state_array_s) { 0 } : peek_stack(prev); +} + +state_array_s _prev_value_timed_ds(stack_s * prev, stack_s * next) { + static clock_t start; + state_array_s state = { 0 }; + + if ((clock() - start) / (CLOCKS_PER_SEC / 1000) >= get_settings_singleton()->time_ms) { + state = _prev_value_ds(prev, next); + start = clock(); + } else { + state = _current_value_ds(*prev); + } + + return state; +} + +void _start_value_ds(stack_s * prev, stack_s * next) { + while (prev->size > 1) { + push_stack(next, pop_stack(prev)); + } + + if (prev->size == 1) get_player_singleton()->play_state = STOP_START_E; +} + +void _end_value_ds(stack_s * prev, stack_s * next) { + while (!is_empty_stack(*next)) { + push_stack(prev, pop_stack(next)); + } + + error_mode = ASSERT_E; + state_array_s state = { 0 }; + int read_return = read(pipefd[READ_PIPE_E], &state, sizeof(state_array_s)); + expect(-1 != read_return, NO_ACTION, "[ERROR] Read from pipe failed: %s", strerror(errno)); + while (read_return) { + push_stack(prev, state); + read_return = read(pipefd[READ_PIPE_E], &state, sizeof(state_array_s)); + expect(-1 != read_return, NO_ACTION, "[ERROR] Read from pipe failed: %s", strerror(errno)); + } + + get_player_singleton()->play_state = STOP_END_E; +} diff --git a/program/source/instance/argument.c b/program/source/instance/argument.c index 17b5c90..1321a3b 100644 --- a/program/source/instance/argument.c +++ b/program/source/instance/argument.c @@ -45,10 +45,10 @@ typedef union hash { typedef enum console_argument_flags { FILEPATH_F = (0 << 1) | 0b1, - BACKTRACK_F = (1 << 1) | 0b1, - FORWARD_CHECK_F = (2 << 1) | 0b1, - ARC_CONSISTENCY_F = (3 << 1) | 0b1, - REDUCE_F = (4 << 1) | 0b1, + BACKTRACK_F = (1 << 1), + FORWARD_CHECK_F = (2 << 1), + ARC_CONSISTENCY_F = (3 << 1), + REDUCE_F = (4 << 1), INFORMATION_F = (5 << 1) | 0b0, HELP_F = (6 << 1) | 0b0, @@ -264,47 +264,23 @@ void _setup_filepath(char * value) { } void _setup_backtrack(char * value) { - assert(value && "ARGUMENT VALUE IS NULL"); - - bool bool_true= !strncmp(value, "true", sizeof("true")); - // check if string value is boolean true or false - assert((bool_true|| !strncmp(value, "false", sizeof("false"))) && "BACKTRACK VALUE IS NEITHER TRUE NOR FALSE"); - - // if 't' is true then value == "true", else value == "false" - get_settings_singleton()->is_backtrack = bool_true; + // set backtracking to true + get_settings_singleton()->is_backtrack = true; } void _setup_forward_check(char * value) { - assert(value && "ARGUMENT VALUE IS NULL"); - - bool bool_true = !strncmp(value, "true", sizeof("true")); - // check if string value is boolean true or false - assert((bool_true || !strncmp(value, "false", sizeof("false"))) && "FORWARD CHECK VALUE IS NEITHER TRUE NOR FALSE"); - - // if 't' is true then value == "true", else value == "false" - get_settings_singleton()->is_forward_check = bool_true; + // set forward checking to true + get_settings_singleton()->is_forward_check = true; } void _setup_arc_consistency(char * value) { - assert(value && "ARGUMENT VALUE IS NULL"); - - bool bool_true = !strncmp(value, "true", sizeof("true")); - // check if string value is boolean true or false - assert((bool_true || !strncmp(value, "false", sizeof("false"))) && "ARC CONSISTENCY VALUE IS NEITHER TRUE NOR FALSE"); - - // if 'bool_true' is true then value == "true", else value == "false" - get_settings_singleton()->is_arc_consistency = bool_true; + // set arc consistency to true + get_settings_singleton()->is_arc_consistency = true; } void _setup_reduce(char * value) { - assert(value && "ARGUMENT VALUE IS NULL"); - - bool bool_true = !strncmp(value, "true", sizeof("true")); - // check if string value is boolean true or false - assert((bool_true || !strncmp(value, "false", sizeof("false"))) && "ARC CONSISTENCY VALUE IS NEITHER TRUE NOR FALSE"); - - // if 'bool_true' is true then value == "true", else value == "false" - get_settings_singleton()->is_reduce = bool_true; + // set reduce to true + get_settings_singleton()->is_reduce = true; } void _setup_information(char * value) { @@ -314,15 +290,15 @@ void _setup_information(char * value) { void _setup_help(char * value) { printf("Information helpful for the user:\n"); - printf("\t--help,-h show this message\n"); - printf("\t--information,-i show information about SLNOSLAV\n"); + printf("\t--help,-h show this message\n"); + printf("\t--information,-i show information about SLNOSLAV\n"); printf("\nProgram settings:\n"); - printf("\t--filepath,-fp filepath to .kkr file [\"program/0.kkr\"]\n"); - printf("\t--backtrack,-bt true|false enable/disable backtracking [true]\n"); - printf("\t--forward-check,-fch true|false enable/disable forward checking [true]\n"); - printf("\t--arc-consistency,-ac true|false enable/disable arc-consistency [true]\n"); - printf("\t--reduce,-r true|false enable/disable reduced states [true]\n"); + printf("\t--filepath,-fp filepath to .kkr file [\"program/0.kkr\"]\n"); + printf("\t--backtrack,-bt enable backtracking [disabled]\n"); + printf("\t--forward-check,-fch enable forward checking [disabled]\n"); + printf("\t--arc-consistency,-ac enable arc-consistency [disabled]\n"); + printf("\t--reduce,-r enable reduced states [disabled]\n"); exit(EXIT_SUCCESS); } diff --git a/program/source/instance/settings.c b/program/source/instance/settings.c index 331d878..49240bb 100644 --- a/program/source/instance/settings.c +++ b/program/source/instance/settings.c @@ -3,12 +3,11 @@ Settings * get_settings_singleton(void) { static Settings setup = { .filepath = "program/0.kkr", - .is_backtrack = true, - .is_forward_check = true, - .is_arc_consistency = true, - .is_reduce = true, - .state = STOP_E, - .time = 1000, + .is_backtrack = false, + .is_forward_check = false, + .is_arc_consistency = false, + .is_reduce = false, + .time_ms = 100, }; return &setup; diff --git a/program/source/structures/concrete/state.c b/program/source/structures/concrete/state.c index 853448d..e21a15c 100644 --- a/program/source/structures/concrete/state.c +++ b/program/source/structures/concrete/state.c @@ -101,7 +101,7 @@ state_t get_edge_state(ulookup_t count, edge_type_e type) { } bool is_one_value(state_t state) { - return state && !(state & (state - 1)); + return __builtin_popcount(state) == 1; } ulookup_t get_one_value(state_t state) {