From c927e6496d0c307a76bec349b90d2a99fe83813b Mon Sep 17 00:00:00 2001 From: bePrivate Date: Sat, 28 Oct 2023 19:45:39 +0200 Subject: [PATCH] initial commit --- LICENSE | 21 +++ Makefile | 10 ++ README.md | 22 ++++ lib/wordlist | 101 +++++++++++++++ main | Bin 0 -> 21816 bytes src/main.c | 358 +++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 512 insertions(+) create mode 100755 LICENSE create mode 100755 Makefile create mode 100755 README.md create mode 100755 lib/wordlist create mode 100755 main create mode 100644 src/main.c diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..c5332dd --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 mnjx + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..7ffa99f --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +first: + gcc src/main.c -o main + +install: + echo "alias wordle='$(realpath build/main)'" >> ~/.bashrc + echo "WORDLE_PATH='$(realpath build/main)'" >> ~/.bashrc + export WORDLE_PATH + source ~/.bashrc +run: + ./build/main diff --git a/README.md b/README.md new file mode 100755 index 0000000..f0aee3d --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# Wordle + +Yet another recreation of the game Wordle (now in C) + +# Usage + +1. Clone the repo +`git clone https://gitlab.com/bePrivate/wordlec` + +2. Change into the cloned directory +`cd wordlec` + +3. Start the game +`make && make run` + +# How to play + +After starting, you are prompted to enter a word. When you enter it, you will see it on +the screen. You will see whether each letter is at the correct place or not, or if it +even is in the word in the first place. You have six tries, then you lose and the correct +word is shown to you. If you win, the number of guesses is displayed, along with the +correct word. diff --git a/lib/wordlist b/lib/wordlist new file mode 100755 index 0000000..62e0710 --- /dev/null +++ b/lib/wordlist @@ -0,0 +1,101 @@ +crane +stove +house +hound +apple +spear +blood +river +ocean +table +chair +phone +bible +cable +trash +juice +paper +stack +queue +graph +earth +gnome +linux +green +black +white +brown +arrow +space +bread +break +drink +board +floor +glass +latex +fever +cough +metro +bingo +cloth +clock +proof +range +brick +shark +ingot +crate +crook +woman +image +timer +coast +plant +stone +horse +mouse +wheel +knife +towel +tower +rover +cover +shirt +video +audio +clone +virus +vodka +sirup +water +plane +plain +photo +radio +berry +shell +guess +paint +screw +frame +place +plank +curve +plate +spoon +mouth +onion +route +issue +start +three +seven +eight +crypt +queen +price +token +night +rebel +crown diff --git a/main b/main new file mode 100755 index 0000000000000000000000000000000000000000..01aff174472152f70883511fec0d18e03c8815bd GIT binary patch literal 21816 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 Yqr<6kl`IX{Qu +#include +#include +#include +#include + +#define RESET "\x1b[0m" // color 0 +#define RED "\x1b[31m" // color 1 +#define GREEN "\x1b[32m" // color 2 +#define YELLOW "\x1b[33m" // color 3 + +char wordlist_path[1024]; +char wordlist[16384][6]; // 2^14 +int len_wordlist = 0; +int flag_verbose = 0; +int flag_worldlist = 0; +char playfield[6][5] = { + { ' ', ' ', ' ', ' ', ' ' }, + { ' ', ' ', ' ', ' ', ' ' }, + { ' ', ' ', ' ', ' ', ' ' }, + { ' ', ' ', ' ', ' ', ' ' }, + { ' ', ' ', ' ', ' ', ' ' }, + { ' ', ' ', ' ', ' ', ' ' } +}; +char pf_props[6][5][2] = { + // + // letter 0, 1, 2 or 3 where + // 0 (RESET) is not yet guessed + // 1 (RED) is out-of-place, + // 2 (GREEN) is correct and + // 3 (YELLOW) is incorrect + + { " 0", " 0", " 0", " 0", " 0" }, + { " 0", " 0", " 0", " 0", " 0" }, + { " 0", " 0", " 0", " 0", " 0" }, + { " 0", " 0", " 0", " 0", " 0" }, + { " 0", " 0", " 0", " 0", " 0" }, + { " 0", " 0", " 0", " 0", " 0" } +}; +char word_to_guess[5]; +int turns = 0; +int win = 0; +int lose = 0; +int quit = 0; + + +/* + tests if a file exists + return 0 - yes + return 1 - no +*/ +int file_exists(char path[]) { + if (access(path, F_OK) != 0) { + return 0; + } else { + return 1; + } +} + +/* + prints the help & usage for the user to know what to do +*/ +void print_help_and_usage() { + fprintf(stdout, + "Help and usage for Wordle:\n" + "\n" + "Help:\n" + "--wordlist ... specify a custom wordlist\n" + " RULES: five characters on each line (no empty lines),\n" + " each new line is separated by '\\n' (Enter/Return)\n" + "--verbose ............................ be verbose (show what the program is doing aka debug info)\n" + "--help ............................... show this help screen\n" + "\n" + "Usage:\n" + "\n" + "wordle\n" + "=> starts Wordle with the default wordlist\n" + "wordle --wordlist \n" + "=> starts Wordle with a specified wordlist\n" + ); +} + +/* + gets a random word from the given wordlist +*/ +void get_random_word(char word_to_guess[], char wordlist_path[]) { + FILE* wordlist_file = fopen(wordlist_path, "r"); + char ch; + int i = 0; + char temp_word[6]; + while ((ch = fgetc(wordlist_file)) != EOF) { + if (ch != '\n') { + temp_word[i] = ch; + i++; + } else { + temp_word[i] = '\0'; + i = 0; + strcpy(wordlist[len_wordlist], temp_word); + len_wordlist++; + } + } + + srand(time(NULL)); + + strcpy(word_to_guess, wordlist[rand() % len_wordlist]); +} + +/* + clears the console/screen +*/ +void clear() { + printf("\e[1;1H\e[2J"); +} + +/* + prints the current state of the game + shows + correct letters + incorrect letters + and out-of-place letters + also the previous attempts +*/ +void show() { + clear(); + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 5; j++) { + switch (pf_props[i][j][1]) { + case '0': + printf("| %c ", playfield[i][j]); + break; + case '1': + printf("| " RED "%c " RESET, playfield[i][j]); + break; + case '2': + printf("| " GREEN "%c " RESET, playfield[i][j]); + break; + case '3': + printf("| " YELLOW "%c " RESET, playfield[i][j]); + break; + } + } + printf("|\n"); + } + printf("\n"); +} + +/* + prompt the user for their input +*/ +void prompt(char user_input[]) { + printf(">> "); + scanf("%5s", user_input); +} + +/* + linear search helper function for searching within the wordlist +*/ +int search_wordlist(char wordlist[][6], char target[]) { + for (int i = 0; i < len_wordlist; i++) { + if (strcmp(wordlist[i], target) == 0) return i; + } + return -1; +} + +/* + helper function for searching within a string +*/ +int search_word(char word[], char target) { + for (int i = 0; i < sizeof(word)/sizeof(word[0]); i++) { + if (word[i] == target) return i; + } + return -1; +} + +/* + evaluate the loss +*/ +void eval_lose() { + if (turns >= 6) { + lose = 1; + } +} + +/* + evaluate the win +*/ +void eval_win(int i) { + if (strcmp(wordlist[i], word_to_guess) == 0) { + win = 1; + } +} + +/* + helper function for evaluating the properties of a word +*/ +void eval_props(char word[]) { + for (int i = 0; i < 5; i++) { + if (word_to_guess[i] == word[i]) { + pf_props[turns][i][0] = word[i]; + pf_props[turns][i][1] = '2'; + } else if (search_word(word_to_guess, word[i]) != -1 && word_to_guess[i] != word[i]) { + pf_props[turns][i][0] = word[i]; + pf_props[turns][i][1] = '3'; + } else if (search_word(word_to_guess, word[i]) == -1) { + pf_props[turns][i][0] = word[i]; + pf_props[turns][i][1] = '1'; + } + } +} + +/* + put a valid word into the playfield +*/ +void put_word_into_playfield(char word[]) { + strcpy(playfield[turns], word); + turns++; + + // resetting the rest of the playfield + for (int i = turns; i < turns + (6 - turns); i++) { + strcpy(playfield[i], " "); + } +} + +/* + evaluate the word +*/ +void eval_word(char word[]) { + printf("\n%d\n", search_wordlist(wordlist, word)); + if (search_wordlist(wordlist, word) != -1) { + eval_win(search_wordlist(wordlist, word)); + eval_props(word); // eval correct/incorrect/oop letters + put_word_into_playfield(word); + } else { + clear(); + printf("Word %s not in wordlist\n", word); + sleep(2); + } + + eval_lose(); +} + +/* + # prerequisites: + & 1 choose a random word from a wordlist + + # algo steps: + & 1 clear the screen + & 2 display the current state of the playfield + & 3 prompt the user to enter a word + & 4 evaluate the word + & 5 print the word on the screen with the corresponding parameters at each letter (right position, wrong position, not in word) + & 6.1 if the user entered the right word, show a winning message and exit + & 6.2 if the user has tried 6 times already, show a losing message, the word and exit +*/ +void game() { + // step 1 & 2 + show(); + + // step 3 + char user_input[5]; + prompt(user_input); + + // implemented a quit function + if (user_input[0] == 'q') quit = 1; + + // step 4 & 5 + eval_word(user_input); +} + +/* + pass the arguments +*/ +int argparse(int argc, char *argv[]) { + int flag_error = 0; + for (int i = 0; i < argc; i++) { + /* + look for invalid arguments + */ + for (int j = 1; j < argc; j++) { + if (strcmp(argv[j], "--wordlist") != 0 && strcmp(argv[j-1], "--wordlist") != 0 && strcmp(argv[j], "--verbose") != 0 && strcmp(argv[j], "--help") != 0) { + fprintf(stderr, "Invalid argument %s\n", argv[j]); + flag_error = 1; + } + } + + /* + print the help & usage + */ + + if (strcmp(argv[i], "--help") == 0) { + print_help_and_usage(); + return 0; + } + + /* + getting the debug flag for debug printing + */ + if (strcmp(argv[i], "--verbose") == 0 && flag_verbose == 0) { + flag_verbose = 1; + } + + /* + getting the path to the wordlist + */ + if (strcmp(argv[i], "--wordlist") == 0 && flag_worldlist == 0) { + flag_worldlist = 1; + if (argv[i+1] != NULL) { // if there is an argument present after that, read it as the wordlist path + strcpy(wordlist_path, argv[i+1]); + } else { + fprintf(stderr, "Missing argument after %s\nUsage: %s \n", argv[i], argv[i]); + flag_error = 1; + } + } else if (strcmp(argv[i], "--wordlist") != 0 && flag_worldlist == 0) { + strcpy(wordlist_path, "lib/wordlist"); + } + } + + if (file_exists(wordlist_path) == 0) { + fprintf(stderr, "File %s not found\n", wordlist_path); + flag_error = 1; + } + + if (flag_verbose == 1) printf("wordlist_path: %s\n", wordlist_path); + + return flag_error; +} + +/* + shows the message after the game ends +*/ +void show_win_lose_message() { + show(); + if (win == 1 && lose == 0) fprintf(stdout, "You won! It took you %d guesses. The word was %s.\n", turns, word_to_guess); + if (win == 0 && lose == 1) fprintf(stdout, "You lost. :( The word was %s.\n", word_to_guess); +} + +int main(int argc, char *argv[]) { + // pass the arguments + if (argparse(argc, argv) == 1) return 1; + + // prerequisite 1 + get_random_word(word_to_guess, wordlist_path); + + // steps + while (win == 0 && lose == 0) { + game(); + if (quit == 1) return 2; + } + + // print winning message + show_win_lose_message(); + + return 0; +} \ No newline at end of file