Skip to content
Snippets Groups Projects
ui.c 9.60 KiB
#include "ui.h"
#include <curses.h>

#define MENU_MAX 4
#define TITLE_Y_OFFSET 2
#define MENU_CHOICE_Y_OFFSET 20
#define GAMEBOARD_Y_OFFSET 13

//==========================
//        PRIVATE
//==========================
// Windows
WINDOW *ui_window = NULL;
WINDOW *help = NULL;
WINDOW *tool = NULL;

char main_title[8][64] = {"$$\\      $$\\  $$$$$$\\  $$$$$$$\\  $$$$$$$\\  $$\\       $$$$$$$$\\ ",
                          "$$ | $\\  $$ |$$  __$$\\ $$  __$$\\ $$  __$$\\ $$ |      $$  _____|",
                          "$$ |$$$\\ $$ |$$ /  $$ |$$ |  $$ |$$ |  $$ |$$ |      $$ |      ",
                          "$$ $$ $$\\$$ |$$ |  $$ |$$$$$$$  |$$ |  $$ |$$ |      $$$$$\\    ",
                          "$$$$  _$$$$ |$$ |  $$ |$$  __$$< $$ |  $$ |$$ |      $$  __|   ",
                          "$$$  / \\$$$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |      $$ |      ",
                          "$$  /   \\$$ | $$$$$$  |$$ |  $$ |$$$$$$$  |$$$$$$$$\\ $$$$$$$$\\ ",
                          "\\__/     \\__| \\______/ \\__|  \\__|\\_______/ \\________|\\________|"};
char mode_solo_title[8][40] = {" $$$$$$\\   $$$$$$\\  $$\\       $$$$$$\\  ", "$$  __$$\\ $$  __$$\\ $$ |     $$  __$$\\ ",
                               "$$ /  \\__|$$ /  $$ |$$ |     $$ /  $$ |",    "\\$$$$$$\\  $$ |  $$ |$$ |     $$ |  $$ |",
                               " \\____$$\\ $$ |  $$ |$$ |     $$ |  $$ |",   "$$\\   $$ |$$ |  $$ |$$ |     $$ |  $$ |",
                               "\\$$$$$$  | $$$$$$  |$$$$$$$$\\ $$$$$$  |",   " \\______/  \\______/ \\________|\\______/ "};
char mode_1v1_title[8][54] = {
    "  $$\\         $$\\    $$\\                   $$\\",   "$$$$ |        $$ |   $$ |                $$$$ |",
    "\\_$$ |        $$ |   $$ | $$$$$$$\\       \\_$$ |",   "  $$ |        \\$$\\  $$  |$$  _____|        $$ |",
    "  $$ |         \\$$\\$$  / \\$$$$$$\\          $$ |",  "  $$ |          \\$$$  /   \\____$$\\         $$ |",
    " $$$$$$\\          \\$  /   $$$$$$$  |      $$$$$$\\", "  \\______|          \\_/    \\_______/       \\______|"};
char mode_computer_title[8][21] = {"$$$$$$\\  $$$$$$\\  ", "\\_$$  _|$$  __$$\\ ", "  $$ |  $$ /  $$ |",  "  $$ |  $$$$$$$$ |",
                                   "  $$ |  $$  __$$ |",   "  $$ |  $$ |  $$ |",   "$$$$$$\\ $$ |  $$ |", "\\______|\\__|  \\__|"};

/**
 * @brief Set the color scheme of the programm
 *
 */
void set_colors() {
    init_pair(GREEN_PAIR, COLOR_GREEN, COLOR_BLACK);
    init_pair(ORANGE_PAIR, COLOR_YELLOW, COLOR_BLACK);
    init_pair(WHITE_PAIR, COLOR_WHITE, COLOR_BLACK);
    init_pair(RED_PAIR, COLOR_RED, COLOR_BLACK);
}

/**
 * @brief Is the window too small
 *
 * @return true
 * @return false
 */
bool window_too_small() { return (LINES < MENU_CHOICE_Y_OFFSET + MENU_MAX + 10); }

/**
 * @brief Modify the menu offset depending on the window size
 *
 * @return int
 */
int menu_offset_modifer() { return (window_too_small() ? 15 : 0); }

/**
 * @brief Get the selected menu choice
 *
 * @return int
 */
void draw_menu(int menu_item) {
    char menu_choices[MENU_MAX][20] = {" Solo ", " 1 Vs 1 ", " Computer ", " Quit "};
    // Print of the menu choices
    for (int i = 0; i < MENU_MAX; i++) {
        if (i == menu_item)
            attron(A_STANDOUT);
        if (i == MENU_QUIT)
            attron(COLOR_PAIR(RED_PAIR));

        printw_center(menu_choices[i], MENU_CHOICE_Y_OFFSET + (i * 2) - menu_offset_modifer());
        attroff(COLOR_PAIR(WHITE_PAIR));
        attroff(A_STANDOUT);
    }
}

/**
 * @brief Hide the help subwindow
 */
void hide_help() {
    wclear(ui_window);
    delwin(help);
    refresh();

    help = NULL;
}

/**
 * @brief Hide the tool subwindow
 */
void hide_tool() {
    wclear(ui_window);
    delwin(tool);
    refresh();

    tool = NULL;
}

//==========================
//        PUBLIC
//==========================
void printw_center(char *foo, int line) { mvaddstr(line, TERM_MID_COLS - (strlen(foo) / 2), foo); }

void draw_title(int id) {
    if (window_too_small())
        return;

    for (int i = 0; i < TITLES_LINE_COUNT; i++) {
        switch (id) {
        case MAIN_TITLE_ID:
            printw_center(main_title[i], i + TITLE_Y_OFFSET);
            break;

        case SOLO_TITLE_ID:
            printw_center(mode_solo_title[i], i + TITLE_Y_OFFSET);
            break;

        case ONE_V_ONE_TITLE_ID:
            printw_center(mode_1v1_title[i], i + TITLE_Y_OFFSET);
            break;

        case COMPUTER_TITLE_ID:
            printw_center(mode_computer_title[i], i + TITLE_Y_OFFSET);
            break;
        }
    }
}

void show_answer(char *answer) { printw_center(answer, GAMEBOARD_Y_OFFSET + 15); }

void hide_answer() { printw_center("     ", GAMEBOARD_Y_OFFSET + 15); }

bool init_term() {
    ui_window = initscr();
    cbreak();
    noecho();
    curs_set(0);
    keypad(stdscr, true);

    if (ui_window == NULL)
        return false;

    start_color();
    if (!has_colors() || !can_change_color()) {
        printf("Warning. Your terminal can't handle this program.\n");
        return false;
    }

    set_colors();

    return true;
}

void cleanup_term() {
    curs_set(1);
    nocbreak();
    delwin(tool);
    delwin(help);
    delwin(ui_window);
    endwin();
    refresh();
}

int show_menu() {
    hide_help();

    // Title print
    if (!window_too_small())
        draw_title(MAIN_TITLE_ID);

    // Menu print
    attron(COLOR_PAIR(GREEN_PAIR));
    attron(A_BOLD);
    printw_center("====== MENU ======", MENU_CHOICE_Y_OFFSET - 3 - menu_offset_modifer());
    attroff(A_BOLD);
    attron(COLOR_PAIR(WHITE_PAIR));

    attron(A_DIM);
    printw_center("Use arrow keys to move; Enter to chose", MENU_CHOICE_Y_OFFSET + 10 - menu_offset_modifer());
    attroff(A_DIM);

    // Menu choices handler
    int key = 0, menu_item = 0;
    do {
        switch (key) {
        case KEY_DOWN:
            menu_item += 1;
            if (menu_item > MENU_MAX - 1)
                menu_item = 0;
            break;

        case KEY_UP:
            menu_item -= 1;
            if (menu_item < 0)
                menu_item = MENU_MAX - 1;
            break;
        }

        draw_menu(menu_item);
        key = getch();
    } while (key != '\n');

    refresh();

    return menu_item;
}

void toggle_help() {
    // Delete existing instance of the subwindow
    if (help != NULL) {
        hide_help();
        return;
    }

    // Create new instance of the subwindow
    help = subwin(ui_window, 7, COLS - 2, LINES - 7, 1);
    refresh();

    if (help == NULL) {
        cleanup_term();
        exit(EXIT_FAILURE);
    }

    box(help, 0, 0);
    mvwprintw(help, 0, 1, " Help "); // Title

    // Content
    // Keybindings
    wattron(help, A_BOLD);
    mvwprintw(help, 2, 2, "Ctrl+h");
    mvwprintw(help, 3, 2, "Ctrl+t");
    mvwprintw(help, 4, 2, "Ctrl+w");
    wattroff(help, A_BOLD);

    // Description
    mvwprintw(help, 2, 10, "Show this page");
    mvwprintw(help, 3, 10, "Toggle the assistance tool (only in Computer mode)");
    mvwprintw(help, 4, 10, "Quit the game");

    refresh();
    wrefresh(help);
}

void show_tool() {
    // 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);
    }

    refresh();
    wrefresh(tool);
}

void update_tool(int count, char words[count][WORD_LENGHT + 1], double *entropies) {
    wclear(tool);

    box(tool, 0, 0);
    mvwprintw(tool, 0, 1, " Tool "); // Title

    mvwprintw(tool, 2, 2, " Mot \t\tEntropie");

    for (int i = 0; i < (count > 30 ? 30 : count); i++)
        mvwprintw(tool, 4 + i, 2, "%s\t\t%f", words[i], entropies[i]);

    refresh();
    wrefresh(tool);
}

void show_gameboard(int tries_count, int word_length, char **tries, Pattern **patterns, int* scores, Gamemode mode, bool p2_turn, int current_turn) {
    for (int i = 0; i < tries_count; i++) {
        // Draw current try letters
        for (int j = 0; j < word_length; j++) {
            // Placeholder
            if (tries[i][j] == '\0') {
                mvaddch(GAMEBOARD_Y_OFFSET + i - menu_offset_modifer() + 5, TERM_MID_COLS - word_length + (j * 2), '_');
                continue;
            }

            // Letters
            if (patterns[i][j] == MISPLACED)
                attron(COLOR_PAIR(ORANGE_PAIR));
            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]);
            attroff(COLOR_PAIR(GREEN_PAIR));
            attroff(COLOR_PAIR(ORANGE_PAIR));
        }
    }

    if (mode == VERSUS) {
        move(GAMEBOARD_Y_OFFSET, TERM_MID_COLS - word_length);
        printw("Turn: %d/%d", current_turn, VERSUS_TURN_COUNT);

        if (!p2_turn)
            attron(COLOR_PAIR(GREEN_PAIR));
        move(GAMEBOARD_Y_OFFSET - 2 - menu_offset_modifer() + 5, TERM_MID_COLS - word_length - 10);
        printw("Score P1: %d", scores[0]);

        attroff(COLOR_PAIR(GREEN_PAIR));

        if (p2_turn)
            attron(COLOR_PAIR(GREEN_PAIR));
        move(GAMEBOARD_Y_OFFSET - 2 - menu_offset_modifer() + 5, TERM_MID_COLS - word_length + 8);
        printw("Score P2: %d", scores[1]); 

        attroff(COLOR_PAIR(GREEN_PAIR));
    }
    else {
        move(GAMEBOARD_Y_OFFSET - 2 - menu_offset_modifer() + 5, TERM_MID_COLS - word_length);
        printw("Score: %d", scores[0]);
    }

    refresh();
}

void show_winner(bool p2_turn, int* scores) {
    move(GAMEBOARD_Y_OFFSET + 15, TERM_MID_COLS - 13);
    printw("Winner: %s with %d points !", p2_turn ? "P2" : "P1", p2_turn ? scores[1] : scores[0]);
}