diff --git a/solution/letter-boxed.c b/solution/letter-boxed.c index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ffc9a85c149d2f4fce7e7532f785c58e8de706c5 100644 --- a/solution/letter-boxed.c +++ b/solution/letter-boxed.c @@ -0,0 +1,187 @@ +#define _POSIX_C_SOURCE 200809L +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define MAX_SIDES 26 +#define MAX_ALPHABET 10 +#define INITIAL_DICT_SIZE 10000 +#define MAX_WORD_SIZE 100 + +typedef struct { + char sides[MAX_SIDES][MAX_ALPHABET + 1]; + int numSides; +} Board; + +void loadBoard(const char *fileName, Board *board); +char **loadDict(const char *fileName, int *num); +int isValidWord(const char *inputWord, char **dict, Board *board, int dictNum, int *trackChar); +void freeMemory(char **dict, int dictNum); + +int main(int argc, char *argv[]) { + if (argc != 3) { + printf("Usage: ./letter-boxed <board file> <dictionary file>\n"); + exit(1); + } + + Board board; + loadBoard(argv[1], &board); // load letter boxed board with board file input + + int dictNum = 0; // number of words in dictionary + char **dict = loadDict(argv[2], &dictNum); // store dictionary words in 2d array + + int trackChar[26] = {0}; // array to track characters in word + + char currInput[MAX_WORD_SIZE]; + char prevInput[MAX_WORD_SIZE] = ""; + + while (scanf("%s", currInput) != EOF) { + if (prevInput[0] != '\0' && prevInput[strlen(prevInput) - 1] != currInput[0]) { + printf("First letter of word does not match last letter of previous word\n"); + exit(1); + } + + if (!isValidWord(currInput, dict, &board, dictNum, trackChar)) { + freeMemory(dict, dictNum); + exit(0); + } + strcpy(prevInput, currInput); // update previous input + } + + for (int i = 0; i < board.numSides; i++) { + for (size_t j = 0; j < strlen(board.sides[i]); j++) { + if (!trackChar[board.sides[i][j] - 'a']) { + printf("Not all letters used\n"); + exit(0); + } + } + } + + printf("Correct\n"); + + freeMemory(dict, dictNum); + return 0; +} + +void loadBoard(const char *fileName, Board *board) { + FILE *file = fopen(fileName, "r"); + + if (!file) { + printf("Board file invalid\n"); + exit(1); + } + + int trackCharNum[26] = {0}; + board->numSides = 0; + + // read each side(line) from file and store in the board side array + while (fgets(board->sides[board->numSides], MAX_ALPHABET + 1, file) != NULL) { + board->sides[board->numSides][strcspn(board->sides[board->numSides], "\n")] = 0; // remove newline + for (size_t i = 0; i < strlen(board->sides[board->numSides]); i++) { + int charIndex = board->sides[board->numSides][i] - 'a'; + if (trackCharNum[charIndex] > 0) { + printf("Invalid board\n"); // board contains a letter more than once + exit(1); + } + trackCharNum[charIndex]++; + } + board->numSides++; // next side(line) of the board + } + + if (board->numSides < 3) { + printf("Invalid board\n"); + exit(1); + } + + fclose(file); +} + +char **loadDict(const char *fileName, int *num) { + FILE *file = fopen(fileName, "r"); + if (!file) { + printf("Dictionary file invalid\n"); + exit(1); + } + int dictSize = INITIAL_DICT_SIZE; + char **dict = malloc(dictSize * sizeof(char *)); + + if (!dict) { + printf("Memory allocation of dictionary array failed\n"); + exit(1); + } + + char word[MAX_WORD_SIZE]; + while (fscanf(file, "%s", word) != EOF) { + if (*num >= dictSize) { + dictSize *= 2; + char **newDict = realloc(dict, dictSize * sizeof(char *)); // dynamically allocate memory + if (!newDict) { + freeMemory(dict, *num); + printf("Memory allocation failed\n"); + exit(1); + } + dict = newDict; + } + dict[*num] = strdup(word); // assign word in the dictionary file to the dictionary array + if (!dict[*num]) { + freeMemory(dict, *num); + printf("Memory allocation failed\n"); + exit(1); + } + (*num)++; // increment num after allocation + } + + fclose(file); + return dict; +} + +int isValidWord(const char *inputWord, char **dict, Board *board, int dictNum, int *trackChar) { + bool foundDict = false; + for (int i = 0; i < dictNum; i++) { + if (strcmp(inputWord, dict[i]) == 0) { + foundDict = true; + break; + } + } + + if (!foundDict) { + printf("Word not found in dictionary\n"); + return 0; + } + + int prevSideNum = -1; + + for (size_t i = 0; i < strlen(inputWord); i++) { + char currChar = inputWord[i]; // loop through each character of word + bool foundBoard = false; + + for (int sideNum = 0; sideNum < board->numSides; sideNum++) { + if (strchr(board->sides[sideNum], currChar) != NULL) { // returns the index of char if found in board side + foundBoard = true; + + if (prevSideNum == sideNum) { + printf("Same-side letter used consecutively\n"); + return 0; + } + trackChar[currChar - 'a'] = 1; + prevSideNum = sideNum; // update previous side num to the board side num in which char exists + break; // break out of the loop since the char is found in the board + } + } + + if (!foundBoard) { + printf("Used a letter not present on the board\n"); + return 0; + } + } + + return 1; +} + +void freeMemory(char **dict, int dictNum) { + for (int i = 0; i < dictNum; i++) { + free(dict[i]); + } + free(dict); +}