commit c927e6496d0c307a76bec349b90d2a99fe83813b Author: bePrivate Date: Sat Oct 28 19:45:39 2023 +0200 initial commit 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 0000000..01aff17 Binary files /dev/null and b/main differ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..b93f577 --- /dev/null +++ b/src/main.c @@ -0,0 +1,358 @@ +/* + Wordle game implementation in the C programming language +*/ + +#include +#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