X-Git-Url: https://jxself.org/git/?p=open-adventure.git;a=blobdiff_plain;f=misc.c;h=61d80d888b56246fa6b480f3679057a15ce4bb6f;hp=b01662f5120f4739cea47623b433c7e15ae58cc2;hb=a4de450bfaf2c600dccd9a5b3625c5a3ca279e7c;hpb=94aca03203cbba94c25fba00b7ff80e529d275c7 diff --git a/misc.c b/misc.c index b01662f..61d80d8 100644 --- a/misc.c +++ b/misc.c @@ -11,6 +11,18 @@ #include "linenoise/linenoise.h" #include "newdb.h" +char* xstrdup(const char* s) +{ + char* ptr = strdup(s); + if (ptr == NULL) { + // LCOV_EXCL_START + // exclude from coverage analysis because we can't simulate an out of memory error in testing + fprintf(stderr, "Out of memory!\n"); + exit(EXIT_FAILURE); + } + return(ptr); +} + void* xmalloc(size_t size) { void* ptr = malloc(size); @@ -28,8 +40,8 @@ void packed_to_token(long packed, char token[6]) { // Unpack and map back to ASCII. for (int i = 0; i < 5; ++i) { - char advent = (packed >> i * 6) & 63; - token[4 - i] = advent_to_ascii[(int) advent]; + char advent = (packed >> i * 6) & 63; + token[i] = new_advent_to_ascii[(int) advent]; } // Ensure the last character is \0. @@ -44,6 +56,55 @@ void packed_to_token(long packed, char token[6]) } } +long token_to_packed(const char token[6]) +{ + size_t t_len = strlen(token); + long packed = 0; + for (size_t i = 0; i < t_len; ++i) + { + char mapped = new_ascii_to_advent[(int) token[i]]; + packed |= (mapped << (6 * i)); + } + return(packed); +} + +void tokenize(char* raw, long tokens[4]) +{ + // set each token to 0 + for (int i = 0; i < 4; ++i) + tokens[i] = 0; + + // grab the first two words + char* words[2]; + words[0] = (char*) xmalloc(strlen(raw)); + words[1] = (char*) xmalloc(strlen(raw)); + int word_count = sscanf(raw, "%s%s", words[0], words[1]); + + // make space for substrings and zero it out + char chunk_data[][6] = { + {"\0\0\0\0\0"}, + {"\0\0\0\0\0"}, + {"\0\0\0\0\0"}, + {"\0\0\0\0\0"}, + }; + + // break the words into up to 4 5-char substrings + sscanf(words[0], "%5s%5s", chunk_data[0], chunk_data[1]); + if (word_count == 2) + sscanf(words[1], "%5s%5s", chunk_data[2], chunk_data[3]); + free(words[0]); + free(words[1]); + + // uppercase all the substrings + for (int i = 0; i < 4; ++i) + for (unsigned int j = 0; j < strlen(chunk_data[i]); ++j) + chunk_data[i][j] = (char) toupper(chunk_data[i][j]); + + // pack the substrings + for (int i = 0; i < 4; ++i) + tokens[i] = token_to_packed(chunk_data[i]); +} + /* Hide the fact that wods are corrently packed longs */ bool wordeq(token_t a, token_t b) @@ -166,7 +227,7 @@ void pspeak(vocab_t msg, enum speaktype mode, int skip, ...) vspeak(objects[msg].inventory, ap); break; case look: - vspeak(objects[msg].longs[skip], ap); + vspeak(objects[msg].descriptions[skip], ap); break; case hear: vspeak(objects[msg].sounds[skip], ap); @@ -174,6 +235,9 @@ void pspeak(vocab_t msg, enum speaktype mode, int skip, ...) case study: vspeak(objects[msg].texts[skip], ap); break; + case change: + vspeak(objects[msg].changes[skip], ap); + break; } va_end(ap); } @@ -233,6 +297,23 @@ void echo_input(FILE* destination, char* input_prompt, char* input) free(prompt_and_input); } +int word_count(char* s) +{ + char* copy = xstrdup(s); + char delims[] = " \t"; + int count = 0; + char* word; + + word = strtok(copy, delims); + while(word != NULL) + { + word = strtok(NULL, delims); + ++count; + } + free(copy); + return(count); +} + char* get_input() { // Set up the prompt @@ -252,8 +333,13 @@ char* get_input() input = NULL; size_t n = 0; if (isatty(0)) + // LCOV_EXCL_START + // Should be unreachable in tests, as they will use a non-interactive shell. printf("%s", input_prompt); - IGNORE(getline(&input, &n, stdin)); + // LCOV_EXCL_STOP + ssize_t numread = getline(&input, &n, stdin); + if (numread == -1) // Got EOF; return with it. + return(NULL); } if (input == NULL) // Got EOF; return with it. @@ -278,6 +364,48 @@ char* get_input() return (input); } +bool silent_yes() +{ + char* reply; + bool outcome; + + for (;;) { + reply = get_input(); + if (reply == NULL) { + // LCOV_EXCL_START + // Should be unreachable. Reply should never be NULL + linenoiseFree(reply); + exit(EXIT_SUCCESS); + // LCOV_EXCL_STOP + } + + char* firstword = (char*) xmalloc(strlen(reply)+1); + sscanf(reply, "%s", firstword); + + for (int i = 0; i < (int)strlen(firstword); ++i) + firstword[i] = tolower(firstword[i]); + + int yes = strncmp("yes", firstword, sizeof("yes") - 1); + int y = strncmp("y", firstword, sizeof("y") - 1); + int no = strncmp("no", firstword, sizeof("no") - 1); + int n = strncmp("n", firstword, sizeof("n") - 1); + + free(firstword); + + if (yes == 0 || y == 0) { + outcome = true; + break; + } else if (no == 0 || n == 0) { + outcome = false; + break; + } else + rspeak(PLEASE_ANSWER); + } + linenoiseFree(reply); + return (outcome); +} + + bool yes(const char* question, const char* yes_response, const char* no_response) /* Print message X, wait for yes/no answer. If yes, print Y and return true; * if no, print Z and return false. */ @@ -289,10 +417,13 @@ bool yes(const char* question, const char* yes_response, const char* no_response speak(question); reply = get_input(); - if (reply == NULL) { - linenoiseFree(reply); - exit(EXIT_SUCCESS); - } + if (reply == NULL) { + // LCOV_EXCL_START + // Should be unreachable. Reply should never be NULL + linenoiseFree(reply); + exit(EXIT_SUCCESS); + // LCOV_EXCL_STOP + } char* firstword = (char*) xmalloc(strlen(reply)+1); sscanf(reply, "%s", firstword); @@ -308,7 +439,7 @@ bool yes(const char* question, const char* yes_response, const char* no_response free(firstword); if (yes == 0 || y == 0) { - speak(yes_response); + speak(yes_response); outcome = true; break; } else if (no == 0 || n == 0) { @@ -411,7 +542,7 @@ long vocab(long id, long init) lexeme = -1; if (init < 0) return (lexeme); - BUG(REQUIRED_VOCABULARY_WORD_NOT_FOUND); + BUG(REQUIRED_VOCABULARY_WORD_NOT_FOUND); // LCOV_EXCL_LINE } if (init >= 0 && KTAB[i] / 1000 != init) continue; @@ -422,7 +553,95 @@ long vocab(long id, long init) return (lexeme); } } - BUG(RAN_OFF_END_OF_VOCABULARY_TABLE); + BUG(RAN_OFF_END_OF_VOCABULARY_TABLE); // LCOV_EXCL_LINE +} + +int get_motion_vocab_id(const char* word) +// Return the first motion number that has 'word' as one of its words. +{ + for (int i = 0; i < NMOTIONS; ++i) + { + for (int j = 0; j < motions[i].words.n; ++j) + { + if (strcasecmp(word, motions[i].words.strs[j]) == 0) + return(i); + } + } + // If execution reaches here, we didn't find the word. + return(WORD_NOT_FOUND); +} + +int get_object_vocab_id(const char* word) +// Return the first object number that has 'word' as one of its words. +{ + for (int i = 0; i < NOBJECTS + 1; ++i) // FIXME: the + 1 should go when 1-indexing for objects is removed + { + for (int j = 0; j < objects[i].words.n; ++j) + { + if (strcasecmp(word, objects[i].words.strs[j]) == 0) + return(i); + } + } + // If execution reaches here, we didn't find the word. + return(WORD_NOT_FOUND); +} + +int get_action_vocab_id(const char* word) +// Return the first motion number that has 'word' as one of its words. +{ + for (int i = 0; i < NACTIONS; ++i) + { + for (int j = 0; j < actions[i].words.n; ++j) + { + if (strcasecmp(word, actions[i].words.strs[j]) == 0) + return(i); + } + } + // If execution reaches here, we didn't find the word. + return(WORD_NOT_FOUND); +} + +int get_special_vocab_id(const char* word) +// Return the first special number that has 'word' as one of its words. +{ + for (int i = 0; i < NSPECIALS; ++i) + { + for (int j = 0; j < specials[i].words.n; ++j) + { + if (strcasecmp(word, specials[i].words.strs[j]) == 0) + return(i); + } + } + // If execution reaches here, we didn't find the word. + return(WORD_NOT_FOUND); +} + +long get_vocab_id(const char* word) +// Search the vocab categories in order for the supplied word. +{ + long ref_num; + + ref_num = get_motion_vocab_id(word); + if (ref_num != WORD_NOT_FOUND) + return(ref_num + 0); // FIXME: replace with a proper hash + + ref_num = get_object_vocab_id(word); + if (ref_num != WORD_NOT_FOUND) + return(ref_num + 1000); // FIXME: replace with a proper hash + + ref_num = get_action_vocab_id(word); + if (ref_num != WORD_NOT_FOUND) + return(ref_num + 2000); // FIXME: replace with a proper hash + + ref_num = get_special_vocab_id(word); + if (ref_num != WORD_NOT_FOUND) + return(ref_num + 3000); // FIXME: replace with a proper hash + + // Check for the reservoir magic word. + if (strcasecmp(word, game.zzword) == 0) + return(PART + 2000); // FIXME: replace with a proper hash + + return(WORD_NOT_FOUND); } void juggle(long object) @@ -529,7 +748,7 @@ long atdwrf(long where) long setbit(long bit) /* Returns 2**bit for use in constructing bit-masks. */ { - return (1 << bit); + return (1L << bit); } bool tstbit(long mask, int bit) @@ -586,6 +805,15 @@ long rndvoc(long second, long force) return rnd; } +void make_zzword(char zzword[6]) +{ + for (int i = 0; i < 5; ++i) + { + zzword[i] = 'A' + randrange(26); + } + zzword[1] = '\''; // force second char to apostrophe + zzword[5] = '\0'; +} /* Machine dependent routines (MAPLIN, SAVEIO) */ @@ -690,10 +918,12 @@ void datime(long* d, long* t) *t = (long) tv.tv_usec; } +// LCOV_EXCL_START void bug(enum bugtype num, const char *error_string) { fprintf(stderr, "Fatal error %d, %s.\n", num, error_string); exit(EXIT_FAILURE); } +// LCOV_EXCL_STOP /* end */