From: Harishankar Date: Sat, 9 May 2020 15:06:31 +0000 (+0530) Subject: Renamed the project to Wordblah from wordblox X-Git-Tag: 0.1a~7 X-Git-Url: https://harishankar.org/repos/?p=wordblah.git;a=commitdiff_plain;h=2aac9fd96329b27dffec6534a042cfbe045f3a11 Renamed the project to Wordblah from wordblox Renamed the project from wordblox to wordblah --- diff --git a/Makefile b/Makefile index 34a60ab..6bd7b90 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ -all: wordblox wordblox_player +all: wordblah wordblah_player -wordblox: wordblox.c wordblox.h constantstrings.h - clang wordblox.c -lgd -lz -lcrypto -o wordblox +wordblah: wordblah.c wordblah.h constantstrings.h + clang wordblah.c -lgd -lz -lcrypto -o wordblah -wordblox_player: wordblox_player.c wordblox.h wordblox_resource.c wordblox.gresource.xml wordblox_player.glade constantstrings.h - glib-compile-resources wordblox.gresource.xml --target wordblox_resource.c --generate-source - clang -rdynamic -lz -lgd -lcrypto -o wordblox_player wordblox_player.c -Wall `pkg-config --cflags --libs gtk+-3.0` +wordblah_player: wordblah_player.c wordblah.h wordblah.gresource.xml wordblah_player.glade constantstrings.h + glib-compile-resources wordblah.gresource.xml --target wordblah_resource.c --generate-source + clang -rdynamic -lz -lgd -lcrypto -o wordblah_player wordblah_player.c -Wall `pkg-config --cflags --libs gtk+-3.0` diff --git a/constantstrings.h b/constantstrings.h index b2a270a..e571501 100644 --- a/constantstrings.h +++ b/constantstrings.h @@ -41,7 +41,7 @@ and is irreversible (y/N): " #define USAGE_LINE_3 "new - create new puzzle with grid \ columns (warning: existing file name may be overwritten)\n" -/* for wordblox_player */ +/* for wordblah_player */ #define ERROR_ICON "Unable to load icon!" #define ERROR_WINDOW "Error loading Window Resource!" #define OPEN_FILE "Open File" @@ -52,8 +52,8 @@ columns (warning: existing file name may be overwritten)\n" /* about box info */ const char *AUTHOR[] = { "V. Harishankar", NULL}; const char *COPYRIGHT = "Copyright 2020 V.Harishankar"; -const char *COMMENTS = "A player for wordblox (crossword puzzles)"; -const char *PROGRAM_NAME = "Wordblox Player"; +const char *COMMENTS = "A player for wordblah (crossword puzzles)"; +const char *PROGRAM_NAME = "Wordblah Player"; const char *WEBSITE = "http://harishankar.org/"; const char *WEBSITE_LABEL = "Author's HomePage"; diff --git a/wordblah.c b/wordblah.c new file mode 100644 index 0000000..2dbc366 --- /dev/null +++ b/wordblah.c @@ -0,0 +1,438 @@ +#include +#include +#include +#include +#include +#include + +#include "wordblah.h" +#include "constantstrings.h" + +/* export the clues to a text file */ +void do_export_clues (Puzzle *p) +{ + if (p->grid_frozen == false) + { + printf (UNFROZEN_GRID); + char ch = getchar (); + return; + } + char fname[256]; + printf (INPUT_FILE); + fgets (fname, 256, stdin); + if (strlen(fname) == 1) + return; + char *filename = strtok (fname, "\n"); + + export_clues (p, filename); + printf (FILE_SAVED); + char ch = getchar (); +} + +/* export the puzzle to a png file */ +void do_export_puzzle (Puzzle *p) +{ + if (p->grid_frozen == false) + { + printf (UNFROZEN_GRID); + char ch = getchar (); + return; + } + char fname[256]; + printf (INPUT_FILE); + fgets (fname, 256, stdin); + if (strlen (fname) == 1) + return; + char* filename = strtok (fname, "\n"); + + printf (INPUT_EXPORT_ANSWERS); + char ans[3]; + fgets (ans, 3, stdin); + bool solution; + solution = (toupper (ans[0]) == 'Y') ? true : false; + + export_grid_image (p, filename, solution); + printf (FILE_SAVED); + char ch = getchar (); +} + +/* reset the grid */ +void do_reset_puzzle (Puzzle *p) +{ + int grid_size = p->grid_size; + printf (INPUT_CONFIRM_RESET); + char conf[3]; + fgets (conf, 3, stdin); + if (toupper (conf[0]) == 'Y') + init_puzzle (p, grid_size); + + print_puzzle (p); + char ch = getchar (); +} + +/* set the solution password for the puzzle */ +void do_set_solution_password (Puzzle *p) +{ + char* password; + password = getpass (INPUT_PASSWORD); + /* if empty reset the password to nothing */ + if (strlen(password) == 0) + { + set_solution_password (p, "\0"); + printf (SOLUTION_PASSWORD_RESET); + char ch = getchar (); + } + /* set the password */ + else + { + set_solution_password (p, (const char* )password); + printf (PASSWORD_SET); + char ch = getchar (); + } +} +/* set the master (editing) password for the puzzle */ +void do_set_master_password (Puzzle *p) +{ + char* password; + password = getpass (INPUT_PASSWORD); + /* if empty reset the password to nothing */ + if (strlen(password) == 0) + { + set_master_password (p, "\0"); + printf (MASTER_PASSWORD_RESET); + char ch = getchar (); + } + /* set the password */ + else + { + set_master_password (p, (const char* )password); + printf (PASSWORD_SET); + char ch = getchar (); + } +} + +/* set clue for a word - only for frozen grid */ +void do_set_clue_word (Puzzle *p, enum ORIENTATION orient) +{ + print_puzzle (p); + if (p->grid_frozen == false) + { + printf (UNFROZEN_GRID); + char ch = getchar (); + return; + } + int index; + String clue; + printf (INPUT_INDEX); + index = get_num (); + printf (INPUT_CLUE); + fgets (clue, MAX_CLUE_LENGTH, stdin); + if (strlen (clue) == 1) + return; + char* cl = strtok (clue, "\n"); + + bool res; + res = set_clue (p, cl, index, orient); + + if (res == false) + { + printf (NO_WORD_INDEX); + char ch = getchar (); + } +} + +/* clear a cell in the grid */ +void do_clear_cell (Puzzle *p) +{ + print_puzzle (p); + if (p->grid_frozen == true) + { + printf (FROZEN_GRID); + char ch = getchar (); + return; + } + int row, col; + printf (INPUT_ROW); + row = get_num (); + printf (INPUT_COL); + col = get_num (); + if (row >= p->grid_size || col >= p->grid_size) + { + printf (EXCEED_GRID_SIZE); + char ch = getchar (); + return; + } + p->chars[row][col] = ' '; + print_puzzle (p); + + char ch = getchar (); +} + +/* add a down word to the grid */ +void do_add_down_word (Puzzle *p) +{ + print_puzzle (p); + if (p->grid_frozen == true) + { + printf (FROZEN_GRID); + char ch = getchar (); + return; + } + char wd[MAX_PUZZLE_SIZE]; + int row, col; + printf (INPUT_WORD); + fgets (wd, MAX_PUZZLE_SIZE, stdin); + char *word = is_valid_word (wd); + if (word == NULL) + { + printf (INVALID_WORD); + char ch = getchar (); + return; + } + printf (INPUT_ROW); + row = get_num (); + printf (INPUT_COL); + col = get_num (); + if (row >= p->grid_size || col >= p->grid_size) + { + printf (EXCEED_GRID_SIZE); + char ch = getchar (); + return; + } + if (strlen (word) > (p->grid_size - row)) + { + printf (WORD_TOO_LONG); + char ch = getchar (); + return; + } + + for (int i = row; i < row + strlen(word); i ++) + p->chars[i][col] = toupper(word[i - row]); + + print_puzzle (p); + char ch = getchar (); +} + +/* add an across word to the grid */ +void do_add_across_word (Puzzle *p) +{ + print_puzzle (p); + if (p->grid_frozen == true) + { + printf (FROZEN_GRID); + char ch = getchar (); + return; + } + char wd[MAX_PUZZLE_SIZE]; + int row, col; + printf (INPUT_WORD); + fgets (wd, MAX_PUZZLE_SIZE, stdin); + char *word = is_valid_word (wd); + if (word == NULL) + { + printf (INVALID_WORD); + char ch = getchar (); + return; + } + printf (INPUT_ROW); + row = get_num (); + printf (INPUT_COL); + col = get_num (); + if (row >= p->grid_size || col >= p->grid_size) + { + printf (EXCEED_GRID_SIZE); + char ch = getchar (); + return; + } + + if (strlen (word) > (p->grid_size - col)) + { + printf (WORD_TOO_LONG); + char ch = getchar (); + return; + } + + for (int i = col; i < col+strlen (word); i ++) + p->chars[row][i] = toupper(word[i - col]); + + print_puzzle (p); + char ch = getchar (); +} +/* confirm exit */ +bool do_confirm_exit () +{ + printf (INPUT_CONFIRM_EXIT); + char res[3]; + fgets (res, 3, stdin); + if (toupper(res[0]) == 'Y') + return true; + else + return false; +} + +/* main loop for the puzzle editor */ +void puzzle_editor_loop (Puzzle *p, const char *filename) +{ + bool loop = true; + while (loop) + { + char puzzle_title[60]; + sprintf (puzzle_title, "%s - %s", PUZZLE_MENU_TITLE, filename); + print_menu (WHITE, BLUE, puzzle_title, PUZZLE_EDIT_MENU, 15, 50); + printf (INPUT_CHOICE); + int ch = get_num (); + switch (ch) + { + case 1: print_puzzle (p); + char ch = getchar (); + break; + case 2: do_add_across_word (p); + break; + case 3: do_add_down_word (p); + break; + case 4: do_clear_cell (p); + break; + case 5: freeze_puzzle (p); + print_puzzle (p); + ch = getchar (); + break; + case 6: unfreeze_puzzle (p); + print_puzzle (p); + ch = getchar (); + break; + case 7: do_set_clue_word (p, ACROSS); + break; + case 8: do_set_clue_word (p, DOWN); + break; + case 9: save_puzzle (p, filename); + printf ("%s\n",FILE_SAVED); + ch = getchar (); + break; + case 10: do_set_master_password (p); + break; + case 11: do_set_solution_password (p); + break; + case 12: do_reset_puzzle (p); + break; + case 13: do_export_puzzle (p); + break; + case 14: do_export_clues (p); + break; + case 15: loop = ! do_confirm_exit (); + break; + } + } +} + +/* open an existing puzzle */ +void do_open_puzzle (const char *filename) +{ + Puzzle p; + /* if no filename is provided, get it from command line */ + if (filename == NULL) + { + printf (INPUT_FILE); + char fname[256]; + fgets(fname, 256, stdin); + if (strlen (fname) == 1) + return; + filename = strtok (fname, "\n"); + } + + p = load_puzzle (filename); + + if (strcmp (p.hashed_master_password, "\0") != 0) + { + char *passwd; + passwd = getpass (INPUT_PASSWORD); + if (strlen (passwd) == 0) + return; + + if (verify_master_password (&p, (const char*) passwd)) + puzzle_editor_loop (&p, filename); + else + { + printf (WRONG_PASSWORD); + char ch = getchar (); + } + } + else + puzzle_editor_loop (&p, filename); +} + +/* create a new blank puzzle */ +void do_new_puzzle (char *filename, int size) +{ + Puzzle p; + /* if filename is not provided get it from command line */ + if (filename == NULL) + { + printf (INPUT_FILE); + char fname[256]; + fgets (fname, 256, stdin); + if (strlen (fname) == 1) + return; + filename = strtok (fname, "\n"); + } + /* if no size is specified get it from command line */ + if (size == -1) + { + printf (INPUT_GRID_SIZE); + size = get_num (); + } + + if (size > MAX_PUZZLE_SIZE) + { + printf (EXCEED_MAX_GRID_SIZE); + char c = getchar (); + } + else + { + init_puzzle (&p, size); + puzzle_editor_loop (&p, filename); + } +} + +/* The main loop of the program */ +int main_loop () +{ + /* Print the main menu */ + while (1) + { + print_menu (WHITE, BLUE, MAIN_MENU_TITLE, MAIN_MENU, 3, 50); + printf (INPUT_CHOICE); + int ch = get_num (); + switch (ch) + { + case 1: do_new_puzzle (NULL, -1); + break; + case 2: do_open_puzzle (NULL); + break; + case 3: exit (0); + } + } +} + +int main (int argc, char* argv[]) +{ + if (argc >= 2) + { + Puzzle p; + switch (argc) + { + case 2 : do_open_puzzle (argv[1]); + break; + case 4 : if (strcmp (argv[2], "new") == 0) + { + int grid_size = atoi (argv[3]); + do_new_puzzle (argv[1], grid_size); + break; + } + default: fprintf (stderr, USAGE_LINE_1, argv[0]); + fprintf (stderr, USAGE_LINE_2); + fprintf (stderr, USAGE_LINE_3); + exit (3); + } + } + return (main_loop ()); +} diff --git a/wordblah.gresource.xml b/wordblah.gresource.xml new file mode 100644 index 0000000..b027cc0 --- /dev/null +++ b/wordblah.gresource.xml @@ -0,0 +1,8 @@ + + + + + wordblah_player.glade + wordblah.svg + + diff --git a/wordblah.h b/wordblah.h new file mode 100644 index 0000000..328148d --- /dev/null +++ b/wordblah.h @@ -0,0 +1,1032 @@ +#ifndef __WORDBLAH_H +#define __WORDBLAH_H +#define _XOPEN_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "constantstrings.h" + +#define MAX_PUZZLE_SIZE 25 +#define MAX_CLUE_LENGTH 150 +#define GRID_PIXELS 37 + +/* Enum to define terminal colours */ +enum COLOR { + BLACK = 0, + RED= 1, + GREEN=2, + YELLOW=3, + BLUE=4, + MAGENTA=5, + CYAN=6, + WHITE=7 +}; + +enum ATTR { + NORMAL = 23, + BOLD=1 +}; + +enum ORIENTATION { + ACROSS=1, + DOWN=2 +}; + +/* for use with the player */ +enum DIRECTION { + DIR_FORWARD = 1, + DIR_BACK = -1 +}; + +typedef char String[MAX_CLUE_LENGTH]; + +/* The main puzzle struct type */ +typedef struct { + char chars[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE]; + int start_across_word[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE]; + int start_down_word[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE]; + String clue_across[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE]; + String clue_down[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE]; + int grid_size; + bool grid_frozen; + char hashed_master_password[256]; + char hashed_solution_password[256]; +} Puzzle; + +/* The player data struct type - for the player app */ +typedef struct { + Puzzle puzzle; + char filename[65535]; + bool is_loaded; + char char_ans[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE]; + int cur_row; + int cur_col; + bool solution_revealed; + enum ORIENTATION current_movement; +} MainPlayerData; + +/* compute the hash of a password */ +void digest_message(const unsigned char *message, + size_t message_len, unsigned char **digest, unsigned int *digest_len) +{ + EVP_MD_CTX *mdctx; + + if((mdctx = EVP_MD_CTX_new()) == NULL) + goto err; + + if(1 != EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL)) + goto err; + + if(1 != EVP_DigestUpdate(mdctx, message, message_len)) + goto err; + + if((*digest = (unsigned char *) + OPENSSL_malloc(EVP_MD_size(EVP_sha256()))) == NULL) + goto err; + + if(1 != EVP_DigestFinal_ex(mdctx, *digest, digest_len)) + goto err; + + EVP_MD_CTX_free(mdctx); + return; +err: + EVP_MD_CTX_free(mdctx); + ERR_print_errors_fp(stderr); + exit (2); +} + +/* convert the hashed binary password to hexadecimal representation and + free the hashed binary password */ +void to_hexadecimal (char *hex, unsigned char *binary_pwd, unsigned int len) +{ + char buf[3]; + /* keep reference to beginning of the hashed password */ + unsigned char *binary_pw_begin = binary_pwd; + for (int i = 0; i < len; i ++) + { + sprintf (buf, "%02x", (*binary_pwd)&0xff); + strcat (hex, buf); + binary_pwd ++; + } + /* free the hashed password */ + OPENSSL_free (binary_pw_begin); +} + +/* get a number from the user */ +int get_num () +{ + char s[5]; + fgets (s, 5, stdin); + int n = atoi (s); + return n; +} + +/* verify solution password */ +bool verify_solution_password (Puzzle *p, const char* password) +{ + /* no password set */ + if (strcmp (p->hashed_solution_password, "\0") == 0) + return true; + + /* hash the user input password and compare it with the stored password */ + unsigned char* hashed_sol_password; + unsigned int len; + digest_message ((const unsigned char *)password, strlen(password), + &hashed_sol_password, &len); + char hashed_hex_pwd[256] = { (char) NULL }; + to_hexadecimal (hashed_hex_pwd, hashed_sol_password, len); + + if (strcmp (p->hashed_solution_password, hashed_hex_pwd) == 0) + return true; + + return false; +} + + +/* verify master password */ +bool verify_master_password (Puzzle *p, const char* password) +{ + /* no password set */ + if (strcmp (p->hashed_master_password, "\0") == 0) + return true; + + /* hash the user input password and compare it with the stored password */ + unsigned char* hashed_mas_password; + unsigned int len; + digest_message ((const unsigned char *)password, strlen(password), + &hashed_mas_password, &len); + char hashed_hex_pwd[256] = { (char) NULL }; + to_hexadecimal (hashed_hex_pwd, hashed_mas_password, len); + + if (strcmp (p->hashed_master_password, hashed_hex_pwd) == 0) + return true; + + return false; +} + +/* Set or reset solution password for puzzle */ +void set_solution_password (Puzzle *p, const char *password) +{ + /* if it is a null string, reset the password */ + if (strcmp (password, "\0") == 0) + strcpy (p->hashed_solution_password, "\0"); + else + { + + unsigned char* hashedpwd; + unsigned int len; + digest_message ((const unsigned char *)password, strlen(password), + &hashedpwd, &len); + /* the hashedpwd contains binary data - we will convert it to + hexadecimal data and store in file */ + + to_hexadecimal (p->hashed_solution_password, hashedpwd, len); + } +} + +/* Set or reset master password for puzzle */ +void set_master_password (Puzzle *p, const char *password) +{ + /* if it is a null string, reset the password */ + if (strcmp (password, "\0") == 0) + strcpy (p->hashed_master_password, "\0"); + else + { + + unsigned char* hashedpwd; + unsigned int len; + digest_message ((const unsigned char *)password, strlen(password), + &hashedpwd, &len); + /* the hashedpwd contains binary data - we will convert it to + hexadecimal data and store in file */ + + to_hexadecimal (p->hashed_master_password, hashedpwd, len); + } +} + +/* Output the clues to text file */ +void export_clues (Puzzle *p, const char *filename) +{ + FILE *outfile = fopen (filename, "w"); + if (outfile == NULL) + { + fprintf (stderr, "%s\n", ERROR_WRITING_FILE); + exit (1); + } + /* first the across clues */ + fprintf (outfile, "ACROSS CLUES\n"); + for (int i = 0; i < p->grid_size; i ++) + { + for (int j = 0; j < p->grid_size; j ++) + { + if (p->start_across_word[i][j] != -1) + fprintf (outfile, "%d - %s\n", p->start_across_word[i][j], + p->clue_across[i][j]); + } + } + /* now the down clues */ + fprintf (outfile, "DOWN CLUES\n"); + for (int i = 0; i < p->grid_size; i ++) + { + for (int j = 0; j < p->grid_size; j ++) + { + if (p->start_down_word[i][j] != -1) + fprintf (outfile, "%d - %s\n", p->start_down_word[i][j], + p->clue_down[i][j]); + } + } + fclose (outfile); +} + +/* Output the grid to image - if answerkey is true export filled grid */ +void export_grid_image (Puzzle *p, const char *filename, bool answerkey) +{ + int img_size = p->grid_size * GRID_PIXELS; + FILE * outfile = fopen (filename, "wb"); + if (outfile == NULL) + { + fprintf (stderr, "%s\n", ERROR_WRITING_FILE); + exit (1); + } + + gdImagePtr img = gdImageCreate (img_size, img_size); + gdImageColorAllocate (img, 255,255,255); + int black = gdImageColorAllocate (img, 0, 0, 0); + int blue = gdImageColorAllocate (img, 0, 0, 216); + gdFontPtr sm_fnt = gdFontGetMediumBold (); + gdFontPtr lg_fnt = gdFontGetGiant (); + + for (int i = 0; i < p->grid_size; i ++) + { + for (int j = 0; j < p->grid_size; j++) + { + /* if it is a block, draw the black square */ + if (p->chars[i][j] == '#') + gdImageFilledRectangle (img, j*GRID_PIXELS, i*GRID_PIXELS, + j*GRID_PIXELS+GRID_PIXELS, + i*GRID_PIXELS+GRID_PIXELS,black); + else + { + /* draw a regular square */ + gdImageRectangle (img, j*GRID_PIXELS, i*GRID_PIXELS, + j*GRID_PIXELS+GRID_PIXELS, + i*GRID_PIXELS+GRID_PIXELS, black); + + /* print the numers, if it is either start across word or + a down word */ + if (p->start_across_word[i][j] != -1 || + p->start_down_word[i][j] != -1) + { + if (p->start_across_word[i][j] != -1) + { + char str[5]; + sprintf (str, "%d", p->start_across_word[i][j]); + gdImageString (img, sm_fnt, j*GRID_PIXELS+2, + i*GRID_PIXELS+2, + (unsigned char *)str, blue); + } + else + { + char str[5]; + sprintf (str, "%d", p->start_down_word[i][j]); + gdImageString (img, sm_fnt, j*GRID_PIXELS+2, + i*GRID_PIXELS+2, + (unsigned char *)str, blue); + } + } + /* if answerkey is true, draw the character in the cell */ + if (answerkey) + { + gdImageChar (img, lg_fnt, j*GRID_PIXELS+15, + i*GRID_PIXELS+10, p->chars[i][j], black); + } + } + } + } + + gdImagePng (img, outfile); + gdImageDestroy (img); + fclose (outfile); +} + +/* Set the terminal colour */ +void set_color (enum COLOR fg, enum COLOR bg, enum ATTR at) { + printf ("\x1B[%d;%d;%dm", fg+30, bg+40, at); +} + +/* Reset the terminal colour */ +void reset_color () { + printf ("\x1B[0m"); +} + +/* check if the prev row has a block or not */ +bool prev_row_block (Puzzle *p, int r, int c) +{ + if (r == 0) + return true; + if (p->chars[r-1][c] == '#') + return true; + return false; +} + +/* check if the next row has a block or not */ +bool next_row_block (Puzzle *p, int r, int c) +{ + if (r == p->grid_size-1) + return true; + if (p->chars[r+1][c] == '#') + return true; + return false; +} + +/* check if the prev col has a block or not */ +bool prev_col_block (Puzzle *p, int r, int c) +{ + if (c == 0) + return true; + if (p->chars[r][c-1] == '#') + return true; + return false; +} + +/* check if the next col has a block or not */ +bool next_col_block (Puzzle *p, int r, int c) +{ + if (c == p->grid_size - 1) + return true; + if (p->chars[r][c+1] == '#') + return true; + return false; +} + +/* check if previous row is blank or not */ +bool prev_row_blank (Puzzle *p, int r, int c) +{ + if (r == 0) return true; + if (p->chars[r-1][c] == ' ' || p->chars[r-1][c] == '#') return true; + return false; +} +/* check if next row is blank or not */ +bool next_row_blank (Puzzle *p, int r, int c) +{ + if (r == p->grid_size - 1) return true; + if (p->chars[r+1][c] == ' ' || p->chars[r+1][c] == '#') return true; + return false; +} +/* check if previous col is blank or not */ +bool prev_col_blank (Puzzle *p, int r, int c) +{ + if (c == 0) return true; + if (p->chars[r][c-1] == ' ' || p->chars[r][c-1] == '#') return true; + return false; +} +/* check if the next col is blank or not */ +bool next_col_blank (Puzzle *p, int r, int c) +{ + if (c == p->grid_size -1) return true; + if (p->chars[r][c+1] == ' ' || p->chars[r][c+1] == '#') return true; + return false; +} + +/* set the current row/col to the beginning of word index (across or down) */ +void set_selection_to_word_start (MainPlayerData *app_data, + enum ORIENTATION orient, int word_index) +{ + for (int i = 0; i < app_data->puzzle.grid_size; i ++) + { + for (int j = 0; j < app_data->puzzle.grid_size; j ++) + { + if (orient == ACROSS && + app_data->puzzle.start_across_word[i][j] == word_index) + { + app_data->current_movement = ACROSS; + app_data->cur_row = i; + app_data->cur_col = j; + break; + } + else if (orient == DOWN && + app_data->puzzle.start_down_word[i][j] == word_index) + { + app_data->current_movement = DOWN; + app_data->cur_row = i; + app_data->cur_col = j; + break; + } + } + } +} + +/* unfreeze the grid - make editing possible to change words */ +void unfreeze_puzzle (Puzzle *p) +{ + for (int i = 0; i < p->grid_size; i ++) + { + for (int j = 0; j < p->grid_size; j ++) + { + if (p->chars[i][j] == '#') + p->chars[i][j] = ' '; + + p->start_across_word[i][j] = -1; + p->start_down_word[i][j] = -1; + } + } + p->grid_frozen = false; +} + +/* freeze the grid - make editing impossible because it finalizes the + across and down words in the grid */ +void freeze_puzzle (Puzzle *p) +{ + int word_num = 1; + bool across_word_start, down_word_start; + for (int i = 0; i < p->grid_size; i ++) + { + for (int j = 0; j < p->grid_size; j++) + { + across_word_start = false; + down_word_start = false; + /* if it is a blank cell - cover it with a block */ + if (p->chars[i][j] == ' ' || p->chars[i][j] == '#') + p->chars[i][j] = '#'; + /* it is not a blank cell - check all possibilities */ + else + { + bool prev_row = prev_row_blank (p, i, j); + bool next_row = next_row_blank (p, i, j); + bool prev_col = prev_col_blank (p, i, j); + bool next_col = next_col_blank (p, i, j); + if (prev_row && ! next_row) + down_word_start = true; + if (prev_col && ! next_col) + across_word_start = true; + } + + if (across_word_start == true) + p->start_across_word[i][j] = word_num; + else + p->start_across_word[i][j] = -1; + if (down_word_start == true) + p->start_down_word[i][j] = word_num; + else + p->start_down_word[i][j] = -1; + if (across_word_start == true || down_word_start == true) + word_num ++; + } + } + p->grid_frozen = true; +} + +/* reset the entire grid */ +void init_puzzle (Puzzle *p, int grid_size) +{ + p->grid_size = grid_size; + p->grid_frozen = false; + for (int i = 0; i < p->grid_size; i ++) + { + for (int j = 0; j < p->grid_size; j ++) + { + p->chars[i][j] = ' '; + p->start_across_word[i][j] = -1; + p->start_down_word[i][j] = -1; + strcpy (p->clue_across[i][j], ""); + strcpy (p->clue_down[i][j], ""); + } + } + strcpy (p->hashed_master_password, "\0"); + strcpy (p->hashed_solution_password, "\0"); + +} + +/* save the puzzle to a file */ +void save_puzzle (Puzzle *puzzle, const char* file) +{ + FILE *outfile; + /* First output the uncompressed contents to a temp file */ + outfile = tmpfile (); + if (outfile == NULL) + { + fprintf (stderr, "%s\n", ERROR_WRITING_FILE); + exit (1); + } + /* grid size is the first field */ + fprintf (outfile, "%d\n", puzzle->grid_size); + /* whether grid is frozen or not */ + fprintf (outfile, "%d\n", puzzle->grid_frozen); + /* the hashed password */ + fprintf (outfile, "%s\n", puzzle->hashed_master_password); + /* the hashed_solution_password */ + fprintf (outfile, "%s\n", puzzle->hashed_solution_password); + + /* First output the grid characters columns/rows */ + for (int i = 0; i < puzzle->grid_size; i ++) + { + for (int j = 0; j < puzzle->grid_size; j ++) + fprintf (outfile, "%c", puzzle->chars[i][j]); + fprintf (outfile, "\n"); + } + + /* Next output the start across/down numbers */ + for (int i = 0; i < puzzle->grid_size; i ++) + { + for (int j = 0; j < puzzle->grid_size; j++) + { + fprintf (outfile, "%d ", puzzle->start_across_word[i][j]); + fprintf (outfile, "%d ", puzzle->start_down_word[i][j]); + } + fprintf (outfile, "\n"); + } + + /* Output the across clues */ + fprintf (outfile, "ACROSS\n"); + /* Search the grid for across words */ + for (int i = 0; i < puzzle->grid_size; i ++) + { + for (int j = 0; j < puzzle->grid_size; j++) + { + /* if it is an across word, then put the word index followed by + tab character (as separator) and the clue */ + if (puzzle->start_across_word[i][j] != -1) + fprintf (outfile, "%d\t%s\n", puzzle->start_across_word[i][j], + puzzle->clue_across[i][j]); + } + } + + /* Output the down clues */ + fprintf (outfile, "DOWN\n"); + /* Search the grid for down words */ + for (int i = 0; i < puzzle->grid_size; i ++) + { + for (int j = 0; j < puzzle->grid_size; j++) + { + /* same as across word, put the word index followed by the tab + character and then the clue */ + if (puzzle->start_down_word[i][j] != -1) + fprintf (outfile, "%d\t%s\n", puzzle->start_down_word[i][j], + puzzle->clue_down[i][j]); + } + } + + /* Flush the buffer and rewind to beginning - to read and save into + gzip compressed file */ + fflush (outfile); + fseek (outfile, 0, 0); + + /* now compress the file and save it to destination file */ + gzFile outdestfile = gzopen (file, "wb"); + if (outdestfile == NULL) + { + fprintf (stderr, "%s\n", ERROR_WRITING_FILE); + fclose (outfile); + exit (1); + } + char buf[128]; + int num = fread (buf, sizeof(char), sizeof(char)*128, outfile); + while (num > 0) + { + int res = gzwrite (outdestfile, buf, num*sizeof(char) ); + if (res == 0) + { + fprintf (stderr, "%s %s\n", ERROR_WRITING_FILE, COMPRESSED); + fclose (outfile); + exit (1); + } + num = fread (buf, sizeof(char), sizeof(char)*128, outfile); + } + gzclose (outdestfile); + fclose (outfile); + +} + +/* read the puzzle from a file */ +Puzzle load_puzzle (const char* file) +{ + /* First open the GZip file */ + gzFile insourcefile = gzopen (file, "rb"); + if (insourcefile == NULL) + { + fprintf (stderr, "%s %s\n", ERROR_READING_FILE, COMPRESSED); + exit (1); + } + /* Open a temporary file to uncompress the contents */ + FILE *infile = tmpfile (); + if (infile == NULL) + { + fprintf (stderr, "%s\n", ERROR_READING_FILE); + exit (1); + } + /* Put the uncompressed content to the temp file */ + char buf[128]; + int num = 0; + num = gzread (insourcefile, buf, 128); + while (num > 0) + { + int res = fwrite (buf, 1, num, infile); + if (res == 0) + { + fprintf (stderr, "%s\n", ERROR_READING_FILE); + fclose (infile); + gzclose (insourcefile); + exit (1); + } + num = gzread (insourcefile, buf, 128); + } + /* Close the gzip file */ + gzclose (insourcefile); + /* Flush the temp file buffer and rewind to beginning */ + fflush (infile); + fseek (infile, 0, 0); + + /* Read the temporary file contents to the structure Puzzle */ + Puzzle p; + char line[MAX_CLUE_LENGTH+10]; + fgets (line, MAX_CLUE_LENGTH + 10, infile); + p.grid_size = atoi (line); + fgets (line, MAX_CLUE_LENGTH + 10, infile); + p.grid_frozen = atoi (line) == 0 ? false : true ; + fgets (line, MAX_CLUE_LENGTH + 10, infile); + if (strlen (line) != 1) + strcpy (p.hashed_master_password, strtok (line, "\n")); + else + strcpy (p.hashed_master_password, "\0"); + fgets (line, MAX_CLUE_LENGTH + 10, infile); + if (strlen (line) != 1) + strcpy (p.hashed_solution_password, strtok (line, "\n")); + else + strcpy (p.hashed_solution_password, "\0"); + + /* read each character of the grid */ + for (int i = 0; i < p.grid_size; i ++ ) + { + fgets (line, MAX_CLUE_LENGTH + 10, infile); + for (int j = 0; j < p.grid_size; j ++) + p.chars[i][j] = line[j]; + } + /* read the word numbers */ + for (int i = 0; i < p.grid_size; i ++) + { + fgets (line, MAX_CLUE_LENGTH + 10, infile); + char *token = strtok (line, " "); + for (int j = 0; j < p.grid_size; j ++) + { + if (token != NULL) + p.start_across_word[i][j] = atoi (token); + token = strtok (NULL, " "); + if (token != NULL) + p.start_down_word[i][j] = atoi (token); + token = strtok (NULL, " "); + } + } + /* read the clues */ + fgets (line, MAX_CLUE_LENGTH + 10, infile); + + /* across clues */ + char clues[100][MAX_CLUE_LENGTH]; + int word_num[100]; + int c = 0; + /* first read the across clues from file */ + while (1) + { + fgets (line, MAX_CLUE_LENGTH + 10, infile); + /* if reached the end of across clues */ + if (strcmp (line, "DOWN\n") == 0) + break; + word_num[c] = atoi (strtok (line, "\t")); + char *cl = strtok (NULL, "\n"); + if (cl != NULL) + strcpy (clues[c], cl); + else + strcpy (clues[c], "\0"); + c++; + } + /* set the clue to the correct cell in grid */ + for (int i = 0; i < p.grid_size; i ++) + { + for (int j = 0; j < p.grid_size; j ++) + { + for (int r = 0; r < c; r ++) + if (p.start_across_word[i][j] == word_num[r]) + strcpy (p.clue_across[i][j], clues[r]); + } + } + + /* down clues */ + c = 0; + while (fgets (line, MAX_CLUE_LENGTH + 10, infile)) + { + word_num[c] = atoi (strtok (line, "\t")); + char* cl = strtok (NULL, "\n"); + if (cl != NULL) + strcpy (clues[c], cl); + else + strcpy (clues[c], "\0"); + c++; + } + for (int i = 0; i < p.grid_size; i ++) + { + for (int j = 0; j < p.grid_size; j ++) + { + for (int r = 0; r < c; r ++) + if (p.start_down_word[i][j] == word_num[r]) + strcpy (p.clue_down[i][j], clues[r]); + } + } + + fclose (infile); + return p; +} + +/* display the puzzle */ +void print_puzzle (Puzzle *p) +{ + printf ("\n"); + set_color (WHITE, CYAN, NORMAL); + printf (" "); + for (int i = 0; i < p->grid_size; i ++) + printf ("%3d", i); + reset_color (); + printf("\n"); + for (int i = 0; i < p->grid_size; i ++) + { + set_color (WHITE, CYAN, NORMAL); + printf ("%3d ", i); + for (int j = 0; j < p->grid_size; j ++) + { + if (p->chars[i][j] == '#') { + set_color (WHITE, BLACK, NORMAL); + printf (" "); + } + else + { + if (p->start_across_word[i][j] != -1 || + p->start_down_word[i][j] != -1) + { + set_color (BLUE, WHITE, NORMAL); + if (p->start_across_word[i][j] != -1) + printf ("%-2d", p->start_across_word[i][j]); + else + printf ("%-2d", p->start_down_word[i][j]); + } + else + { + set_color (BLACK, WHITE,NORMAL); + printf (" "); + } + + set_color (BLACK, WHITE, BOLD); + printf ("%c", p->chars[i][j]); + } + reset_color (); + } + printf ("\n"); + } + /* print the clues if set */ + if (p->grid_frozen == true) + { + printf ("\x1B[1mACROSS - CLUES\x1B[0m\n"); + for (int i = 0; i < p->grid_size; i ++) + { + for (int j = 0; j < p->grid_size; j ++) + { + if (p->start_across_word[i][j] != -1) + { + printf ("%d - %s; ", p->start_across_word[i][j], + p->clue_across[i][j]); + } + } + } + printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n"); + for (int i = 0; i < p->grid_size; i ++) + { + for (int j = 0; j < p->grid_size; j ++) + { + if (p->start_down_word[i][j] != -1) + { + printf ("%d - %s; ", p->start_down_word[i][j], + p->clue_down[i][j]); + } + } + } + printf ("\n"); + } +} + +/* function to check if a word is valid or not */ +char* is_valid_word (char *word) +{ + if (word == NULL || strlen(word) == 0) + return NULL; + for (int i = 0; i < strlen (word) - 1; i ++) + if (! isalpha (word[i])) + return NULL; + + return strtok (word, "\n"); +} + + +/* function to set a clue for an across word */ +bool set_clue (Puzzle *p, String clue, int index, enum ORIENTATION order) +{ + for (int i = 0; i < p->grid_size; i ++) + { + for (int j = 0; j < p->grid_size; j ++) + { + if (order == ACROSS) + { + if (p->start_across_word[i][j] == index) + { + strcpy (p->clue_across[i][j], clue); + return true; + } + } + else if (order == DOWN) + { + if (p->start_down_word[i][j] == index) + { + strcpy (p->clue_down[i][j], clue); + return true; + } + } + } + } + return false; +} + +/* function to print a menu */ +void print_menu (enum COLOR fg, enum COLOR bg, const char* title, + char **items, int num_items, int padding) +{ + /* clear screen */ + printf ("\e[1;1H\e[2J"); + set_color (fg, bg, NORMAL); + printf ("\u2554"); + for (int i = 0; i < padding; i ++) + printf ("\u2550"); + printf ("\u2557"); + reset_color (); printf ("\n"); + set_color (fg, bg, NORMAL); + printf ("\u2551"); + set_color (fg, bg, BOLD); + printf ("%-*s", padding, title); + reset_color (); + set_color (fg, bg, NORMAL); + printf ("\u2551"); + reset_color (); printf ("\n"); + set_color (fg, bg, NORMAL); + printf ("\u2560"); + for (int i = 0; i < padding; i ++) + printf ("\u2550"); + printf ("\u2563"); + reset_color (); printf ("\n"); + for (int i = 0; i < num_items; i ++) + { + set_color (fg, bg, NORMAL); + printf ("\u2551%-*s\u2551", padding, items[i]); + reset_color (); printf ("\n"); + } + set_color (fg, bg, NORMAL); + printf ("\u255A"); + for (int i = 0; i < padding; i ++) + printf ("\u2550"); + printf ("\u255D"); + reset_color (); printf ("\n"); +} + +/* reset the player data, loading from the puzzle file */ +void reset_player_data (MainPlayerData *app_data, const char *filename) +{ + app_data->puzzle = load_puzzle (filename); + + app_data->is_loaded = app_data->puzzle.grid_frozen; + app_data->cur_col = -1; + app_data->cur_row = -1; + app_data->solution_revealed = false; + strcpy (app_data->filename, filename); + /* reset the answer keys */ + for (int i = 0; i < app_data->puzzle.grid_size; i ++) + for (int j = 0; j < app_data->puzzle.grid_size; j ++) + app_data->char_ans[i][j] = ' '; + +} + +/* save the user grid to a file */ +void save_user_data (MainPlayerData *app_data, const char *filename) +{ + FILE *outfile; + outfile = fopen (filename, "wb"); + if (outfile == NULL) + { + fprintf (stderr, ERROR_WRITING_FILE); + return; + } + fprintf (outfile, "%s\n", app_data->filename); + for (int i = 0; i < app_data->puzzle.grid_size; i ++) + { + for (int j = 0; j < app_data->puzzle.grid_size; j ++) + fprintf (outfile, "%c", app_data->char_ans[i][j]); + fprintf (outfile, "\n"); + } + + fclose (outfile); +} + +/* load the user grid from a file */ +void load_user_data (MainPlayerData *app_data, const char *filename) +{ + FILE *infile; + infile = fopen (filename, "rb"); + if (infile == NULL) + { + fprintf (stderr, "%s\n", ERROR_READING_FILE); + return; + } + + char puzzle_file_name[65535]; + fgets (puzzle_file_name, 65535, infile); + reset_player_data (app_data, strtok (puzzle_file_name, "\n")); + + char line[MAX_PUZZLE_SIZE+10]; + for (int i = 0; i < app_data->puzzle.grid_size; i ++) + { + fgets (line, MAX_PUZZLE_SIZE+10, infile); + for (int j = 0; j < app_data->puzzle.grid_size; j ++) + app_data->char_ans[i][j] = line[j]; + + } + fclose (infile); +} + +/* in the player app, move the current selection index left or right */ +void move_current_col (MainPlayerData *app_data, enum DIRECTION dir) +{ + int r = app_data->cur_row; + int c = app_data->cur_col; + if (dir == DIR_FORWARD) + { + c ++; + while (c < app_data->puzzle.grid_size) + { + if (app_data->puzzle.chars[r][c] == '#') + c ++; + else + break; + } + if (c < app_data->puzzle.grid_size) + app_data->cur_col = c; + } + else + { + c --; + while (c >= 0) + { + if (app_data->puzzle.chars[r][c] == '#') + c --; + else + break; + } + if (c >= 0) + app_data->cur_col = c; + } +} + +/* in the player app move the current selection index up or down */ +void move_current_row (MainPlayerData *app_data, enum DIRECTION dir) +{ + int r = app_data->cur_row; + int c = app_data->cur_col; + if (dir == DIR_FORWARD) + { + r ++; + while (r < app_data->puzzle.grid_size) + { + if (app_data->puzzle.chars[r][c] == '#') + r ++; + else + break; + } + if (r < app_data->puzzle.grid_size) + app_data->cur_row = r; + } + else + { + r --; + while (r >= 0) + { + if (app_data->puzzle.chars[r][c] == '#') + r --; + else + break; + } + if (r >= 0) + app_data->cur_row = r; + } +} + +#endif diff --git a/wordblah.svg b/wordblah.svg new file mode 100644 index 0000000..d7e6745 --- /dev/null +++ b/wordblah.svg @@ -0,0 +1,106 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/wordblah_player.c b/wordblah_player.c new file mode 100644 index 0000000..26dab29 --- /dev/null +++ b/wordblah_player.c @@ -0,0 +1,724 @@ +#include + +#include "constantstrings.h" +#include "wordblah_resource.c" +#include "wordblah.h" + +GtkWidget *main_window; +GtkListStore *across_store; +GtkListStore *down_store; + +MainPlayerData app_data; + +/* update the clue items store */ +void update_clue_items () +{ + gtk_list_store_clear (across_store); + gtk_list_store_clear (down_store); + + /* if the puzzle is loaded */ + if (app_data.is_loaded == true) + { + /* iterate through the puzzle grid and gte the clues */ + for (int i = 0; i < app_data.puzzle.grid_size; i ++) + { + for (int j = 0; j < app_data.puzzle.grid_size; j ++) + { + /* if it is the start of an across word */ + if (app_data.puzzle.start_across_word[i][j] != -1) + { + GtkTreeIter iter; + + gtk_list_store_append (across_store, &iter); + gtk_list_store_set (across_store, &iter, 0, + app_data.puzzle.start_across_word[i][j], + 1, app_data.puzzle.clue_across[i][j], + -1); + } + /* if it is the start of a down word */ + if (app_data.puzzle.start_down_word[i][j] != -1) + { + GtkTreeIter iter; + gtk_list_store_append (down_store, &iter); + gtk_list_store_set (down_store, &iter, 0, + app_data.puzzle.start_down_word[i][j], + 1, app_data.puzzle.clue_down[i][j], + -1); + } + } + } + } +} + +/* slot for handling list down selection changed */ +gboolean on_down_list_selection_changed (GtkTreeSelection *sel, + GtkDrawingArea *area) +{ + GtkTreeIter iter; + GtkTreeModel* mod; + + if (gtk_tree_selection_get_selected (sel, &mod, &iter)) + { + guint sel_word_index; + gtk_tree_model_get (mod, &iter, 0, &sel_word_index, -1); + set_selection_to_word_start (&app_data, DOWN, sel_word_index); + } + + gtk_widget_queue_draw_area (GTK_WIDGET(area), 0, 0, + app_data.puzzle.grid_size*GRID_PIXELS+10, + app_data.puzzle.grid_size*GRID_PIXELS+10); + + return FALSE; + +} +/* slot for handling list across selection changed */ +gboolean on_across_list_selection_changed (GtkTreeSelection *sel, + GtkDrawingArea *area) +{ + GtkTreeIter iter; + GtkTreeModel* mod; + + if (gtk_tree_selection_get_selected (sel, &mod, &iter)) + { + guint sel_word_index; + gtk_tree_model_get (mod, &iter, 0, &sel_word_index, -1); + set_selection_to_word_start (&app_data, ACROSS, sel_word_index); + } + + gtk_widget_queue_draw_area (GTK_WIDGET(area), 0, 0, + app_data.puzzle.grid_size*GRID_PIXELS+10, + app_data.puzzle.grid_size*GRID_PIXELS+10); + + return FALSE; + +} + +/* slot for handling mouse button event for puzzle drawing area */ +gboolean on_puzzle_area_button_press_event (GtkWidget *widget, + GdkEventButton *event, gpointer data) +{ + /* if it is solution mode, then don't do anything but reset it to + non solution mode */ + if (app_data.solution_revealed == true) + { + app_data.solution_revealed = false; + gtk_widget_queue_draw_area (widget, 0, 0, + app_data.puzzle.grid_size*GRID_PIXELS+10, + app_data.puzzle.grid_size*GRID_PIXELS+10); + return FALSE; + } + + /* Depending on the type of button handle the movement */ + if (event->type == GDK_BUTTON_PRESS && event->button == 1) + { + int col = (event->x - 5) / GRID_PIXELS; + int row = (event->y - 5) / GRID_PIXELS; + + if (app_data.puzzle.chars[row][col] != '#') + { + if (row < app_data.puzzle.grid_size && + col < app_data.puzzle.grid_size) + { + app_data.cur_row = row; + app_data.cur_col = col; + + /* if it is a start of both across and down word, then + toggle the movement on clicking it */ + if (app_data.puzzle.start_across_word[row][col] != -1 && + app_data.puzzle.start_down_word[row][col] != -1) + { + if (app_data.current_movement == ACROSS) + app_data.current_movement = DOWN; + else + app_data.current_movement = ACROSS; + } + /* if it is only start of across word, make the current + movement across */ + else if (app_data.puzzle.start_across_word[row][col] != -1) + app_data.current_movement = ACROSS; + /* else down movement */ + else if (app_data.puzzle.start_down_word[row][col] != -1) + app_data.current_movement = DOWN; + } + } + } + + gtk_widget_queue_draw_area (widget, 0, 0, + app_data.puzzle.grid_size*GRID_PIXELS+10, + app_data.puzzle.grid_size*GRID_PIXELS+10); + + return FALSE; +} + +/* slot for handling key press event for puzzle drawing area */ +gboolean on_puzzle_area_key_press_event (GtkWidget *widget, + GdkEventKey *event, gpointer data) +{ + /* respond to key events only if the puzzle is loaded */ + if (app_data.is_loaded == true) + { + /* if the solution is revealed, don't respond to key events except + to return to the non-solution mode */ + if (app_data.solution_revealed == true) + { + app_data.solution_revealed = false; + gtk_widget_queue_draw_area (widget, 0, 0, + app_data.puzzle.grid_size*GRID_PIXELS+10, + app_data.puzzle.grid_size*GRID_PIXELS+10); + return FALSE; + } + + switch (event->keyval) + { + case GDK_KEY_Down : move_current_row (&app_data, DIR_FORWARD); + app_data.current_movement = DOWN; + break; + case GDK_KEY_Up : move_current_row (&app_data, DIR_BACK); + app_data.current_movement = DOWN; + break; + case GDK_KEY_Right: move_current_col (&app_data, DIR_FORWARD); + app_data.current_movement = ACROSS; + break; + case GDK_KEY_Left : move_current_col (&app_data, DIR_BACK); + app_data.current_movement = ACROSS; + break; + case GDK_KEY_BackSpace: + case GDK_KEY_Delete: + case GDK_KEY_space: + app_data.char_ans[app_data.cur_row][app_data.cur_col] = ' '; + break; + case GDK_KEY_a : + case GDK_KEY_A : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'A'; + break; + case GDK_KEY_b : + case GDK_KEY_B : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'B'; + break; + case GDK_KEY_c : + case GDK_KEY_C : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'C'; + break; + case GDK_KEY_d : + case GDK_KEY_D : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'D'; + break; + case GDK_KEY_e : + case GDK_KEY_E : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'E'; + break; + case GDK_KEY_f : + case GDK_KEY_F : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'F'; + break; + case GDK_KEY_g : + case GDK_KEY_G : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'G'; + break; + case GDK_KEY_h : + case GDK_KEY_H : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'H'; + break; + case GDK_KEY_i : + case GDK_KEY_I : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'I'; + break; + case GDK_KEY_j : + case GDK_KEY_J : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'J'; + break; + case GDK_KEY_k : + case GDK_KEY_K : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'K'; + break; + case GDK_KEY_l : + case GDK_KEY_L : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'L'; + break; + case GDK_KEY_m : + case GDK_KEY_M : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'M'; + break; + case GDK_KEY_n : + case GDK_KEY_N : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'N'; + break; + case GDK_KEY_o : + case GDK_KEY_O : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'O'; + break; + case GDK_KEY_p : + case GDK_KEY_P : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'P'; + break; + case GDK_KEY_q : + case GDK_KEY_Q : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'Q'; + break; + case GDK_KEY_r : + case GDK_KEY_R : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'R'; + break; + case GDK_KEY_s : + case GDK_KEY_S : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'S'; + break; + case GDK_KEY_t : + case GDK_KEY_T : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'T'; + break; + case GDK_KEY_u : + case GDK_KEY_U : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'U'; + break; + case GDK_KEY_v : + case GDK_KEY_V : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'V'; + break; + case GDK_KEY_w : + case GDK_KEY_W : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'W'; + break; + case GDK_KEY_x : + case GDK_KEY_X : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'X'; + break; + case GDK_KEY_y : + case GDK_KEY_Y : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'Y'; + break; + case GDK_KEY_z : + case GDK_KEY_Z : + app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'Z'; + break; + default : return FALSE; + + } + } + + /* move the selection pointer to next row/col as per the current + movement orientation if it is not a key left right up or down */ + if (event->keyval != GDK_KEY_Down && event->keyval != GDK_KEY_Up && + event->keyval != GDK_KEY_Left && event->keyval != GDK_KEY_Right + && event->keyval != GDK_KEY_Delete) + { + /* if the current movement is an across movement */ + if (app_data.current_movement == ACROSS) + { + /* if the next column is not blocking move the col */ + if (event->keyval != GDK_KEY_BackSpace && + next_col_block (&app_data.puzzle, app_data.cur_row, + app_data.cur_col) == false) + move_current_col (&app_data, DIR_FORWARD); + else if (event->keyval == GDK_KEY_BackSpace && + prev_col_block (&app_data.puzzle, app_data.cur_row, + app_data.cur_col) == false) + move_current_col (&app_data, DIR_BACK); + } + /* current movement is a up/down movement */ + else + { + if (event->keyval != GDK_KEY_BackSpace && + next_row_block (&app_data.puzzle, + app_data.cur_row, app_data.cur_col) == false) + move_current_row (&app_data, DIR_FORWARD); + else if (event->keyval == GDK_KEY_BackSpace && + prev_row_block (&app_data.puzzle, + app_data.cur_row, app_data.cur_col) == false) + move_current_row (&app_data, DIR_BACK); + } + } + + + gtk_widget_queue_draw_area (widget, 0, 0, + app_data.puzzle.grid_size*GRID_PIXELS+10, + app_data.puzzle.grid_size*GRID_PIXELS+10); + + return FALSE; +} + +/* slot for drawing the puzzle */ +gboolean on_puzzle_area_draw (GtkWidget *widget, cairo_t *cr, gpointer data) +{ + /* if a puzzle is loaded */ + if (app_data.is_loaded == true) + { + GdkRGBA colorfore, colorback, colorblue, colorbacksel1, + colorbacksel2, colorsolution; + gdk_rgba_parse (&colorfore, "#000000"); + gdk_rgba_parse (&colorback, "#ffffff"); + gdk_rgba_parse (&colorblue, "#0000dd"); + gdk_rgba_parse (&colorbacksel1, "#ffffaa"); + gdk_rgba_parse (&colorbacksel2, "#aaffff"); + gdk_rgba_parse (&colorsolution, "#990099"); + cairo_set_line_width (cr, 3); + + /* set the size of the drawing area */ + gtk_widget_set_size_request (widget, + app_data.puzzle.grid_size*GRID_PIXELS+10, + app_data.puzzle.grid_size*GRID_PIXELS+10); + + /* Draw the grid */ + for (int i = 0; i < app_data.puzzle.grid_size; i ++) + { + for (int j = 0; j < app_data.puzzle.grid_size; j ++) + { + /* if it is the current selection or if -1 is the current + selection then let the current selection be the first word */ + if (app_data.cur_col == -1 && app_data.cur_row == -1) + { + if (app_data.puzzle.start_across_word[i][j] == 1) + { + app_data.cur_row = i; + app_data.cur_col = j; + app_data.current_movement = ACROSS; + } + else if (app_data.puzzle.start_down_word[i][j] == 1) + { + app_data.cur_row = i; + app_data.cur_col = j; + app_data.current_movement = DOWN; + } + } + + gdk_cairo_set_source_rgba (cr, &colorfore); + cairo_rectangle (cr, j*GRID_PIXELS+5, i*GRID_PIXELS+5, + GRID_PIXELS, GRID_PIXELS); + + cairo_stroke (cr); + + /* if it is not a blank grid then set the background color + to black */ + if (app_data.puzzle.chars[i][j] != '#') + gdk_cairo_set_source_rgba (cr, &colorback); + + /* if it is a current selection and solution reveal mode is not + set then then set the background depending on whether across + or down movement mode is current */ + if (app_data.solution_revealed == false && + app_data.cur_row == i && app_data.cur_col == j) + { + if (app_data.current_movement == ACROSS) + gdk_cairo_set_source_rgba (cr, &colorbacksel1); + else + gdk_cairo_set_source_rgba (cr, &colorbacksel2); + } + + cairo_rectangle (cr, j*GRID_PIXELS+5, i*GRID_PIXELS+5, + GRID_PIXELS, GRID_PIXELS); + cairo_fill (cr); + + /* draw the word number if it is the start of a word */ + if (app_data.puzzle.start_across_word[i][j] != -1 || + app_data.puzzle.start_down_word[i][j] != -1) + { + int num; + if (app_data.puzzle.start_across_word[i][j] != -1) + num = app_data.puzzle.start_across_word[i][j]; + else + num = app_data.puzzle.start_down_word[i][j]; + + gdk_cairo_set_source_rgba (cr, &colorblue); + cairo_select_font_face (cr, "sans serif", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size (cr, 11); + cairo_move_to (cr, j*GRID_PIXELS+7, i*GRID_PIXELS+15); + char cnum[3]; + sprintf (cnum, "%d", num); + cairo_show_text (cr, (const char*)cnum); + } + + /* if solution is not revealed set the color to normal + or set it to solution color */ + if (app_data.solution_revealed == false) + gdk_cairo_set_source_rgba (cr, &colorfore); + else + gdk_cairo_set_source_rgba (cr, &colorsolution); + + cairo_select_font_face (cr, "sans serif", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + + cairo_set_font_size (cr, 16); + cairo_move_to (cr, j*GRID_PIXELS+GRID_PIXELS/2, + i*GRID_PIXELS+GRID_PIXELS-10); + char ctxt[3]; + /* if it is solution mode reveal the answer or else the + user input */ + if (app_data.solution_revealed == false) + sprintf (ctxt, "%c", app_data.char_ans[i][j]); + else + if (app_data.puzzle.chars[i][j] != '#') + sprintf (ctxt, "%c", app_data.puzzle.chars[i][j]); + else + sprintf (ctxt, "%c", ' '); + cairo_show_text (cr, (const char*) ctxt); + + } + } + } + + return FALSE; + +} + +/* slot for reveal solution menu */ +void on_menu_reveal_solution_activate (GtkMenuItem *item, GtkDrawingArea *area) +{ + /* if puzzle solution is password protected ask for the password */ + if (strlen (app_data.puzzle.hashed_solution_password) > 0) + { + GtkBuilder *builder; + builder = gtk_builder_new (); + + guint ret = gtk_builder_add_from_resource + (builder, + "/org/harishankar/wordblah/wordblah_player.glade", + NULL); + if (ret == 0) + { + fprintf (stderr, ERROR_WINDOW); + g_object_unref (builder); + return; + } + + GtkWidget *password_dialog = GTK_WIDGET (gtk_builder_get_object + (builder, "password_dialog")); + GtkWidget *password_text = GTK_WIDGET (gtk_builder_get_object + (builder, "password_text")); + + if (password_dialog == NULL) + { + fprintf (stderr, ERROR_WINDOW); + g_object_unref (builder); + return; + } + gtk_window_set_transient_for (GTK_WINDOW(password_dialog), + GTK_WINDOW(main_window)); + gtk_dialog_set_default_response (GTK_DIALOG(password_dialog), + GTK_RESPONSE_ACCEPT); + gint res = gtk_dialog_run (GTK_DIALOG (password_dialog)); + if (res == GTK_RESPONSE_ACCEPT) + { + const gchar *user_pwd = gtk_entry_get_text + (GTK_ENTRY(password_text)); + /* if password is correct */ + if (verify_solution_password (&app_data.puzzle, user_pwd) == true) + app_data.solution_revealed = true; + /* password is incorrect */ + else + { + app_data.solution_revealed = false; + GtkWidget *errordlg ; + errordlg = gtk_message_dialog_new (GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + WRONG_PASSWORD); + gtk_dialog_run (GTK_DIALOG(errordlg)); + gtk_widget_destroy (errordlg); + } + } + + gtk_widget_destroy (password_text); + gtk_widget_destroy (password_dialog); + g_object_unref (builder); + } + else + app_data.solution_revealed = true; + + gtk_widget_queue_draw_area (GTK_WIDGET(area), 0, 0, + app_data.puzzle.grid_size*GRID_PIXELS+10, + app_data.puzzle.grid_size*GRID_PIXELS+10); + +} + +/* slot for load grid state menu */ +void on_menu_load_grid_state_activate (GtkMenuItem *item, GtkDrawingArea *area) +{ + GtkWidget *dialog; + GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; + gint res; + dialog = gtk_file_chooser_dialog_new (OPEN_FILE, GTK_WINDOW (main_window), + action, + "_Cancel", + GTK_RESPONSE_CANCEL, + "_Open", + GTK_RESPONSE_ACCEPT, + NULL + ); + + res = gtk_dialog_run (GTK_DIALOG (dialog)); + if (res == GTK_RESPONSE_ACCEPT) + { + char *filename; + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + load_user_data (&app_data, filename); + g_free (filename); + } + + gtk_widget_destroy (dialog); + + gtk_widget_queue_draw_area (GTK_WIDGET(area), 0, 0, + app_data.puzzle.grid_size*GRID_PIXELS+10, + app_data.puzzle.grid_size*GRID_PIXELS+10); + + update_clue_items (); +} + +/* slot for save grid state menu */ +void on_menu_save_grid_state_activate (GtkMenuItem *item, gpointer *data) +{ + if (app_data.is_loaded == false) + return; + + GtkWidget *dialog; + GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE; + gint res; + dialog = gtk_file_chooser_dialog_new (SAVE_FILE, GTK_WINDOW(main_window), + action, + "_Cancel", + GTK_RESPONSE_CANCEL, + "_Save", + GTK_RESPONSE_ACCEPT, + NULL); + res = gtk_dialog_run (GTK_DIALOG (dialog)); + if (res == GTK_RESPONSE_ACCEPT) + { + char *filename; + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + save_user_data (&app_data, filename); + g_free (filename); + } + + gtk_widget_destroy (dialog); +} + +/* slot for exit menu */ +void on_menu_exit_activate (GtkMenuItem *item, gpointer data) +{ + gtk_main_quit (); +} + +/* slot for open menu */ +void on_menu_open_activate (GtkMenuItem *item, GtkDrawingArea* area) +{ + GtkWidget *dialog; + GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; + gint res; + dialog = gtk_file_chooser_dialog_new (OPEN_FILE, GTK_WINDOW(main_window), + action, + "_Cancel", + GTK_RESPONSE_CANCEL, + "_Open", + GTK_RESPONSE_ACCEPT, + NULL); + res = gtk_dialog_run (GTK_DIALOG (dialog)); + if (res == GTK_RESPONSE_ACCEPT) + { + char *filename; + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog)); + MainPlayerData temp; + reset_player_data (&temp, filename); + + /* if the grid is not frozen then the game cannot be played */ + if (temp.is_loaded == false) + { + GtkWidget *errordlg ; + errordlg = gtk_message_dialog_new (GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + UNFROZEN_GRID_PLAYER); + gtk_dialog_run (GTK_DIALOG(errordlg)); + gtk_widget_destroy (errordlg); + } + else + { + app_data = temp; + gtk_widget_queue_draw_area (GTK_WIDGET (area), 0, 0, + app_data.puzzle.grid_size*30+10, + app_data.puzzle.grid_size*30+10); + + } + update_clue_items (); + g_free (filename); + } + + gtk_widget_destroy (dialog); +} + +/* slot for about menu */ +void on_menu_about_activate (GtkMenuItem *item, gpointer data) +{ + const char *AUTHOR[] = {"V.Harishankar", NULL}; + gtk_show_about_dialog (GTK_WINDOW(main_window), "authors",AUTHOR, + "program-name", PROGRAM_NAME, + "copyright", COPYRIGHT, + "comments", COMMENTS, + "website", WEBSITE, + "website-label", WEBSITE_LABEL, + "license-type", GTK_LICENSE_GPL_2_0, + "version", VERSION, + (char*)NULL); +} + +int main (int argc, char *argv []) +{ + gtk_init (&argc, &argv); + GdkPixbuf *icon; + icon = gdk_pixbuf_new_from_resource + ("/org/harishankar/wordblah/wordblah.svg", NULL); + if (icon == NULL) + fprintf (stderr, ERROR_ICON); + + GtkBuilder *builder; + builder = gtk_builder_new (); + guint ret = gtk_builder_add_from_resource (builder, + "/org/harishankar/wordblah/wordblah_player.glade", NULL); + + app_data.is_loaded = false; + + if (ret == 0) + { + fprintf (stderr, ERROR_WINDOW); + g_object_unref (builder); + return 1; + } + else + { + main_window = GTK_WIDGET (gtk_builder_get_object (builder, + "main_window")); + + across_store = GTK_LIST_STORE (gtk_builder_get_object + (builder, "store_across_clues")); + down_store = GTK_LIST_STORE (gtk_builder_get_object + (builder, "store_down_clues")); + + if (main_window != NULL) + { + gtk_window_set_default_icon (icon); + + GtkWidget *draw_area = GTK_WIDGET ( + gtk_builder_get_object(builder, "puzzle_area")); + + /* make drawing area respond to mouse event */ + gtk_widget_set_events(draw_area, + gtk_widget_get_events(draw_area) + | GDK_BUTTON_PRESS_MASK); + + gtk_builder_connect_signals (builder, NULL); + g_object_unref (builder); + gtk_widget_show (main_window); + gtk_main (); + return 0; + } + else + { + g_object_unref (builder); + fprintf (stderr, ERROR_WINDOW); + return 1; + } + } +} diff --git a/wordblah_player.glade b/wordblah_player.glade new file mode 100644 index 0000000..36c2054 --- /dev/null +++ b/wordblah_player.glade @@ -0,0 +1,417 @@ + + + + + + + + + + + + + + + + + + + + + + False + Wordblah Player + 540 + 400 + + + + + + + True + False + vertical + + + True + False + + + True + False + _Puzzle + True + + + True + False + + + True + False + _Open... + True + + + + + + True + False + + + + + True + False + E_xit + True + + + + + + + + + + True + False + _Grid + True + + + True + False + + + True + False + _Save Grid State... + True + + + + + + True + False + _Load Grid State... + True + + + + + + + + + + True + False + _View + True + + + True + False + + + True + False + _Reveal Solution... + True + + + + + + + + + + True + False + _Help + True + + + True + False + + + True + False + _About... + True + + + + + + + + + + False + True + 0 + + + + + True + True + + + 80 + 80 + True + True + in + + + True + False + natural + natural + + + True + True + True + True + True + + + + + + + + + + True + True + + + + + True + False + vertical + + + True + False + in + + + True + False + store_across_clues + False + 0 + False + + + + + + + + True + Id + + + + 0 + + + + + + + True + Clues Across + + + + 1 + 1 + + + + + + + + + True + True + + + + + True + False + in + + + True + False + store_down_clues + False + 0 + False + + + + + + + + True + Id + + + + 0 + + + + + + + True + Clues Down + + + + 1 + + + + + + + + + True + True + + + + + True + True + + + + + True + True + 1 + + + + + + + False + Password + False + True + dialog + main_window + + + + + + False + 10 + 10 + 10 + 10 + vertical + 2 + + + False + end + + + gtk-cancel + True + True + False + True + True + + + True + True + 0 + + + + + gtk-ok + True + True + True + False + True + True + + + True + True + 1 + + + + + False + False + 0 + + + + + True + False + Password for revealing solution + + + False + True + 1 + + + + + True + True + 256 + False + ● + True + password + + + False + True + 3 + + + + + + button1 + button2 + + + diff --git a/wordblox.c b/wordblox.c deleted file mode 100644 index f30c039..0000000 --- a/wordblox.c +++ /dev/null @@ -1,438 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "wordblox.h" -#include "constantstrings.h" - -/* export the clues to a text file */ -void do_export_clues (Puzzle *p) -{ - if (p->grid_frozen == false) - { - printf (UNFROZEN_GRID); - char ch = getchar (); - return; - } - char fname[256]; - printf (INPUT_FILE); - fgets (fname, 256, stdin); - if (strlen(fname) == 1) - return; - char *filename = strtok (fname, "\n"); - - export_clues (p, filename); - printf (FILE_SAVED); - char ch = getchar (); -} - -/* export the puzzle to a png file */ -void do_export_puzzle (Puzzle *p) -{ - if (p->grid_frozen == false) - { - printf (UNFROZEN_GRID); - char ch = getchar (); - return; - } - char fname[256]; - printf (INPUT_FILE); - fgets (fname, 256, stdin); - if (strlen (fname) == 1) - return; - char* filename = strtok (fname, "\n"); - - printf (INPUT_EXPORT_ANSWERS); - char ans[3]; - fgets (ans, 3, stdin); - bool solution; - solution = (toupper (ans[0]) == 'Y') ? true : false; - - export_grid_image (p, filename, solution); - printf (FILE_SAVED); - char ch = getchar (); -} - -/* reset the grid */ -void do_reset_puzzle (Puzzle *p) -{ - int grid_size = p->grid_size; - printf (INPUT_CONFIRM_RESET); - char conf[3]; - fgets (conf, 3, stdin); - if (toupper (conf[0]) == 'Y') - init_puzzle (p, grid_size); - - print_puzzle (p); - char ch = getchar (); -} - -/* set the solution password for the puzzle */ -void do_set_solution_password (Puzzle *p) -{ - char* password; - password = getpass (INPUT_PASSWORD); - /* if empty reset the password to nothing */ - if (strlen(password) == 0) - { - set_solution_password (p, "\0"); - printf (SOLUTION_PASSWORD_RESET); - char ch = getchar (); - } - /* set the password */ - else - { - set_solution_password (p, (const char* )password); - printf (PASSWORD_SET); - char ch = getchar (); - } -} -/* set the master (editing) password for the puzzle */ -void do_set_master_password (Puzzle *p) -{ - char* password; - password = getpass (INPUT_PASSWORD); - /* if empty reset the password to nothing */ - if (strlen(password) == 0) - { - set_master_password (p, "\0"); - printf (MASTER_PASSWORD_RESET); - char ch = getchar (); - } - /* set the password */ - else - { - set_master_password (p, (const char* )password); - printf (PASSWORD_SET); - char ch = getchar (); - } -} - -/* set clue for a word - only for frozen grid */ -void do_set_clue_word (Puzzle *p, enum ORIENTATION orient) -{ - print_puzzle (p); - if (p->grid_frozen == false) - { - printf (UNFROZEN_GRID); - char ch = getchar (); - return; - } - int index; - String clue; - printf (INPUT_INDEX); - index = get_num (); - printf (INPUT_CLUE); - fgets (clue, MAX_CLUE_LENGTH, stdin); - if (strlen (clue) == 1) - return; - char* cl = strtok (clue, "\n"); - - bool res; - res = set_clue (p, cl, index, orient); - - if (res == false) - { - printf (NO_WORD_INDEX); - char ch = getchar (); - } -} - -/* clear a cell in the grid */ -void do_clear_cell (Puzzle *p) -{ - print_puzzle (p); - if (p->grid_frozen == true) - { - printf (FROZEN_GRID); - char ch = getchar (); - return; - } - int row, col; - printf (INPUT_ROW); - row = get_num (); - printf (INPUT_COL); - col = get_num (); - if (row >= p->grid_size || col >= p->grid_size) - { - printf (EXCEED_GRID_SIZE); - char ch = getchar (); - return; - } - p->chars[row][col] = ' '; - print_puzzle (p); - - char ch = getchar (); -} - -/* add a down word to the grid */ -void do_add_down_word (Puzzle *p) -{ - print_puzzle (p); - if (p->grid_frozen == true) - { - printf (FROZEN_GRID); - char ch = getchar (); - return; - } - char wd[MAX_PUZZLE_SIZE]; - int row, col; - printf (INPUT_WORD); - fgets (wd, MAX_PUZZLE_SIZE, stdin); - char *word = is_valid_word (wd); - if (word == NULL) - { - printf (INVALID_WORD); - char ch = getchar (); - return; - } - printf (INPUT_ROW); - row = get_num (); - printf (INPUT_COL); - col = get_num (); - if (row >= p->grid_size || col >= p->grid_size) - { - printf (EXCEED_GRID_SIZE); - char ch = getchar (); - return; - } - if (strlen (word) > (p->grid_size - row)) - { - printf (WORD_TOO_LONG); - char ch = getchar (); - return; - } - - for (int i = row; i < row + strlen(word); i ++) - p->chars[i][col] = toupper(word[i - row]); - - print_puzzle (p); - char ch = getchar (); -} - -/* add an across word to the grid */ -void do_add_across_word (Puzzle *p) -{ - print_puzzle (p); - if (p->grid_frozen == true) - { - printf (FROZEN_GRID); - char ch = getchar (); - return; - } - char wd[MAX_PUZZLE_SIZE]; - int row, col; - printf (INPUT_WORD); - fgets (wd, MAX_PUZZLE_SIZE, stdin); - char *word = is_valid_word (wd); - if (word == NULL) - { - printf (INVALID_WORD); - char ch = getchar (); - return; - } - printf (INPUT_ROW); - row = get_num (); - printf (INPUT_COL); - col = get_num (); - if (row >= p->grid_size || col >= p->grid_size) - { - printf (EXCEED_GRID_SIZE); - char ch = getchar (); - return; - } - - if (strlen (word) > (p->grid_size - col)) - { - printf (WORD_TOO_LONG); - char ch = getchar (); - return; - } - - for (int i = col; i < col+strlen (word); i ++) - p->chars[row][i] = toupper(word[i - col]); - - print_puzzle (p); - char ch = getchar (); -} -/* confirm exit */ -bool do_confirm_exit () -{ - printf (INPUT_CONFIRM_EXIT); - char res[3]; - fgets (res, 3, stdin); - if (toupper(res[0]) == 'Y') - return true; - else - return false; -} - -/* main loop for the puzzle editor */ -void puzzle_editor_loop (Puzzle *p, const char *filename) -{ - bool loop = true; - while (loop) - { - char puzzle_title[60]; - sprintf (puzzle_title, "%s - %s", PUZZLE_MENU_TITLE, filename); - print_menu (WHITE, BLUE, puzzle_title, PUZZLE_EDIT_MENU, 15, 50); - printf (INPUT_CHOICE); - int ch = get_num (); - switch (ch) - { - case 1: print_puzzle (p); - char ch = getchar (); - break; - case 2: do_add_across_word (p); - break; - case 3: do_add_down_word (p); - break; - case 4: do_clear_cell (p); - break; - case 5: freeze_puzzle (p); - print_puzzle (p); - ch = getchar (); - break; - case 6: unfreeze_puzzle (p); - print_puzzle (p); - ch = getchar (); - break; - case 7: do_set_clue_word (p, ACROSS); - break; - case 8: do_set_clue_word (p, DOWN); - break; - case 9: save_puzzle (p, filename); - printf ("%s\n",FILE_SAVED); - ch = getchar (); - break; - case 10: do_set_master_password (p); - break; - case 11: do_set_solution_password (p); - break; - case 12: do_reset_puzzle (p); - break; - case 13: do_export_puzzle (p); - break; - case 14: do_export_clues (p); - break; - case 15: loop = ! do_confirm_exit (); - break; - } - } -} - -/* open an existing puzzle */ -void do_open_puzzle (const char *filename) -{ - Puzzle p; - /* if no filename is provided, get it from command line */ - if (filename == NULL) - { - printf (INPUT_FILE); - char fname[256]; - fgets(fname, 256, stdin); - if (strlen (fname) == 1) - return; - filename = strtok (fname, "\n"); - } - - p = load_puzzle (filename); - - if (strcmp (p.hashed_master_password, "\0") != 0) - { - char *passwd; - passwd = getpass (INPUT_PASSWORD); - if (strlen (passwd) == 0) - return; - - if (verify_master_password (&p, (const char*) passwd)) - puzzle_editor_loop (&p, filename); - else - { - printf (WRONG_PASSWORD); - char ch = getchar (); - } - } - else - puzzle_editor_loop (&p, filename); -} - -/* create a new blank puzzle */ -void do_new_puzzle (char *filename, int size) -{ - Puzzle p; - /* if filename is not provided get it from command line */ - if (filename == NULL) - { - printf (INPUT_FILE); - char fname[256]; - fgets (fname, 256, stdin); - if (strlen (fname) == 1) - return; - filename = strtok (fname, "\n"); - } - /* if no size is specified get it from command line */ - if (size == -1) - { - printf (INPUT_GRID_SIZE); - size = get_num (); - } - - if (size > MAX_PUZZLE_SIZE) - { - printf (EXCEED_MAX_GRID_SIZE); - char c = getchar (); - } - else - { - init_puzzle (&p, size); - puzzle_editor_loop (&p, filename); - } -} - -/* The main loop of the program */ -int main_loop () -{ - /* Print the main menu */ - while (1) - { - print_menu (WHITE, BLUE, MAIN_MENU_TITLE, MAIN_MENU, 3, 50); - printf (INPUT_CHOICE); - int ch = get_num (); - switch (ch) - { - case 1: do_new_puzzle (NULL, -1); - break; - case 2: do_open_puzzle (NULL); - break; - case 3: exit (0); - } - } -} - -int main (int argc, char* argv[]) -{ - if (argc >= 2) - { - Puzzle p; - switch (argc) - { - case 2 : do_open_puzzle (argv[1]); - break; - case 4 : if (strcmp (argv[2], "new") == 0) - { - int grid_size = atoi (argv[3]); - do_new_puzzle (argv[1], grid_size); - break; - } - default: fprintf (stderr, USAGE_LINE_1, argv[0]); - fprintf (stderr, USAGE_LINE_2); - fprintf (stderr, USAGE_LINE_3); - exit (3); - } - } - return (main_loop ()); -} diff --git a/wordblox.gresource.xml b/wordblox.gresource.xml deleted file mode 100644 index ae2f5f0..0000000 --- a/wordblox.gresource.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - wordblox_player.glade - wordblox.svg - - diff --git a/wordblox.h b/wordblox.h deleted file mode 100644 index 7519ff4..0000000 --- a/wordblox.h +++ /dev/null @@ -1,1032 +0,0 @@ -#ifndef __WORDBLOX_H -#define __WORDBLOX_H -#define _XOPEN_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "constantstrings.h" - -#define MAX_PUZZLE_SIZE 25 -#define MAX_CLUE_LENGTH 150 -#define GRID_PIXELS 37 - -/* Enum to define terminal colours */ -enum COLOR { - BLACK = 0, - RED= 1, - GREEN=2, - YELLOW=3, - BLUE=4, - MAGENTA=5, - CYAN=6, - WHITE=7 -}; - -enum ATTR { - NORMAL = 23, - BOLD=1 -}; - -enum ORIENTATION { - ACROSS=1, - DOWN=2 -}; - -/* for use with the player */ -enum DIRECTION { - DIR_FORWARD = 1, - DIR_BACK = -1 -}; - -typedef char String[MAX_CLUE_LENGTH]; - -/* The main puzzle struct type */ -typedef struct { - char chars[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE]; - int start_across_word[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE]; - int start_down_word[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE]; - String clue_across[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE]; - String clue_down[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE]; - int grid_size; - bool grid_frozen; - char hashed_master_password[256]; - char hashed_solution_password[256]; -} Puzzle; - -/* The player data struct type - for the player app */ -typedef struct { - Puzzle puzzle; - char filename[65535]; - bool is_loaded; - char char_ans[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE]; - int cur_row; - int cur_col; - bool solution_revealed; - enum ORIENTATION current_movement; -} MainPlayerData; - -/* compute the hash of a password */ -void digest_message(const unsigned char *message, - size_t message_len, unsigned char **digest, unsigned int *digest_len) -{ - EVP_MD_CTX *mdctx; - - if((mdctx = EVP_MD_CTX_new()) == NULL) - goto err; - - if(1 != EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL)) - goto err; - - if(1 != EVP_DigestUpdate(mdctx, message, message_len)) - goto err; - - if((*digest = (unsigned char *) - OPENSSL_malloc(EVP_MD_size(EVP_sha256()))) == NULL) - goto err; - - if(1 != EVP_DigestFinal_ex(mdctx, *digest, digest_len)) - goto err; - - EVP_MD_CTX_free(mdctx); - return; -err: - EVP_MD_CTX_free(mdctx); - ERR_print_errors_fp(stderr); - exit (2); -} - -/* convert the hashed binary password to hexadecimal representation and - free the hashed binary password */ -void to_hexadecimal (char *hex, unsigned char *binary_pwd, unsigned int len) -{ - char buf[3]; - /* keep reference to beginning of the hashed password */ - unsigned char *binary_pw_begin = binary_pwd; - for (int i = 0; i < len; i ++) - { - sprintf (buf, "%02x", (*binary_pwd)&0xff); - strcat (hex, buf); - binary_pwd ++; - } - /* free the hashed password */ - OPENSSL_free (binary_pw_begin); -} - -/* get a number from the user */ -int get_num () -{ - char s[5]; - fgets (s, 5, stdin); - int n = atoi (s); - return n; -} - -/* verify solution password */ -bool verify_solution_password (Puzzle *p, const char* password) -{ - /* no password set */ - if (strcmp (p->hashed_solution_password, "\0") == 0) - return true; - - /* hash the user input password and compare it with the stored password */ - unsigned char* hashed_sol_password; - unsigned int len; - digest_message ((const unsigned char *)password, strlen(password), - &hashed_sol_password, &len); - char hashed_hex_pwd[256] = { (char) NULL }; - to_hexadecimal (hashed_hex_pwd, hashed_sol_password, len); - - if (strcmp (p->hashed_solution_password, hashed_hex_pwd) == 0) - return true; - - return false; -} - - -/* verify master password */ -bool verify_master_password (Puzzle *p, const char* password) -{ - /* no password set */ - if (strcmp (p->hashed_master_password, "\0") == 0) - return true; - - /* hash the user input password and compare it with the stored password */ - unsigned char* hashed_mas_password; - unsigned int len; - digest_message ((const unsigned char *)password, strlen(password), - &hashed_mas_password, &len); - char hashed_hex_pwd[256] = { (char) NULL }; - to_hexadecimal (hashed_hex_pwd, hashed_mas_password, len); - - if (strcmp (p->hashed_master_password, hashed_hex_pwd) == 0) - return true; - - return false; -} - -/* Set or reset solution password for puzzle */ -void set_solution_password (Puzzle *p, const char *password) -{ - /* if it is a null string, reset the password */ - if (strcmp (password, "\0") == 0) - strcpy (p->hashed_solution_password, "\0"); - else - { - - unsigned char* hashedpwd; - unsigned int len; - digest_message ((const unsigned char *)password, strlen(password), - &hashedpwd, &len); - /* the hashedpwd contains binary data - we will convert it to - hexadecimal data and store in file */ - - to_hexadecimal (p->hashed_solution_password, hashedpwd, len); - } -} - -/* Set or reset master password for puzzle */ -void set_master_password (Puzzle *p, const char *password) -{ - /* if it is a null string, reset the password */ - if (strcmp (password, "\0") == 0) - strcpy (p->hashed_master_password, "\0"); - else - { - - unsigned char* hashedpwd; - unsigned int len; - digest_message ((const unsigned char *)password, strlen(password), - &hashedpwd, &len); - /* the hashedpwd contains binary data - we will convert it to - hexadecimal data and store in file */ - - to_hexadecimal (p->hashed_master_password, hashedpwd, len); - } -} - -/* Output the clues to text file */ -void export_clues (Puzzle *p, const char *filename) -{ - FILE *outfile = fopen (filename, "w"); - if (outfile == NULL) - { - fprintf (stderr, "%s\n", ERROR_WRITING_FILE); - exit (1); - } - /* first the across clues */ - fprintf (outfile, "ACROSS CLUES\n"); - for (int i = 0; i < p->grid_size; i ++) - { - for (int j = 0; j < p->grid_size; j ++) - { - if (p->start_across_word[i][j] != -1) - fprintf (outfile, "%d - %s\n", p->start_across_word[i][j], - p->clue_across[i][j]); - } - } - /* now the down clues */ - fprintf (outfile, "DOWN CLUES\n"); - for (int i = 0; i < p->grid_size; i ++) - { - for (int j = 0; j < p->grid_size; j ++) - { - if (p->start_down_word[i][j] != -1) - fprintf (outfile, "%d - %s\n", p->start_down_word[i][j], - p->clue_down[i][j]); - } - } - fclose (outfile); -} - -/* Output the grid to image - if answerkey is true export filled grid */ -void export_grid_image (Puzzle *p, const char *filename, bool answerkey) -{ - int img_size = p->grid_size * GRID_PIXELS; - FILE * outfile = fopen (filename, "wb"); - if (outfile == NULL) - { - fprintf (stderr, "%s\n", ERROR_WRITING_FILE); - exit (1); - } - - gdImagePtr img = gdImageCreate (img_size, img_size); - gdImageColorAllocate (img, 255,255,255); - int black = gdImageColorAllocate (img, 0, 0, 0); - int blue = gdImageColorAllocate (img, 0, 0, 216); - gdFontPtr sm_fnt = gdFontGetMediumBold (); - gdFontPtr lg_fnt = gdFontGetGiant (); - - for (int i = 0; i < p->grid_size; i ++) - { - for (int j = 0; j < p->grid_size; j++) - { - /* if it is a block, draw the black square */ - if (p->chars[i][j] == '#') - gdImageFilledRectangle (img, j*GRID_PIXELS, i*GRID_PIXELS, - j*GRID_PIXELS+GRID_PIXELS, - i*GRID_PIXELS+GRID_PIXELS,black); - else - { - /* draw a regular square */ - gdImageRectangle (img, j*GRID_PIXELS, i*GRID_PIXELS, - j*GRID_PIXELS+GRID_PIXELS, - i*GRID_PIXELS+GRID_PIXELS, black); - - /* print the numers, if it is either start across word or - a down word */ - if (p->start_across_word[i][j] != -1 || - p->start_down_word[i][j] != -1) - { - if (p->start_across_word[i][j] != -1) - { - char str[5]; - sprintf (str, "%d", p->start_across_word[i][j]); - gdImageString (img, sm_fnt, j*GRID_PIXELS+2, - i*GRID_PIXELS+2, - (unsigned char *)str, blue); - } - else - { - char str[5]; - sprintf (str, "%d", p->start_down_word[i][j]); - gdImageString (img, sm_fnt, j*GRID_PIXELS+2, - i*GRID_PIXELS+2, - (unsigned char *)str, blue); - } - } - /* if answerkey is true, draw the character in the cell */ - if (answerkey) - { - gdImageChar (img, lg_fnt, j*GRID_PIXELS+15, - i*GRID_PIXELS+10, p->chars[i][j], black); - } - } - } - } - - gdImagePng (img, outfile); - gdImageDestroy (img); - fclose (outfile); -} - -/* Set the terminal colour */ -void set_color (enum COLOR fg, enum COLOR bg, enum ATTR at) { - printf ("\x1B[%d;%d;%dm", fg+30, bg+40, at); -} - -/* Reset the terminal colour */ -void reset_color () { - printf ("\x1B[0m"); -} - -/* check if the prev row has a block or not */ -bool prev_row_block (Puzzle *p, int r, int c) -{ - if (r == 0) - return true; - if (p->chars[r-1][c] == '#') - return true; - return false; -} - -/* check if the next row has a block or not */ -bool next_row_block (Puzzle *p, int r, int c) -{ - if (r == p->grid_size-1) - return true; - if (p->chars[r+1][c] == '#') - return true; - return false; -} - -/* check if the prev col has a block or not */ -bool prev_col_block (Puzzle *p, int r, int c) -{ - if (c == 0) - return true; - if (p->chars[r][c-1] == '#') - return true; - return false; -} - -/* check if the next col has a block or not */ -bool next_col_block (Puzzle *p, int r, int c) -{ - if (c == p->grid_size - 1) - return true; - if (p->chars[r][c+1] == '#') - return true; - return false; -} - -/* check if previous row is blank or not */ -bool prev_row_blank (Puzzle *p, int r, int c) -{ - if (r == 0) return true; - if (p->chars[r-1][c] == ' ' || p->chars[r-1][c] == '#') return true; - return false; -} -/* check if next row is blank or not */ -bool next_row_blank (Puzzle *p, int r, int c) -{ - if (r == p->grid_size - 1) return true; - if (p->chars[r+1][c] == ' ' || p->chars[r+1][c] == '#') return true; - return false; -} -/* check if previous col is blank or not */ -bool prev_col_blank (Puzzle *p, int r, int c) -{ - if (c == 0) return true; - if (p->chars[r][c-1] == ' ' || p->chars[r][c-1] == '#') return true; - return false; -} -/* check if the next col is blank or not */ -bool next_col_blank (Puzzle *p, int r, int c) -{ - if (c == p->grid_size -1) return true; - if (p->chars[r][c+1] == ' ' || p->chars[r][c+1] == '#') return true; - return false; -} - -/* set the current row/col to the beginning of word index (across or down) */ -void set_selection_to_word_start (MainPlayerData *app_data, - enum ORIENTATION orient, int word_index) -{ - for (int i = 0; i < app_data->puzzle.grid_size; i ++) - { - for (int j = 0; j < app_data->puzzle.grid_size; j ++) - { - if (orient == ACROSS && - app_data->puzzle.start_across_word[i][j] == word_index) - { - app_data->current_movement = ACROSS; - app_data->cur_row = i; - app_data->cur_col = j; - break; - } - else if (orient == DOWN && - app_data->puzzle.start_down_word[i][j] == word_index) - { - app_data->current_movement = DOWN; - app_data->cur_row = i; - app_data->cur_col = j; - break; - } - } - } -} - -/* unfreeze the grid - make editing possible to change words */ -void unfreeze_puzzle (Puzzle *p) -{ - for (int i = 0; i < p->grid_size; i ++) - { - for (int j = 0; j < p->grid_size; j ++) - { - if (p->chars[i][j] == '#') - p->chars[i][j] = ' '; - - p->start_across_word[i][j] = -1; - p->start_down_word[i][j] = -1; - } - } - p->grid_frozen = false; -} - -/* freeze the grid - make editing impossible because it finalizes the - across and down words in the grid */ -void freeze_puzzle (Puzzle *p) -{ - int word_num = 1; - bool across_word_start, down_word_start; - for (int i = 0; i < p->grid_size; i ++) - { - for (int j = 0; j < p->grid_size; j++) - { - across_word_start = false; - down_word_start = false; - /* if it is a blank cell - cover it with a block */ - if (p->chars[i][j] == ' ' || p->chars[i][j] == '#') - p->chars[i][j] = '#'; - /* it is not a blank cell - check all possibilities */ - else - { - bool prev_row = prev_row_blank (p, i, j); - bool next_row = next_row_blank (p, i, j); - bool prev_col = prev_col_blank (p, i, j); - bool next_col = next_col_blank (p, i, j); - if (prev_row && ! next_row) - down_word_start = true; - if (prev_col && ! next_col) - across_word_start = true; - } - - if (across_word_start == true) - p->start_across_word[i][j] = word_num; - else - p->start_across_word[i][j] = -1; - if (down_word_start == true) - p->start_down_word[i][j] = word_num; - else - p->start_down_word[i][j] = -1; - if (across_word_start == true || down_word_start == true) - word_num ++; - } - } - p->grid_frozen = true; -} - -/* reset the entire grid */ -void init_puzzle (Puzzle *p, int grid_size) -{ - p->grid_size = grid_size; - p->grid_frozen = false; - for (int i = 0; i < p->grid_size; i ++) - { - for (int j = 0; j < p->grid_size; j ++) - { - p->chars[i][j] = ' '; - p->start_across_word[i][j] = -1; - p->start_down_word[i][j] = -1; - strcpy (p->clue_across[i][j], ""); - strcpy (p->clue_down[i][j], ""); - } - } - strcpy (p->hashed_master_password, "\0"); - strcpy (p->hashed_solution_password, "\0"); - -} - -/* save the puzzle to a file */ -void save_puzzle (Puzzle *puzzle, const char* file) -{ - FILE *outfile; - /* First output the uncompressed contents to a temp file */ - outfile = tmpfile (); - if (outfile == NULL) - { - fprintf (stderr, "%s\n", ERROR_WRITING_FILE); - exit (1); - } - /* grid size is the first field */ - fprintf (outfile, "%d\n", puzzle->grid_size); - /* whether grid is frozen or not */ - fprintf (outfile, "%d\n", puzzle->grid_frozen); - /* the hashed password */ - fprintf (outfile, "%s\n", puzzle->hashed_master_password); - /* the hashed_solution_password */ - fprintf (outfile, "%s\n", puzzle->hashed_solution_password); - - /* First output the grid characters columns/rows */ - for (int i = 0; i < puzzle->grid_size; i ++) - { - for (int j = 0; j < puzzle->grid_size; j ++) - fprintf (outfile, "%c", puzzle->chars[i][j]); - fprintf (outfile, "\n"); - } - - /* Next output the start across/down numbers */ - for (int i = 0; i < puzzle->grid_size; i ++) - { - for (int j = 0; j < puzzle->grid_size; j++) - { - fprintf (outfile, "%d ", puzzle->start_across_word[i][j]); - fprintf (outfile, "%d ", puzzle->start_down_word[i][j]); - } - fprintf (outfile, "\n"); - } - - /* Output the across clues */ - fprintf (outfile, "ACROSS\n"); - /* Search the grid for across words */ - for (int i = 0; i < puzzle->grid_size; i ++) - { - for (int j = 0; j < puzzle->grid_size; j++) - { - /* if it is an across word, then put the word index followed by - tab character (as separator) and the clue */ - if (puzzle->start_across_word[i][j] != -1) - fprintf (outfile, "%d\t%s\n", puzzle->start_across_word[i][j], - puzzle->clue_across[i][j]); - } - } - - /* Output the down clues */ - fprintf (outfile, "DOWN\n"); - /* Search the grid for down words */ - for (int i = 0; i < puzzle->grid_size; i ++) - { - for (int j = 0; j < puzzle->grid_size; j++) - { - /* same as across word, put the word index followed by the tab - character and then the clue */ - if (puzzle->start_down_word[i][j] != -1) - fprintf (outfile, "%d\t%s\n", puzzle->start_down_word[i][j], - puzzle->clue_down[i][j]); - } - } - - /* Flush the buffer and rewind to beginning - to read and save into - gzip compressed file */ - fflush (outfile); - fseek (outfile, 0, 0); - - /* now compress the file and save it to destination file */ - gzFile outdestfile = gzopen (file, "wb"); - if (outdestfile == NULL) - { - fprintf (stderr, "%s\n", ERROR_WRITING_FILE); - fclose (outfile); - exit (1); - } - char buf[128]; - int num = fread (buf, sizeof(char), sizeof(char)*128, outfile); - while (num > 0) - { - int res = gzwrite (outdestfile, buf, num*sizeof(char) ); - if (res == 0) - { - fprintf (stderr, "%s %s\n", ERROR_WRITING_FILE, COMPRESSED); - fclose (outfile); - exit (1); - } - num = fread (buf, sizeof(char), sizeof(char)*128, outfile); - } - gzclose (outdestfile); - fclose (outfile); - -} - -/* read the puzzle from a file */ -Puzzle load_puzzle (const char* file) -{ - /* First open the GZip file */ - gzFile insourcefile = gzopen (file, "rb"); - if (insourcefile == NULL) - { - fprintf (stderr, "%s %s\n", ERROR_READING_FILE, COMPRESSED); - exit (1); - } - /* Open a temporary file to uncompress the contents */ - FILE *infile = tmpfile (); - if (infile == NULL) - { - fprintf (stderr, "%s\n", ERROR_READING_FILE); - exit (1); - } - /* Put the uncompressed content to the temp file */ - char buf[128]; - int num = 0; - num = gzread (insourcefile, buf, 128); - while (num > 0) - { - int res = fwrite (buf, 1, num, infile); - if (res == 0) - { - fprintf (stderr, "%s\n", ERROR_READING_FILE); - fclose (infile); - gzclose (insourcefile); - exit (1); - } - num = gzread (insourcefile, buf, 128); - } - /* Close the gzip file */ - gzclose (insourcefile); - /* Flush the temp file buffer and rewind to beginning */ - fflush (infile); - fseek (infile, 0, 0); - - /* Read the temporary file contents to the structure Puzzle */ - Puzzle p; - char line[MAX_CLUE_LENGTH+10]; - fgets (line, MAX_CLUE_LENGTH + 10, infile); - p.grid_size = atoi (line); - fgets (line, MAX_CLUE_LENGTH + 10, infile); - p.grid_frozen = atoi (line) == 0 ? false : true ; - fgets (line, MAX_CLUE_LENGTH + 10, infile); - if (strlen (line) != 1) - strcpy (p.hashed_master_password, strtok (line, "\n")); - else - strcpy (p.hashed_master_password, "\0"); - fgets (line, MAX_CLUE_LENGTH + 10, infile); - if (strlen (line) != 1) - strcpy (p.hashed_solution_password, strtok (line, "\n")); - else - strcpy (p.hashed_solution_password, "\0"); - - /* read each character of the grid */ - for (int i = 0; i < p.grid_size; i ++ ) - { - fgets (line, MAX_CLUE_LENGTH + 10, infile); - for (int j = 0; j < p.grid_size; j ++) - p.chars[i][j] = line[j]; - } - /* read the word numbers */ - for (int i = 0; i < p.grid_size; i ++) - { - fgets (line, MAX_CLUE_LENGTH + 10, infile); - char *token = strtok (line, " "); - for (int j = 0; j < p.grid_size; j ++) - { - if (token != NULL) - p.start_across_word[i][j] = atoi (token); - token = strtok (NULL, " "); - if (token != NULL) - p.start_down_word[i][j] = atoi (token); - token = strtok (NULL, " "); - } - } - /* read the clues */ - fgets (line, MAX_CLUE_LENGTH + 10, infile); - - /* across clues */ - char clues[100][MAX_CLUE_LENGTH]; - int word_num[100]; - int c = 0; - /* first read the across clues from file */ - while (1) - { - fgets (line, MAX_CLUE_LENGTH + 10, infile); - /* if reached the end of across clues */ - if (strcmp (line, "DOWN\n") == 0) - break; - word_num[c] = atoi (strtok (line, "\t")); - char *cl = strtok (NULL, "\n"); - if (cl != NULL) - strcpy (clues[c], cl); - else - strcpy (clues[c], "\0"); - c++; - } - /* set the clue to the correct cell in grid */ - for (int i = 0; i < p.grid_size; i ++) - { - for (int j = 0; j < p.grid_size; j ++) - { - for (int r = 0; r < c; r ++) - if (p.start_across_word[i][j] == word_num[r]) - strcpy (p.clue_across[i][j], clues[r]); - } - } - - /* down clues */ - c = 0; - while (fgets (line, MAX_CLUE_LENGTH + 10, infile)) - { - word_num[c] = atoi (strtok (line, "\t")); - char* cl = strtok (NULL, "\n"); - if (cl != NULL) - strcpy (clues[c], cl); - else - strcpy (clues[c], "\0"); - c++; - } - for (int i = 0; i < p.grid_size; i ++) - { - for (int j = 0; j < p.grid_size; j ++) - { - for (int r = 0; r < c; r ++) - if (p.start_down_word[i][j] == word_num[r]) - strcpy (p.clue_down[i][j], clues[r]); - } - } - - fclose (infile); - return p; -} - -/* display the puzzle */ -void print_puzzle (Puzzle *p) -{ - printf ("\n"); - set_color (WHITE, CYAN, NORMAL); - printf (" "); - for (int i = 0; i < p->grid_size; i ++) - printf ("%3d", i); - reset_color (); - printf("\n"); - for (int i = 0; i < p->grid_size; i ++) - { - set_color (WHITE, CYAN, NORMAL); - printf ("%3d ", i); - for (int j = 0; j < p->grid_size; j ++) - { - if (p->chars[i][j] == '#') { - set_color (WHITE, BLACK, NORMAL); - printf (" "); - } - else - { - if (p->start_across_word[i][j] != -1 || - p->start_down_word[i][j] != -1) - { - set_color (BLUE, WHITE, NORMAL); - if (p->start_across_word[i][j] != -1) - printf ("%-2d", p->start_across_word[i][j]); - else - printf ("%-2d", p->start_down_word[i][j]); - } - else - { - set_color (BLACK, WHITE,NORMAL); - printf (" "); - } - - set_color (BLACK, WHITE, BOLD); - printf ("%c", p->chars[i][j]); - } - reset_color (); - } - printf ("\n"); - } - /* print the clues if set */ - if (p->grid_frozen == true) - { - printf ("\x1B[1mACROSS - CLUES\x1B[0m\n"); - for (int i = 0; i < p->grid_size; i ++) - { - for (int j = 0; j < p->grid_size; j ++) - { - if (p->start_across_word[i][j] != -1) - { - printf ("%d - %s; ", p->start_across_word[i][j], - p->clue_across[i][j]); - } - } - } - printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n"); - for (int i = 0; i < p->grid_size; i ++) - { - for (int j = 0; j < p->grid_size; j ++) - { - if (p->start_down_word[i][j] != -1) - { - printf ("%d - %s; ", p->start_down_word[i][j], - p->clue_down[i][j]); - } - } - } - printf ("\n"); - } -} - -/* function to check if a word is valid or not */ -char* is_valid_word (char *word) -{ - if (word == NULL || strlen(word) == 0) - return NULL; - for (int i = 0; i < strlen (word) - 1; i ++) - if (! isalpha (word[i])) - return NULL; - - return strtok (word, "\n"); -} - - -/* function to set a clue for an across word */ -bool set_clue (Puzzle *p, String clue, int index, enum ORIENTATION order) -{ - for (int i = 0; i < p->grid_size; i ++) - { - for (int j = 0; j < p->grid_size; j ++) - { - if (order == ACROSS) - { - if (p->start_across_word[i][j] == index) - { - strcpy (p->clue_across[i][j], clue); - return true; - } - } - else if (order == DOWN) - { - if (p->start_down_word[i][j] == index) - { - strcpy (p->clue_down[i][j], clue); - return true; - } - } - } - } - return false; -} - -/* function to print a menu */ -void print_menu (enum COLOR fg, enum COLOR bg, const char* title, - char **items, int num_items, int padding) -{ - /* clear screen */ - printf ("\e[1;1H\e[2J"); - set_color (fg, bg, NORMAL); - printf ("\u2554"); - for (int i = 0; i < padding; i ++) - printf ("\u2550"); - printf ("\u2557"); - reset_color (); printf ("\n"); - set_color (fg, bg, NORMAL); - printf ("\u2551"); - set_color (fg, bg, BOLD); - printf ("%-*s", padding, title); - reset_color (); - set_color (fg, bg, NORMAL); - printf ("\u2551"); - reset_color (); printf ("\n"); - set_color (fg, bg, NORMAL); - printf ("\u2560"); - for (int i = 0; i < padding; i ++) - printf ("\u2550"); - printf ("\u2563"); - reset_color (); printf ("\n"); - for (int i = 0; i < num_items; i ++) - { - set_color (fg, bg, NORMAL); - printf ("\u2551%-*s\u2551", padding, items[i]); - reset_color (); printf ("\n"); - } - set_color (fg, bg, NORMAL); - printf ("\u255A"); - for (int i = 0; i < padding; i ++) - printf ("\u2550"); - printf ("\u255D"); - reset_color (); printf ("\n"); -} - -/* reset the player data, loading from the puzzle file */ -void reset_player_data (MainPlayerData *app_data, const char *filename) -{ - app_data->puzzle = load_puzzle (filename); - - app_data->is_loaded = app_data->puzzle.grid_frozen; - app_data->cur_col = -1; - app_data->cur_row = -1; - app_data->solution_revealed = false; - strcpy (app_data->filename, filename); - /* reset the answer keys */ - for (int i = 0; i < app_data->puzzle.grid_size; i ++) - for (int j = 0; j < app_data->puzzle.grid_size; j ++) - app_data->char_ans[i][j] = ' '; - -} - -/* save the user grid to a file */ -void save_user_data (MainPlayerData *app_data, const char *filename) -{ - FILE *outfile; - outfile = fopen (filename, "wb"); - if (outfile == NULL) - { - fprintf (stderr, ERROR_WRITING_FILE); - return; - } - fprintf (outfile, "%s\n", app_data->filename); - for (int i = 0; i < app_data->puzzle.grid_size; i ++) - { - for (int j = 0; j < app_data->puzzle.grid_size; j ++) - fprintf (outfile, "%c", app_data->char_ans[i][j]); - fprintf (outfile, "\n"); - } - - fclose (outfile); -} - -/* load the user grid from a file */ -void load_user_data (MainPlayerData *app_data, const char *filename) -{ - FILE *infile; - infile = fopen (filename, "rb"); - if (infile == NULL) - { - fprintf (stderr, "%s\n", ERROR_READING_FILE); - return; - } - - char puzzle_file_name[65535]; - fgets (puzzle_file_name, 65535, infile); - reset_player_data (app_data, strtok (puzzle_file_name, "\n")); - - char line[MAX_PUZZLE_SIZE+10]; - for (int i = 0; i < app_data->puzzle.grid_size; i ++) - { - fgets (line, MAX_PUZZLE_SIZE+10, infile); - for (int j = 0; j < app_data->puzzle.grid_size; j ++) - app_data->char_ans[i][j] = line[j]; - - } - fclose (infile); -} - -/* in the player app, move the current selection index left or right */ -void move_current_col (MainPlayerData *app_data, enum DIRECTION dir) -{ - int r = app_data->cur_row; - int c = app_data->cur_col; - if (dir == DIR_FORWARD) - { - c ++; - while (c < app_data->puzzle.grid_size) - { - if (app_data->puzzle.chars[r][c] == '#') - c ++; - else - break; - } - if (c < app_data->puzzle.grid_size) - app_data->cur_col = c; - } - else - { - c --; - while (c >= 0) - { - if (app_data->puzzle.chars[r][c] == '#') - c --; - else - break; - } - if (c >= 0) - app_data->cur_col = c; - } -} - -/* in the player app move the current selection index up or down */ -void move_current_row (MainPlayerData *app_data, enum DIRECTION dir) -{ - int r = app_data->cur_row; - int c = app_data->cur_col; - if (dir == DIR_FORWARD) - { - r ++; - while (r < app_data->puzzle.grid_size) - { - if (app_data->puzzle.chars[r][c] == '#') - r ++; - else - break; - } - if (r < app_data->puzzle.grid_size) - app_data->cur_row = r; - } - else - { - r --; - while (r >= 0) - { - if (app_data->puzzle.chars[r][c] == '#') - r --; - else - break; - } - if (r >= 0) - app_data->cur_row = r; - } -} - -#endif diff --git a/wordblox.svg b/wordblox.svg deleted file mode 100644 index d7e6745..0000000 --- a/wordblox.svg +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - diff --git a/wordblox_player.c b/wordblox_player.c deleted file mode 100644 index 9560ac8..0000000 --- a/wordblox_player.c +++ /dev/null @@ -1,724 +0,0 @@ -#include - -#include "constantstrings.h" -#include "wordblox_resource.c" -#include "wordblox.h" - -GtkWidget *main_window; -GtkListStore *across_store; -GtkListStore *down_store; - -MainPlayerData app_data; - -/* update the clue items store */ -void update_clue_items () -{ - gtk_list_store_clear (across_store); - gtk_list_store_clear (down_store); - - /* if the puzzle is loaded */ - if (app_data.is_loaded == true) - { - /* iterate through the puzzle grid and gte the clues */ - for (int i = 0; i < app_data.puzzle.grid_size; i ++) - { - for (int j = 0; j < app_data.puzzle.grid_size; j ++) - { - /* if it is the start of an across word */ - if (app_data.puzzle.start_across_word[i][j] != -1) - { - GtkTreeIter iter; - - gtk_list_store_append (across_store, &iter); - gtk_list_store_set (across_store, &iter, 0, - app_data.puzzle.start_across_word[i][j], - 1, app_data.puzzle.clue_across[i][j], - -1); - } - /* if it is the start of a down word */ - if (app_data.puzzle.start_down_word[i][j] != -1) - { - GtkTreeIter iter; - gtk_list_store_append (down_store, &iter); - gtk_list_store_set (down_store, &iter, 0, - app_data.puzzle.start_down_word[i][j], - 1, app_data.puzzle.clue_down[i][j], - -1); - } - } - } - } -} - -/* slot for handling list down selection changed */ -gboolean on_down_list_selection_changed (GtkTreeSelection *sel, - GtkDrawingArea *area) -{ - GtkTreeIter iter; - GtkTreeModel* mod; - - if (gtk_tree_selection_get_selected (sel, &mod, &iter)) - { - guint sel_word_index; - gtk_tree_model_get (mod, &iter, 0, &sel_word_index, -1); - set_selection_to_word_start (&app_data, DOWN, sel_word_index); - } - - gtk_widget_queue_draw_area (GTK_WIDGET(area), 0, 0, - app_data.puzzle.grid_size*GRID_PIXELS+10, - app_data.puzzle.grid_size*GRID_PIXELS+10); - - return FALSE; - -} -/* slot for handling list across selection changed */ -gboolean on_across_list_selection_changed (GtkTreeSelection *sel, - GtkDrawingArea *area) -{ - GtkTreeIter iter; - GtkTreeModel* mod; - - if (gtk_tree_selection_get_selected (sel, &mod, &iter)) - { - guint sel_word_index; - gtk_tree_model_get (mod, &iter, 0, &sel_word_index, -1); - set_selection_to_word_start (&app_data, ACROSS, sel_word_index); - } - - gtk_widget_queue_draw_area (GTK_WIDGET(area), 0, 0, - app_data.puzzle.grid_size*GRID_PIXELS+10, - app_data.puzzle.grid_size*GRID_PIXELS+10); - - return FALSE; - -} - -/* slot for handling mouse button event for puzzle drawing area */ -gboolean on_puzzle_area_button_press_event (GtkWidget *widget, - GdkEventButton *event, gpointer data) -{ - /* if it is solution mode, then don't do anything but reset it to - non solution mode */ - if (app_data.solution_revealed == true) - { - app_data.solution_revealed = false; - gtk_widget_queue_draw_area (widget, 0, 0, - app_data.puzzle.grid_size*GRID_PIXELS+10, - app_data.puzzle.grid_size*GRID_PIXELS+10); - return FALSE; - } - - /* Depending on the type of button handle the movement */ - if (event->type == GDK_BUTTON_PRESS && event->button == 1) - { - int col = (event->x - 5) / GRID_PIXELS; - int row = (event->y - 5) / GRID_PIXELS; - - if (app_data.puzzle.chars[row][col] != '#') - { - if (row < app_data.puzzle.grid_size && - col < app_data.puzzle.grid_size) - { - app_data.cur_row = row; - app_data.cur_col = col; - - /* if it is a start of both across and down word, then - toggle the movement on clicking it */ - if (app_data.puzzle.start_across_word[row][col] != -1 && - app_data.puzzle.start_down_word[row][col] != -1) - { - if (app_data.current_movement == ACROSS) - app_data.current_movement = DOWN; - else - app_data.current_movement = ACROSS; - } - /* if it is only start of across word, make the current - movement across */ - else if (app_data.puzzle.start_across_word[row][col] != -1) - app_data.current_movement = ACROSS; - /* else down movement */ - else if (app_data.puzzle.start_down_word[row][col] != -1) - app_data.current_movement = DOWN; - } - } - } - - gtk_widget_queue_draw_area (widget, 0, 0, - app_data.puzzle.grid_size*GRID_PIXELS+10, - app_data.puzzle.grid_size*GRID_PIXELS+10); - - return FALSE; -} - -/* slot for handling key press event for puzzle drawing area */ -gboolean on_puzzle_area_key_press_event (GtkWidget *widget, - GdkEventKey *event, gpointer data) -{ - /* respond to key events only if the puzzle is loaded */ - if (app_data.is_loaded == true) - { - /* if the solution is revealed, don't respond to key events except - to return to the non-solution mode */ - if (app_data.solution_revealed == true) - { - app_data.solution_revealed = false; - gtk_widget_queue_draw_area (widget, 0, 0, - app_data.puzzle.grid_size*GRID_PIXELS+10, - app_data.puzzle.grid_size*GRID_PIXELS+10); - return FALSE; - } - - switch (event->keyval) - { - case GDK_KEY_Down : move_current_row (&app_data, DIR_FORWARD); - app_data.current_movement = DOWN; - break; - case GDK_KEY_Up : move_current_row (&app_data, DIR_BACK); - app_data.current_movement = DOWN; - break; - case GDK_KEY_Right: move_current_col (&app_data, DIR_FORWARD); - app_data.current_movement = ACROSS; - break; - case GDK_KEY_Left : move_current_col (&app_data, DIR_BACK); - app_data.current_movement = ACROSS; - break; - case GDK_KEY_BackSpace: - case GDK_KEY_Delete: - case GDK_KEY_space: - app_data.char_ans[app_data.cur_row][app_data.cur_col] = ' '; - break; - case GDK_KEY_a : - case GDK_KEY_A : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'A'; - break; - case GDK_KEY_b : - case GDK_KEY_B : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'B'; - break; - case GDK_KEY_c : - case GDK_KEY_C : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'C'; - break; - case GDK_KEY_d : - case GDK_KEY_D : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'D'; - break; - case GDK_KEY_e : - case GDK_KEY_E : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'E'; - break; - case GDK_KEY_f : - case GDK_KEY_F : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'F'; - break; - case GDK_KEY_g : - case GDK_KEY_G : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'G'; - break; - case GDK_KEY_h : - case GDK_KEY_H : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'H'; - break; - case GDK_KEY_i : - case GDK_KEY_I : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'I'; - break; - case GDK_KEY_j : - case GDK_KEY_J : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'J'; - break; - case GDK_KEY_k : - case GDK_KEY_K : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'K'; - break; - case GDK_KEY_l : - case GDK_KEY_L : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'L'; - break; - case GDK_KEY_m : - case GDK_KEY_M : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'M'; - break; - case GDK_KEY_n : - case GDK_KEY_N : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'N'; - break; - case GDK_KEY_o : - case GDK_KEY_O : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'O'; - break; - case GDK_KEY_p : - case GDK_KEY_P : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'P'; - break; - case GDK_KEY_q : - case GDK_KEY_Q : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'Q'; - break; - case GDK_KEY_r : - case GDK_KEY_R : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'R'; - break; - case GDK_KEY_s : - case GDK_KEY_S : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'S'; - break; - case GDK_KEY_t : - case GDK_KEY_T : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'T'; - break; - case GDK_KEY_u : - case GDK_KEY_U : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'U'; - break; - case GDK_KEY_v : - case GDK_KEY_V : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'V'; - break; - case GDK_KEY_w : - case GDK_KEY_W : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'W'; - break; - case GDK_KEY_x : - case GDK_KEY_X : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'X'; - break; - case GDK_KEY_y : - case GDK_KEY_Y : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'Y'; - break; - case GDK_KEY_z : - case GDK_KEY_Z : - app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'Z'; - break; - default : return FALSE; - - } - } - - /* move the selection pointer to next row/col as per the current - movement orientation if it is not a key left right up or down */ - if (event->keyval != GDK_KEY_Down && event->keyval != GDK_KEY_Up && - event->keyval != GDK_KEY_Left && event->keyval != GDK_KEY_Right - && event->keyval != GDK_KEY_Delete) - { - /* if the current movement is an across movement */ - if (app_data.current_movement == ACROSS) - { - /* if the next column is not blocking move the col */ - if (event->keyval != GDK_KEY_BackSpace && - next_col_block (&app_data.puzzle, app_data.cur_row, - app_data.cur_col) == false) - move_current_col (&app_data, DIR_FORWARD); - else if (event->keyval == GDK_KEY_BackSpace && - prev_col_block (&app_data.puzzle, app_data.cur_row, - app_data.cur_col) == false) - move_current_col (&app_data, DIR_BACK); - } - /* current movement is a up/down movement */ - else - { - if (event->keyval != GDK_KEY_BackSpace && - next_row_block (&app_data.puzzle, - app_data.cur_row, app_data.cur_col) == false) - move_current_row (&app_data, DIR_FORWARD); - else if (event->keyval == GDK_KEY_BackSpace && - prev_row_block (&app_data.puzzle, - app_data.cur_row, app_data.cur_col) == false) - move_current_row (&app_data, DIR_BACK); - } - } - - - gtk_widget_queue_draw_area (widget, 0, 0, - app_data.puzzle.grid_size*GRID_PIXELS+10, - app_data.puzzle.grid_size*GRID_PIXELS+10); - - return FALSE; -} - -/* slot for drawing the puzzle */ -gboolean on_puzzle_area_draw (GtkWidget *widget, cairo_t *cr, gpointer data) -{ - /* if a puzzle is loaded */ - if (app_data.is_loaded == true) - { - GdkRGBA colorfore, colorback, colorblue, colorbacksel1, - colorbacksel2, colorsolution; - gdk_rgba_parse (&colorfore, "#000000"); - gdk_rgba_parse (&colorback, "#ffffff"); - gdk_rgba_parse (&colorblue, "#0000dd"); - gdk_rgba_parse (&colorbacksel1, "#ffffaa"); - gdk_rgba_parse (&colorbacksel2, "#aaffff"); - gdk_rgba_parse (&colorsolution, "#990099"); - cairo_set_line_width (cr, 3); - - /* set the size of the drawing area */ - gtk_widget_set_size_request (widget, - app_data.puzzle.grid_size*GRID_PIXELS+10, - app_data.puzzle.grid_size*GRID_PIXELS+10); - - /* Draw the grid */ - for (int i = 0; i < app_data.puzzle.grid_size; i ++) - { - for (int j = 0; j < app_data.puzzle.grid_size; j ++) - { - /* if it is the current selection or if -1 is the current - selection then let the current selection be the first word */ - if (app_data.cur_col == -1 && app_data.cur_row == -1) - { - if (app_data.puzzle.start_across_word[i][j] == 1) - { - app_data.cur_row = i; - app_data.cur_col = j; - app_data.current_movement = ACROSS; - } - else if (app_data.puzzle.start_down_word[i][j] == 1) - { - app_data.cur_row = i; - app_data.cur_col = j; - app_data.current_movement = DOWN; - } - } - - gdk_cairo_set_source_rgba (cr, &colorfore); - cairo_rectangle (cr, j*GRID_PIXELS+5, i*GRID_PIXELS+5, - GRID_PIXELS, GRID_PIXELS); - - cairo_stroke (cr); - - /* if it is not a blank grid then set the background color - to black */ - if (app_data.puzzle.chars[i][j] != '#') - gdk_cairo_set_source_rgba (cr, &colorback); - - /* if it is a current selection and solution reveal mode is not - set then then set the background depending on whether across - or down movement mode is current */ - if (app_data.solution_revealed == false && - app_data.cur_row == i && app_data.cur_col == j) - { - if (app_data.current_movement == ACROSS) - gdk_cairo_set_source_rgba (cr, &colorbacksel1); - else - gdk_cairo_set_source_rgba (cr, &colorbacksel2); - } - - cairo_rectangle (cr, j*GRID_PIXELS+5, i*GRID_PIXELS+5, - GRID_PIXELS, GRID_PIXELS); - cairo_fill (cr); - - /* draw the word number if it is the start of a word */ - if (app_data.puzzle.start_across_word[i][j] != -1 || - app_data.puzzle.start_down_word[i][j] != -1) - { - int num; - if (app_data.puzzle.start_across_word[i][j] != -1) - num = app_data.puzzle.start_across_word[i][j]; - else - num = app_data.puzzle.start_down_word[i][j]; - - gdk_cairo_set_source_rgba (cr, &colorblue); - cairo_select_font_face (cr, "sans serif", - CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_WEIGHT_NORMAL); - cairo_set_font_size (cr, 11); - cairo_move_to (cr, j*GRID_PIXELS+7, i*GRID_PIXELS+15); - char cnum[3]; - sprintf (cnum, "%d", num); - cairo_show_text (cr, (const char*)cnum); - } - - /* if solution is not revealed set the color to normal - or set it to solution color */ - if (app_data.solution_revealed == false) - gdk_cairo_set_source_rgba (cr, &colorfore); - else - gdk_cairo_set_source_rgba (cr, &colorsolution); - - cairo_select_font_face (cr, "sans serif", - CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_WEIGHT_BOLD); - - cairo_set_font_size (cr, 16); - cairo_move_to (cr, j*GRID_PIXELS+GRID_PIXELS/2, - i*GRID_PIXELS+GRID_PIXELS-10); - char ctxt[3]; - /* if it is solution mode reveal the answer or else the - user input */ - if (app_data.solution_revealed == false) - sprintf (ctxt, "%c", app_data.char_ans[i][j]); - else - if (app_data.puzzle.chars[i][j] != '#') - sprintf (ctxt, "%c", app_data.puzzle.chars[i][j]); - else - sprintf (ctxt, "%c", ' '); - cairo_show_text (cr, (const char*) ctxt); - - } - } - } - - return FALSE; - -} - -/* slot for reveal solution menu */ -void on_menu_reveal_solution_activate (GtkMenuItem *item, GtkDrawingArea *area) -{ - /* if puzzle solution is password protected ask for the password */ - if (strlen (app_data.puzzle.hashed_solution_password) > 0) - { - GtkBuilder *builder; - builder = gtk_builder_new (); - - guint ret = gtk_builder_add_from_resource - (builder, - "/org/harishankar/wordblox/wordblox_player.glade", - NULL); - if (ret == 0) - { - fprintf (stderr, ERROR_WINDOW); - g_object_unref (builder); - return; - } - - GtkWidget *password_dialog = GTK_WIDGET (gtk_builder_get_object - (builder, "password_dialog")); - GtkWidget *password_text = GTK_WIDGET (gtk_builder_get_object - (builder, "password_text")); - - if (password_dialog == NULL) - { - fprintf (stderr, ERROR_WINDOW); - g_object_unref (builder); - return; - } - gtk_window_set_transient_for (GTK_WINDOW(password_dialog), - GTK_WINDOW(main_window)); - gtk_dialog_set_default_response (GTK_DIALOG(password_dialog), - GTK_RESPONSE_ACCEPT); - gint res = gtk_dialog_run (GTK_DIALOG (password_dialog)); - if (res == GTK_RESPONSE_ACCEPT) - { - const gchar *user_pwd = gtk_entry_get_text - (GTK_ENTRY(password_text)); - /* if password is correct */ - if (verify_solution_password (&app_data.puzzle, user_pwd) == true) - app_data.solution_revealed = true; - /* password is incorrect */ - else - { - app_data.solution_revealed = false; - GtkWidget *errordlg ; - errordlg = gtk_message_dialog_new (GTK_WINDOW(main_window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, - WRONG_PASSWORD); - gtk_dialog_run (GTK_DIALOG(errordlg)); - gtk_widget_destroy (errordlg); - } - } - - gtk_widget_destroy (password_text); - gtk_widget_destroy (password_dialog); - g_object_unref (builder); - } - else - app_data.solution_revealed = true; - - gtk_widget_queue_draw_area (GTK_WIDGET(area), 0, 0, - app_data.puzzle.grid_size*GRID_PIXELS+10, - app_data.puzzle.grid_size*GRID_PIXELS+10); - -} - -/* slot for load grid state menu */ -void on_menu_load_grid_state_activate (GtkMenuItem *item, GtkDrawingArea *area) -{ - GtkWidget *dialog; - GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; - gint res; - dialog = gtk_file_chooser_dialog_new (OPEN_FILE, GTK_WINDOW (main_window), - action, - "_Cancel", - GTK_RESPONSE_CANCEL, - "_Open", - GTK_RESPONSE_ACCEPT, - NULL - ); - - res = gtk_dialog_run (GTK_DIALOG (dialog)); - if (res == GTK_RESPONSE_ACCEPT) - { - char *filename; - filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); - load_user_data (&app_data, filename); - g_free (filename); - } - - gtk_widget_destroy (dialog); - - gtk_widget_queue_draw_area (GTK_WIDGET(area), 0, 0, - app_data.puzzle.grid_size*GRID_PIXELS+10, - app_data.puzzle.grid_size*GRID_PIXELS+10); - - update_clue_items (); -} - -/* slot for save grid state menu */ -void on_menu_save_grid_state_activate (GtkMenuItem *item, gpointer *data) -{ - if (app_data.is_loaded == false) - return; - - GtkWidget *dialog; - GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE; - gint res; - dialog = gtk_file_chooser_dialog_new (SAVE_FILE, GTK_WINDOW(main_window), - action, - "_Cancel", - GTK_RESPONSE_CANCEL, - "_Save", - GTK_RESPONSE_ACCEPT, - NULL); - res = gtk_dialog_run (GTK_DIALOG (dialog)); - if (res == GTK_RESPONSE_ACCEPT) - { - char *filename; - filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); - save_user_data (&app_data, filename); - g_free (filename); - } - - gtk_widget_destroy (dialog); -} - -/* slot for exit menu */ -void on_menu_exit_activate (GtkMenuItem *item, gpointer data) -{ - gtk_main_quit (); -} - -/* slot for open menu */ -void on_menu_open_activate (GtkMenuItem *item, GtkDrawingArea* area) -{ - GtkWidget *dialog; - GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; - gint res; - dialog = gtk_file_chooser_dialog_new (OPEN_FILE, GTK_WINDOW(main_window), - action, - "_Cancel", - GTK_RESPONSE_CANCEL, - "_Open", - GTK_RESPONSE_ACCEPT, - NULL); - res = gtk_dialog_run (GTK_DIALOG (dialog)); - if (res == GTK_RESPONSE_ACCEPT) - { - char *filename; - filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog)); - MainPlayerData temp; - reset_player_data (&temp, filename); - - /* if the grid is not frozen then the game cannot be played */ - if (temp.is_loaded == false) - { - GtkWidget *errordlg ; - errordlg = gtk_message_dialog_new (GTK_WINDOW(main_window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, - UNFROZEN_GRID_PLAYER); - gtk_dialog_run (GTK_DIALOG(errordlg)); - gtk_widget_destroy (errordlg); - } - else - { - app_data = temp; - gtk_widget_queue_draw_area (GTK_WIDGET (area), 0, 0, - app_data.puzzle.grid_size*30+10, - app_data.puzzle.grid_size*30+10); - - } - update_clue_items (); - g_free (filename); - } - - gtk_widget_destroy (dialog); -} - -/* slot for about menu */ -void on_menu_about_activate (GtkMenuItem *item, gpointer data) -{ - const char *AUTHOR[] = {"V.Harishankar", NULL}; - gtk_show_about_dialog (GTK_WINDOW(main_window), "authors",AUTHOR, - "program-name", PROGRAM_NAME, - "copyright", COPYRIGHT, - "comments", COMMENTS, - "website", WEBSITE, - "website-label", WEBSITE_LABEL, - "license-type", GTK_LICENSE_GPL_2_0, - "version", VERSION, - (char*)NULL); -} - -int main (int argc, char *argv []) -{ - gtk_init (&argc, &argv); - GdkPixbuf *icon; - icon = gdk_pixbuf_new_from_resource - ("/org/harishankar/wordblox/wordblox.svg", NULL); - if (icon == NULL) - fprintf (stderr, ERROR_ICON); - - GtkBuilder *builder; - builder = gtk_builder_new (); - guint ret = gtk_builder_add_from_resource (builder, - "/org/harishankar/wordblox/wordblox_player.glade", NULL); - - app_data.is_loaded = false; - - if (ret == 0) - { - fprintf (stderr, ERROR_WINDOW); - g_object_unref (builder); - return 1; - } - else - { - main_window = GTK_WIDGET (gtk_builder_get_object (builder, - "main_window")); - - across_store = GTK_LIST_STORE (gtk_builder_get_object - (builder, "store_across_clues")); - down_store = GTK_LIST_STORE (gtk_builder_get_object - (builder, "store_down_clues")); - - if (main_window != NULL) - { - gtk_window_set_default_icon (icon); - - GtkWidget *draw_area = GTK_WIDGET ( - gtk_builder_get_object(builder, "puzzle_area")); - - /* make drawing area respond to mouse event */ - gtk_widget_set_events(draw_area, - gtk_widget_get_events(draw_area) - | GDK_BUTTON_PRESS_MASK); - - gtk_builder_connect_signals (builder, NULL); - g_object_unref (builder); - gtk_widget_show (main_window); - gtk_main (); - return 0; - } - else - { - g_object_unref (builder); - fprintf (stderr, ERROR_WINDOW); - return 1; - } - } -} diff --git a/wordblox_player.glade b/wordblox_player.glade deleted file mode 100644 index dc20b5b..0000000 --- a/wordblox_player.glade +++ /dev/null @@ -1,417 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - False - Wordblox Player - 540 - 400 - - - - - - - True - False - vertical - - - True - False - - - True - False - _Puzzle - True - - - True - False - - - True - False - _Open... - True - - - - - - True - False - - - - - True - False - E_xit - True - - - - - - - - - - True - False - _Grid - True - - - True - False - - - True - False - _Save Grid State... - True - - - - - - True - False - _Load Grid State... - True - - - - - - - - - - True - False - _View - True - - - True - False - - - True - False - _Reveal Solution... - True - - - - - - - - - - True - False - _Help - True - - - True - False - - - True - False - _About... - True - - - - - - - - - - False - True - 0 - - - - - True - True - - - 80 - 80 - True - True - in - - - True - False - natural - natural - - - True - True - True - True - True - - - - - - - - - - True - True - - - - - True - False - vertical - - - True - False - in - - - True - False - store_across_clues - False - 0 - False - - - - - - - - True - Id - - - - 0 - - - - - - - True - Clues Across - - - - 1 - 1 - - - - - - - - - True - True - - - - - True - False - in - - - True - False - store_down_clues - False - 0 - False - - - - - - - - True - Id - - - - 0 - - - - - - - True - Clues Down - - - - 1 - - - - - - - - - True - True - - - - - True - True - - - - - True - True - 1 - - - - - - - False - Password - False - True - dialog - main_window - - - - - - False - 10 - 10 - 10 - 10 - vertical - 2 - - - False - end - - - gtk-cancel - True - True - False - True - True - - - True - True - 0 - - - - - gtk-ok - True - True - True - False - True - True - - - True - True - 1 - - - - - False - False - 0 - - - - - True - False - Password for revealing solution - - - False - True - 1 - - - - - True - True - 256 - False - ● - True - password - - - False - True - 3 - - - - - - button1 - button2 - - -