Files
wordlec/src/main.c
T
2023-10-29 01:08:27 +02:00

362 lines
8.3 KiB
C

/*
Wordle game implementation in the C programming language
*/
// TODO: implement light mode
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#define RESET "\x1b[37;0m" // color 0
#define RED "\x1b[37;41m" // color 1
#define GREEN "\x1b[37;42m" // color 2
#define YELLOW "\x1b[37;43m" // color 3
#define GRAY "\x1b[37;90m" // color gray
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] = {
// <char> <colorcode>
// 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 <path_to_wordlist_file> ... 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 <path_to_wordlist_file>\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();
printf(GRAY "╔═══╦═══╦═══╦═══╦═══╗" RESET "\n");
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 5; j++) {
switch (pf_props[i][j][1]) {
case '0':
printf(GRAY "║" RESET " %c ", playfield[i][j]);
break;
case '1':
printf(GRAY "║" RED " %c " RESET, playfield[i][j]);
break;
case '2':
printf(GRAY "║" GREEN " %c " RESET, playfield[i][j]);
break;
case '3':
printf(GRAY "║" YELLOW " %c " RESET, playfield[i][j]);
break;
}
}
printf(GRAY "║" RESET "\n");
if (i < 5) printf(GRAY "╠═══╬═══╬═══╬═══╬═══╣" RESET "\n");
}
printf(GRAY "╚═══╩═══╩═══╩═══╩═══╝" RESET "\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 < strlen(word); i++) {
if (word[i] == target) return i;
}
return -1;
}
/*
evaluate the loss
*/
void eval_lose() {
if (turns >= 5 && win == 0) {
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_lose();
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);
}
}
/*
# 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 <path_to_wordlist_file>\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;
}