diff --git a/conf.h b/conf.h new file mode 100644 index 0000000000000000000000000000000000000000..36f148ac13a0202fe5a94ead3c289499976234a6 --- /dev/null +++ b/conf.h @@ -0,0 +1,12 @@ +#ifndef CONF_H_ +#define CONF_H_ + +// Game setup +#define TRIES_COUNT 6 +#define WORD_LENGHT 5 + +// Enums +typedef enum Gamemode { SOLO, VERSUS, TOOL_ASSISTED } Gamemode; +typedef enum Pattern { WRONG, MISPLACED, CORRECT, NAP } Pattern; // NAP => Not A Pattern (Only used when computing all possible patterns) + +#endif \ No newline at end of file diff --git a/tmp.txt b/tmp.txt new file mode 100644 index 0000000000000000000000000000000000000000..2ee515d11c8b58d5a54114630ad969cdb00b804f --- /dev/null +++ b/tmp.txt @@ -0,0 +1,16 @@ +AddressSanitizer:DEADLYSIGNAL +================================================================= +==43005==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5577b8bb4608 bp 0x7fffa0d74860 sp 0x7fffa0d745c0 T0) +==43005==The signal is caused by a READ memory access. +==43005==Hint: address points to the zero page. + #0 0x5577b8bb4608 in handle_controls wordle/wordle.c:58 + #1 0x5577b8bb4e85 in launch_game wordle/wordle.c:261 + #2 0x5577b8baca69 in main /home/toguy/dev/hepia/sequencial-programming/wordle/main.c:18 + #3 0x7f4b4553afcf in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 + #4 0x7f4b4553b07c in __libc_start_main_impl ../csu/libc-start.c:409 + #5 0x5577b8bacbd4 in _start (/home/toguy/dev/hepia/sequencial-programming/wordle/bin/main+0x3bd4) + +AddressSanitizer can not provide additional info. +SUMMARY: AddressSanitizer: SEGV wordle/wordle.c:58 in handle_controls +==43005==ABORTING +make: *** [Makefile:18: main] Error 1 diff --git a/tool/test/tool-test.c b/tool/test/tool-test.c index fed7b6a9230cdcfa18508c55a49bdf61234636b8..00b537d93d8f8eceaca31cd33d34a0cbffac76cb 100644 --- a/tool/test/tool-test.c +++ b/tool/test/tool-test.c @@ -12,24 +12,44 @@ void test_compute_matches_should_remain_BALAI(void) { // Act set_gamemode(MENU_SOLO); - init_tool(); - set_answer("BALAI"); + char *answer = "BALAI"; + set_answer(answer); + + printf("ANSWER: %s\n\n", answer); set_try("BRUTE"); Pattern *pattern = generate_pattern(); possibility_t **p = compute_matches("BRUTE", pattern); - save_computed_matches(p); - free(pattern); - pattern = NULL; - - set_try("SALUT"); - pattern = generate_pattern(); - p = compute_matches("SALUT", pattern); - save_computed_matches(p); + for (int i = 0; i < get_remaining_bank_count(); i++) + printf("%s\t%f\n", p[i]->word, p[i]->entropy); free(pattern); + pattern = NULL; + // + // printf("\n\n"); + // + // set_try("BLOCS"); + // pattern = generate_pattern(); + // p = compute_matches("BLOCS", pattern); + // + // for (int i = 0; i < get_remaining_bank_count(); i++) + // printf("%s\t%f\n", p[i]->word, p[i]->entropy); + // + // free(pattern); + // pattern = NULL; + // + // printf("\n\n"); + // + // set_try("BILLY"); + // pattern = generate_pattern(); + // p = compute_matches("BILLY", pattern); + // + // for (int i = 0; i < get_remaining_bank_count(); i++) + // printf("%s\t%f\n", p[i]->word, p[i]->entropy); + // + // free(pattern); // Assert TEST_ASSERT_EQUAL_INT(get_remaining_bank_count(), 1); @@ -39,5 +59,4 @@ void test_compute_matches_should_remain_BALAI(void) { free(target); destroy_possibilities(p); - destroy_tool(); } diff --git a/tool/tool.c b/tool/tool.c index 2a09095f2506e483867b451ea943e249d2a5504e..7a801048869f64a59a33665912d7d65824cf29f6 100644 --- a/tool/tool.c +++ b/tool/tool.c @@ -8,21 +8,6 @@ char **remaining_bank; int remaining_bank_count; possibility_t **possibilities; -/** - * @brief Create a possibility - * - * @param word - * @param entropy - * @return - */ -possibility_t *create_possibility(char *word, double entropy) { - possibility_t *p = malloc(sizeof(possibility_t)); - p->word = calloc(WORD_LENGHT + 1, sizeof(char)); - strcpy(p->word, word); - p->entropy = entropy; - return p; -} - /** * @brief Destroiy a given matches set * @@ -56,6 +41,10 @@ matches_t *filter_out_remaining_bank(char *word, Pattern *pattern) { char current_word[WORD_LENGHT + 1]; strcpy(current_word, remaining_bank[j]); + // Exclude current word + if (strcmp(current_word, word) == 0) + continue; + bool valid_word = true; for (int i = 0; i < WORD_LENGHT; i++) { // Filter out the non-valid words @@ -115,14 +104,12 @@ double compute_entropy(char *word) { matches_t *matches = filter_out_remaining_bank(word, (Pattern[WORD_LENGHT]){*curr[0], *curr[1], *curr[2], *curr[3], *curr[4]}); // Skip unmatch pattern - if (matches->count == 0) { - destroy_matches(matches); - break; + if (matches->count != 0) { + // Apply Shannon's formula + double px = ((double)matches->count / (double)remaining_bank_count); + sum += px * (log2(1 / px)); } - // Apply Shannon's formula - double px = ((double)matches->count / (double)remaining_bank_count); - sum += px * (log2(1 / px)); destroy_matches(matches); // Go to next pattern @@ -146,6 +133,21 @@ double compute_entropy(char *word) { return sum; } +/** + * @brief Create a possibility + * + * @param word + * @param entropy + * @return + */ +possibility_t *create_possibility(char *word) { + possibility_t *p = malloc(sizeof(possibility_t)); + p->word = calloc(WORD_LENGHT + 1, sizeof(char)); + strcpy(p->word, word); + p->entropy = compute_entropy(word); + return p; +} + /** * @brief Compare two given possibilites * @@ -216,8 +218,9 @@ possibility_t **compute_matches(char *word, Pattern *pattern) { possibility_count = matches->count; possibilities = realloc(possibilities, possibility_count * sizeof(possibility_t *)); for (int i = 0; i < possibility_count; i++) - possibilities[i] = create_possibility(matches->words[i], 0.0); // compute_entropy(matches->words[i]) + possibilities[i] = create_possibility(matches->words[i]); + save_computed_matches(possibilities); destroy_matches(matches); // Sort possibilities by entropy ASC diff --git a/tool/tool.h b/tool/tool.h index c1894a0b7000566bdbf7d816e809b4660d6afaf2..94a27a4d7be9a4359e9a4b7689947510c869d1e6 100644 --- a/tool/tool.h +++ b/tool/tool.h @@ -1,8 +1,8 @@ #ifndef TOOL_H #define TOOL_H +#include "../conf.h" #include "../word-bank/bank.h" -#include "../wordle/wordle.h" #include <math.h> #include <stdbool.h> diff --git a/ui/ui.c b/ui/ui.c index 1dbf5ec6f223df4a19ab47770c8fb70a16594bff..5dfc769c1ef5b43947c0ca7cdd9371a65678937c 100644 --- a/ui/ui.c +++ b/ui/ui.c @@ -11,6 +11,7 @@ // Windows WINDOW *ui_window = NULL; WINDOW *help = NULL; +WINDOW *tool = NULL; char main_title[8][64] = {"$$\\ $$\\ $$$$$$\\ $$$$$$$\\ $$$$$$$\\ $$\\ $$$$$$$$\\ ", "$$ | $\\ $$ |$$ __$$\\ $$ __$$\\ $$ __$$\\ $$ | $$ _____|", @@ -90,6 +91,17 @@ void hide_help() { help = NULL; } +/** + * @brief Hide the tool subwindow + */ +void hide_tool() { + wclear(ui_window); + delwin(tool); + refresh(); + + tool = NULL; +} + //========================== // PUBLIC //========================== @@ -151,6 +163,7 @@ bool init_term() { void cleanup_term() { curs_set(1); nocbreak(); + delwin(tool); delwin(help); delwin(ui_window); endwin(); @@ -237,7 +250,35 @@ void toggle_help() { wrefresh(help); } -void show_gameboard(int tries_count, int word_length, char **tries, int **validations, int score) { +void toggle_tool(int count, char words[count][WORD_LENGHT + 1], double *entropies) { + // Delete existing instance of the subwindow + if (tool != NULL) { + hide_tool(); + return; + } + + // Create new instance of the subwindow + tool = subwin(ui_window, LINES - 2, 30, 1, 1); + refresh(); + + if (tool == NULL) { + cleanup_term(); + exit(EXIT_FAILURE); + } + + scrollok(tool, TRUE); + box(tool, 0, 0); + mvwprintw(tool, 0, 1, " Tool "); // Title + + // Content + for (int i = 0; i < count; i++) + mvwprintw(tool, 2 + i, 1, "%s\t%f\n", words[i], entropies[i]); + + refresh(); + wrefresh(tool); +} + +void show_gameboard(int tries_count, int word_length, char **tries, Pattern **patterns, int score) { for (int i = 0; i < tries_count; i++) { // Draw current try letters for (int j = 0; j < word_length; j++) { @@ -248,9 +289,9 @@ void show_gameboard(int tries_count, int word_length, char **tries, int **valida } // Letters - if (validations[i][j] == 1) + if (patterns[i][j] == MISPLACED) attron(COLOR_PAIR(ORANGE_PAIR)); - if (validations[i][j] == 2) + if (patterns[i][j] == CORRECT) attron(COLOR_PAIR(GREEN_PAIR)); mvaddch(GAMEBOARD_Y_OFFSET + i - menu_offset_modifer() + 5, TERM_MID_COLS - word_length + (j * 2), tries[i][j]); diff --git a/ui/ui.h b/ui/ui.h index 75042339ac83a38733d962f2c1ae4c53b285dc81..6a19096cc3bf81b7f4fe25085ba01367804414ac 100644 --- a/ui/ui.h +++ b/ui/ui.h @@ -1,6 +1,7 @@ #ifndef UI_H_ #define UI_H_ +#include "../conf.h" #include <ncurses.h> #include <stdbool.h> #include <stdio.h> @@ -96,15 +97,24 @@ int show_menu(); */ void toggle_help(); +/** + * @brief Toggle the tool window + * + * @param count + * @param words + * @param entropies + */ +void toggle_tool(int count, char words[count][WORD_LENGHT + 1], double *entropies); + /** * @brief Show the gameboard * * @param tries_count * @param word_length * @param tries - * @param validations + * @param patterns * @param score */ -void show_gameboard(int tries_count, int word_length, char **tries, int **validations, int score); +void show_gameboard(int tries_count, int word_length, char **tries, Pattern **patterns, int score); #endif \ No newline at end of file diff --git a/wordle/wordle.c b/wordle/wordle.c index 20992deb13c844ab1abd8c89c6086fc9dedd3a3f..556ba748f9a3b9b9f4a8f5267932079bdef285e8 100644 --- a/wordle/wordle.c +++ b/wordle/wordle.c @@ -8,7 +8,8 @@ bool _game_finished = false; int score = 0; char **tries; char *chosen_word; -int **validations; +Pattern **patterns; +possibility_t **remaining_possibilities; int current_try_id = 0; int current_try_letter_id = 0; @@ -30,7 +31,7 @@ void render_game() { break; } - show_gameboard(TRIES_COUNT, WORD_LENGHT, tries, validations, score); + show_gameboard(TRIES_COUNT, WORD_LENGHT, tries, patterns, score); } /** @@ -44,6 +45,23 @@ void handle_controls(int key) { toggle_help(); break; + case KEYBIND_TAG: + if (mode == TOOL_ASSISTED) { + if (current_try_id == 0) + break; + + char words[get_remaining_bank_count()][WORD_LENGHT + 1]; + double entropies[get_remaining_bank_count()]; + + for (int i = 0; i < get_remaining_bank_count(); i++) { + strcpy(words[i], remaining_possibilities[i]->word); + entropies[i] = remaining_possibilities[i]->entropy; + } + + toggle_tool(get_remaining_bank_count(), words, entropies); + } + break; + case KEYBIND_QUIT: terminate_game(); break; @@ -52,10 +70,10 @@ void handle_controls(int key) { if (!validate_guess(tries[current_try_id])) break; - // Validations + // Patterns Pattern *pattern = generate_pattern(); for (int i = 0; i < WORD_LENGHT; i++) - validations[current_try_id][i] = pattern[i]; + patterns[current_try_id][i] = pattern[i]; free(pattern); current_try_id += 1; @@ -64,10 +82,12 @@ void handle_controls(int key) { // Place correct letters from previous in the current one if (current_try_id < TRIES_COUNT) { for (int i = 0; i < WORD_LENGHT; i++) { - if (validations[current_try_id - 1][i] == CORRECT) + if (patterns[current_try_id - 1][i] == CORRECT) tries[current_try_id][i] = tries[current_try_id - 1][i]; } } + + remaining_possibilities = compute_matches(tries[current_try_id - 1], patterns[current_try_id - 1]); break; } } @@ -76,6 +96,8 @@ void handle_controls(int key) { // PUBLIC //========================== void initialize_game() { + init_tool(); + // Reset game variables _game_finished = false; current_try_id = 0; @@ -86,10 +108,10 @@ void initialize_game() { set_answer(get_random_word()); tries = calloc(TRIES_COUNT, sizeof(char *)); - validations = malloc(sizeof(int *) * TRIES_COUNT); + patterns = malloc(sizeof(Pattern *) * TRIES_COUNT); for (int i = 0; i < TRIES_COUNT; i++) { - validations[i] = calloc(WORD_LENGHT + 1, sizeof(int)); + patterns[i] = calloc(WORD_LENGHT + 1, sizeof(Pattern)); tries[i] = calloc(WORD_LENGHT + 1, sizeof(char)); // +1 because of \0 } } @@ -97,16 +119,20 @@ void initialize_game() { void terminate_game() { _game_finished = true; } void destroy_game() { + destroy_tool(); + for (int i = 0; i < TRIES_COUNT; i++) { free(tries[i]); - free(validations[i]); + free(patterns[i]); } free(tries); - free(validations); + free(patterns); } void restart_game() { + destroy_tool(); + init_tool(); score += (TRIES_COUNT - current_try_id) * 10; set_answer(get_random_word()); @@ -115,7 +141,7 @@ void restart_game() { for (int i = 0; i < TRIES_COUNT; i++) { for (int j = 0; j < WORD_LENGHT; j++) { - validations[i][j] = 0; + patterns[i][j] = WRONG; tries[i][j] = '\0'; } } @@ -125,7 +151,7 @@ bool lose_condition() { return (current_try_id > TRIES_COUNT - 1); } bool win_condition() { for (int i = 0; i < WORD_LENGHT; i++) { - if (validations[current_try_id - (current_try_id > 0 ? 1 : 0)][i] != CORRECT) + if (patterns[current_try_id - (current_try_id > 0 ? 1 : 0)][i] != CORRECT) return false; } @@ -135,7 +161,7 @@ bool win_condition() { bool validate_letter(int key) { return (islower(key) || isupper(key)); } Pattern *generate_pattern() { - Pattern *validation = calloc(WORD_LENGHT, sizeof(int)); + Pattern *pattern = calloc(WORD_LENGHT, sizeof(int)); char cpy[WORD_LENGHT + 1]; strcpy(cpy, chosen_word); @@ -144,20 +170,20 @@ Pattern *generate_pattern() { continue; if (tries[current_try_id][i] == chosen_word[i]) { - validation[i] = CORRECT; + pattern[i] = CORRECT; cpy[i] = ' '; continue; } char *addr; if ((addr = strchr(chosen_word, tries[current_try_id][i])) != NULL) { - validation[i] = MISPLACED; + pattern[i] = MISPLACED; cpy[(int)(addr - chosen_word)] = ' '; // Get the index of the found char and replace with space continue; } } - return validation; + return pattern; } bool validate_guess(char current_try[WORD_LENGHT]) { diff --git a/wordle/wordle.h b/wordle/wordle.h index 3c6c0fd99dbad815294bb0fcd72771f265030172..87ba5166767059bd08e6eb0babf60f4bd0a4c131 100644 --- a/wordle/wordle.h +++ b/wordle/wordle.h @@ -1,20 +1,14 @@ #ifndef WORDLE_H_ #define WORDLE_H_ +#include "../conf.h" +#include "../tool/tool.h" #include "../ui/ui.h" #include "../word-bank/bank.h" #include <ctype.h> #include <stdbool.h> #include <string.h> -// Game setup -#define TRIES_COUNT 6 -#define WORD_LENGHT 5 - -// Enums -typedef enum Gamemode { SOLO, VERSUS, TOOL_ASSISTED } Gamemode; -typedef enum Pattern { WRONG, MISPLACED, CORRECT, NAP } Pattern; // NAP => Not A Pattern (Only used when computing all possible patterns) - /** * @brief Initialize the game */