diff options
Diffstat (limited to 'main.c')
-rw-r--r-- | main.c | 304 |
1 files changed, 304 insertions, 0 deletions
@@ -0,0 +1,304 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "strarr.h" + +/* eow: guess the last three letters of a set of words. + e.g. ti___, man___, cas___: the player must guess `tle`. + + How to do + - Eliminate all words less than NUMBER_OF_CHARS + 1 chars. + - Then somehow search for common endings...? + + for (word in words) + ending = get ending + for (word in remaining words) + + 10 => 10 + 9 + 8 + 7 ... + For 370k words, that is 68 bil. That will take seconds. + + I think, since this is a simple game, I will just pick + out words at random. In other words, randomly index + into the array (possibly costly in this structure)... + I think add a function to generate interstitial indexes + and perform random (not: linear from start-to-end) + indexes. + Then, iterate over the entire list (subsecond) and + find ones which share the same ending. Use those + for the game. + +TODO + + Finish logic to accept a guess. + + Add flags to change settings. + - Make number of guesses configurable. + + Add option to get a hint, which gives away one of the letters. + (When it does this, it reveals the letter in the actual word displayed.) + - Figure out bug that when you run /hint, it for some reason + goes prints a line up, swallowing the topmost line? + - Add in /set commands. + */ + +int MIN_LENGTH = 6; +int NUMBER_OF_CHARS = 3; +int NUMBER_OF_WORDS = 3; +int NUMBER_OF_TRIES = 100; + +/* Get the first entry from the words as a random index. */ +char * +random_index(strarr *words_arr, int min_length) +{ + for (;;) { + int idx = rand() % words_arr->len; + char *w = strarr_random_index(words_arr, idx); + if (strlen(w) < min_length) { + continue; + } + + return w; + } +} + +/* Get strings matching the ending of the first word. + Caller must free the array but not the members. + Returns NULL if the allocation fails, or there + are insufficient matching strings for the entry + provided. +*/ +const char ** +get_matching_strs(strarr *words_arr, const char *first) +{ + // The `-1` is due to the first word already being chosen. + const char **arr = calloc(sizeof(char*), NUMBER_OF_WORDS - 1); + if (!arr) { + return NULL; + } + + /* Get ending. */ + const char *ending = first + (strlen(first) - NUMBER_OF_CHARS); + + /* Iterate over words and find NUMBER_OF_WORDS entries with + this ending. + */ + char *p; + int i; + for (i = 0, p = strarr_index(words_arr, 0); p != NULL; p = strarr_next(words_arr, p)) { + size_t sz = strlen(p); + if (sz < MIN_LENGTH + || strcmp((p + (sz - NUMBER_OF_CHARS)), ending) != 0 + || strcmp(p, first) == 0) { + continue; + } + + /* If it's passed the check, we know it is the right length, and has the correct + ending: add it to the list, check if we've got all the words, quit if so. + */ + arr[i++] = p; + if (i == NUMBER_OF_WORDS - 1) { + return arr; + } + } + + /* If we managed to break out of the loop, it means we've iterated over the + entire array and not found enough words. So return NULL to signify error. + */ + free(arr); + return NULL; +} + +void +print_n_uscore(int i) { + for (int rep = 0; rep < i; ++rep) { + printf("_"); + } +} + +void +display_prompt(int totalpoints, int points, const char *first, const char **others, const char *hinted_ending, int guesses) +{ + printf("Points: %d Points available: %d\n", totalpoints, points); + printf("Your words are: \n"); + printf(" %.*s", (int)strlen(first) - NUMBER_OF_CHARS, first); + printf("%s", hinted_ending); + printf("\n"); + + for (int i = 0; i < NUMBER_OF_WORDS - 1; ++i) { + printf(" %.*s", (int)strlen(others[i]) - NUMBER_OF_CHARS, others[i]); + printf("%s", hinted_ending); + printf("\n"); + } + printf("What do you think the word is (incorrect guesses: %d): ", guesses); +} + +void +clear_prompt() +{ + static int is_first_call = 1; + if (is_first_call) { + /* do nothing */ + is_first_call = 0; + return; + } + int lines_displayed = 3 + (NUMBER_OF_WORDS - 1) + 1 + 1 + 1; + + for (int c = 0; c < lines_displayed - 1; ++c) { + printf(" "); + printf("\033[F"); + } +} + +int +ask(const char *word, const char **others, int totalpoints) +{ + const char *ending = word + strlen(word) - NUMBER_OF_CHARS; + char *hinted_ending = malloc(strlen(ending)); + for (int i = 0; i < strlen(ending); ++i) { + hinted_ending[i] = '_'; + } + hinted_ending[strlen(ending)] = '\0'; + + printf("Type /help for help, or /hint, or /giveup, or /quit to quit.\n"); + int hinted_to = 0; + int guesses = 0; + int points = 5; + for (;;) { + /* Display the words. */ + clear_prompt(); + display_prompt(totalpoints, points, word, others, hinted_ending, guesses); + + /* Get input. */ + char ui[256]; + scanf("%s", ui); /* TODO: add safety to check len(input) < 256 */ + + if (!strcmp(ui, ending)) { + break; + } else if (!strcmp(ui, "/help")) { + /* TODO add help */ + } else if (!strcmp(ui, "/hint")) { + if (hinted_to == NUMBER_OF_CHARS - 1) { + continue; /* do not give hint as hints are exhausted*/ + } + hinted_ending[hinted_to] = ending[hinted_to]; + hinted_to++; + if (--points == 0) { + points = 1; + } + } else if (!strcmp(ui, "/giveup")) { + points = -1; + break; + } else if (!strcmp(ui, "/quit")) { + exit(0); + } else { + guesses++; + if (--points == 0) { + points = 1; + } + continue; + } + } + + return points; +} + +void +run(strarr *words) +{ + int points = 0; + for (;;) { + /* Generate the first word, guaranteed to be at least the min length. */ + const char *w = random_index(words, MIN_LENGTH); + + /* Find two words with matching endings - for now, just grab the first + NUMBER_OF_WORDS entries we find, but... + TODO: add logic to get more than just the first two entries. + */ + const char **others = NULL; + /* Continually retries until a valid set found. If no valid words found + after NUMBER_OF_TRIES attempts, just panic and die. */ + int i = 0; + while (i++ < NUMBER_OF_TRIES && !others) { + others = get_matching_strs(words, w); + } + + if (!others) { + // printf("No words matching the ending of %s - retrying!\n", w); + continue; + } + + /* Ask the user what the suffix is. */ + points += ask(w, others, points); + free(others); + } +} + +int main(int argc, char *argv[]) +{ + /* Setup. */ + srand(time(NULL)); + + /* Parse arguments. */ + for (int i = 0; i < argc; ++i) { + if (argv[i][0] == '-') { + char f = argv[i][1]; + switch (f) { + case 'l': + MIN_LENGTH = strtol(argv[++i], NULL, 10); + if (MIN_LENGTH < 2) { + fprintf(stderr, "-l flag invalid\n"); + exit(1); + } + break; + + case 'c': + NUMBER_OF_CHARS = strtol(argv[++i], NULL, 10); + if (NUMBER_OF_CHARS < 1) { + fprintf(stderr, "-c flag invalid\n"); + exit(2); + } + break; + case 'n': + NUMBER_OF_WORDS = strtol(argv[++i], NULL, 10); + if (NUMBER_OF_WORDS < 2) { + fprintf(stderr, "-n flag invalid\n"); + exit(3); + } + break; + case 't': + NUMBER_OF_TRIES = strtol(argv[++i], NULL, 10); + if (NUMBER_OF_TRIES < 2) { + fprintf(stderr, "-t flag invalid\n"); + exit(4); + } + break; + } + } + } + + if (MIN_LENGTH <= NUMBER_OF_CHARS) { + fprintf(stderr, "min length of word (-l) cannot be less than number of characters missing (-c)\n"); + exit(5); + } + + /* Read contents of file. */ + FILE *fp = fopen("./words.txt", "r"); + fseek(fp, 0, SEEK_END); + long fsize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + char *words = malloc(fsize + 1); + fread(words, fsize, 1, fp); + fclose(fp); + words[fsize] = 0; + + /* Convert into an array. */ + // size_t words_sz = 0; + strarr words_arr; + strarr_init_from_str_split(&words_arr, words, '\n'); + + /* Run the game. */ + run(&words_arr); + + strarr_free(&words_arr); + return 0; +} |