commit c02ff71daa23ea0c1457002a5248211c7d61c3ba Author: wildscotsmen Date: Sat Apr 22 16:57:29 2017 -0400 First version. diff --git a/arghandler.c b/arghandler.c new file mode 100644 index 0000000..a64a015 --- /dev/null +++ b/arghandler.c @@ -0,0 +1,130 @@ +#include +#include +#include "arghandler.h" + +/************************************************************************************ +* @author Jacob McCloughan +* @version 3/03/2017 +* @see arghandler.h +* +* My implementation for argp. Handles arguments for the Connect Four game. +*************************************************************************************/ + +/* +* Program version. +*/ +const char *argp_program_version = "Connect Four (Version 3/03/2017)"; + +/* +* My email address for bug reports. +*/ +const char *argp_program_bug_address = "mcclougj@mail.gvsu.edu"; + +/* +* Game description. +*/ +char doc[] = "Connect Four: Game where you place tokens until a player connects four!"; + +/* +* Accepted arguments. Unused, but defined. +*/ +char args_doc[] = ""; + +/* +* Saved width board parameter. +*/ +int wid; + +/* +* Saved height board parameter. +*/ +int hei; + +/* +* Saved square board parameter. +*/ +int sqr; + +/* +* Saved win length parameter. +*/ +int winlen; + +/* +* Saved filename for loading. +*/ +char* loadFile; + +struct argp_option options[] = { + { "width", 'w', "WIDTH", 0, "Width dimension for board." }, + { "height", 'h', "HEIGHT", 0, "Height dimension for board." }, + { "square", 's', "SQUARE", 0, "Dimensions for a square board (width and height will be overrided)." }, + { "connect", 'c', "CONNECT", 0, "Length to win the game." }, + { "load", 'l', "LOAD", 0, "Load a previous saved game." }, + { 0 } +}; + +/* +* Required arguments for the argp struct. +*/ +struct argp argp = { options, parse_opt, args_doc, doc }; + +/* +* Argument values for flags. +*/ +struct arguments arguments; + +/* +* Function that sets up values post-parsing. +* +* @param key, the key of the argument. +* @param arg, the argument being passed. +* @param state, the state of parser. +*/ +error_t parse_opt(int key, char* arg, struct argp_state *state) { + struct arguments *arguments = state->input; + switch (key) { + case 'w': + arguments->width = atoi(arg); + break; + case 'h': + arguments->height = atoi(arg); + break; + case 's': + arguments->square = atoi(arg); + break; + case 'c': + arguments->winL = atoi(arg); + break; + case 'l': + arguments->loadFile = arg; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +/* +* Parses arguments. +* @param argc, number of command line arguments. +* @param argv, the command line arguments. +*/ +void setup(int argc, char** argv) { + wid= 0; + hei= 0; + sqr = 0; + winlen = 0; + loadFile = NULL; + + argp_parse(&argp, argc, argv, 0, 0, &arguments); + + wid = arguments.width; + hei = arguments.height; + sqr = arguments.square; + winlen = arguments.winL; + + if (arguments.loadFile != NULL) { + loadFile = arguments.loadFile; + } +} \ No newline at end of file diff --git a/arghandler.h b/arghandler.h new file mode 100644 index 0000000..19dfb29 --- /dev/null +++ b/arghandler.h @@ -0,0 +1,59 @@ +#ifndef ARG_HANDLER +#define ARG_HANDLER +#include + +/************************************************************************************ +* @author Jacob McCloughan +* @version 3/03/2017 +* @see argp.h +* +* My implementation for argp. Will handle arguments for the Connect Four game. +*************************************************************************************/ + +/* +* Saved width board parameter. +*/ +int wid; + +/* +* Saved height board parameter. +*/ +int hei; + +/* +* Saved square board parameter. +*/ +int sqr; + +/* +* Saved win length parameter. +*/ +int winlen; + +/* +* Saved filename for loading. +*/ +char* loadFile; + +/* +* Argument values for flags. +*/ +struct arguments { + int width; + int height; + int square; + int winL; + char* loadFile; +}; + +/* +* Function that sets up values post-parsing. +*/ +error_t parse_opt(int key, char* arg, struct argp_state *state); + +/* +* Parses arguments. +*/ +void setup(int argc, char** argv); + +#endif \ No newline at end of file diff --git a/connectfour.c b/connectfour.c new file mode 100644 index 0000000..9144dde --- /dev/null +++ b/connectfour.c @@ -0,0 +1,171 @@ +#include +#include +#include +#include "gamelogic.h" +#include "arghandler.h" + +/************************************************************************************ +* @author Jacob McCloughan +* @version 3/03/2017 +* @see gamelogic.h +* @see arghandler.h +* +* Includes the main method for our Connect Four game. Uses to handle +* command line arguments. +* +* This post taught me how to use colored text in Linux. +* http://stackoverflow.com/questions/3219393/stdlib-and-colored-output-in-c +*************************************************************************************/ + +/* +* Main method for the Connect Four game. Uses all of the functions from the +* game logic file and allows the player to play the game. +* +* @param argc, number of command line arguments. +* @param argv, the command line arguments. +* @return 0 if the operation failed and 1 if it succeeded. +*/ +int main(int argc, char** argv) { + // Variables and array setup + char* input = malloc(100 * sizeof(char)); + int player = 1; + setup(argc, argv); + + // Fixes invalid height or width arguments + if (hei <= 0 || hei > 2147483647 || wid <= 0 || wid > 2147483647) { + setHeight(7); + setWidth(7); + } + else { + setHeight(hei); + setWidth(wid); + } + + // Fixes invalid win length arguments + if (winlen <= 0 || winlen > 2147483647) { + setWinLength(4); + } + else { + setWinLength(winlen); + } + + // Fixes invalid square dimension arguments + if (sqr > 0 && sqr <= 2147483647) { + setHeight(sqr); + setWidth(sqr); + } + + // Either starts new game or loads from argument + printf("%sWelcome to Connect Four!\nType 'save' to save, 'load' to load, and 'quit' to quit.\n%s", "\x1B[32m", "\033[0m"); + if (loadFile != NULL) { + if (loadGame(loadFile) == 0) { + printf("\nInvalid file.\n"); + return 0; + } + printBoard(); + } else { + populateBoard(); + printBoard(); + } + + // While loop for the game logic + while (1) { + // Checks if player 1 has won + if (checkWin('o') == 1) { + printf("Player 1 wins!\n"); + printf("Would you like to play again? Type 'yes' for yes or 'no' for no.\n"); + while (1) { + scanf("%s", input); + if ((strcmp("yes", input) == 0)) { + printf("\n"); + populateBoard(); + player = 1; + printBoard(); + break; + } + if ((strcmp("no", input) == 0)) { + printf("\n"); + return 1; + } else { + printf("Invalid input.\n"); + continue; + } + } + } + + // Checks if player 2 has won + if (checkWin('x') == 1) { + printf("Player 2 wins!\n"); + printf("Would you like to play again? Type 'yes' for yes or 'no' for no.\n"); + while (1) { + scanf("%s", input); + if ((strcmp("yes", input) == 0)) { + populateBoard(); + player = 1; + printBoard(); + printf("\n"); + break; + } if ((strcmp("no", input) == 0)) { + return 1; + } else { + printf("Invalid input.\n"); + continue; + } + } + } + + // User input + printf("\nPlayer %d's turn.\n", player); + printf("Input a column (from 1 to %d) to place a token:\n", width); + scanf("%s", input); + + // For quitting + if (strcmp("quit", input) == 0) { + return 1; + } + + // Saving functionality + if (strcmp("save", input) == 0) { + printf("Input a filename:\n"); + scanf("%s", input); + saveGame(input); + printf("Game saved.\n"); + continue; + } + + // Loading functionality + if (strcmp("load", input) == 0) { + printf("Input a filename:\n"); + scanf("%s", input); + if (loadGame(loadFile) == 0) { + printf("\nInvalid file.\n"); + continue; + } else { + printBoard(); + printf("\nGame loaded.\n"); + continue; + } + } + + // Checks to make sure input is valid + if (atoi(input) < 1 || atoi(input) > width) { + printf("Invalid input. Input another column:\n"); + continue; + } + + // Checks to make sure input is valid + if (placeChip(atoi(input) - 1, player) == 0) { + printf("Invalid input. Input another column:\n"); + } else { + if (player == 1) { + player = 2; + } + else { + player = 1; + } + printBoard(); + } + } + + return 1; +} diff --git a/file_utils.c b/file_utils.c new file mode 100644 index 0000000..164d463 --- /dev/null +++ b/file_utils.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include "file_utils.h" + +/************************************************************************************ +* @author Jacob McCloughan +* @version 3/03/2017 +* @see file_utils.h +* +* Implementation for file_utils.h. Contains functions +* for reading from a file and putting it into a selected array +* and for writing the array backwards into a selected file. +* +* This post taught me everything I now know about files in C: +* http://stackoverflow.com/questions/22697407/reading-text-file-into-char-array +*************************************************************************************/ + +/* +* Reads the .txt file passed as a parameter into the array (buffer) +* pointed to as a parameter. +* +* @param filename, the name of the .txt file being read from. +* @param buffer, a pointer to the array that will have the data read in. +* @return 0 if the operation failed and the number of bytes if it succeeded. +*/ +int read_file(char* filename, char **buffer) { + // Variables. The stat struct is from Professor Woodring. + FILE *fil; + struct stat st; + stat(filename, &st); + int size = st.st_size; + + // Opens up the file for extracting the data. If statement is for error-checking. + fil = fopen(filename, "r"); + if (!fil) { + fprintf(stderr, "Unable to read file."); + return 0; + } + + // Creates the space for our array. If statement is for error-checking. + *buffer = malloc((size) * sizeof(char)); + if (!*buffer) { + fprintf(stderr, "Unable to allocate memory."); + fclose(fil); + return 0; + } + + /* Reads the data into our array. If statement is for error-checking, but it also contains the + command that reads in the data. */ + if (fread(*buffer, size, 1, fil) != 1) { + fprintf(stderr, "Error copying file to memory."); + fclose(fil); + return 0; + } + + // Closes the file and returns the size of the array. + fclose(fil); + return size; +} + +/* +* Writes to the filename passed as a parameter from the array (buffer) +* passed as a parameter. The size of the array must be specified. +* +* @param filename, the name of the .txt file being written to. +* @param buffer, the array that the data is being written from. +* @param size, the size of the array. If read in properly by the read_file +* function, this should have been returned. +* @return 0 if the operation failed and 1 if it succeeded. +*/ +int write_file(char* filename, char *buffer, int size) { + // File being operated on. + FILE *fil; + + // Opens up the file for printing the data. If statement is for error-checking. + fil = fopen(filename, "w"); + if (!fil) { + fprintf(stderr, "Unable to write to that file."); + fclose(fil); + return 0; + } + + /* Iterates through the buffer passed and prints it backwards to a text file. + If statement is for error-checking, but it also contains the command that writes the data. */ + for (int i = 0; i < size; i++) { + if (fprintf(fil, "%c", buffer[i]) != 1) { + fprintf(stderr, "Error when writing to file."); + fclose(fil); + return 0; + } + } + + // Closes the file and returns 1. + fclose(fil); + return 1; +} \ No newline at end of file diff --git a/file_utils.h b/file_utils.h new file mode 100644 index 0000000..8746469 --- /dev/null +++ b/file_utils.h @@ -0,0 +1,34 @@ +#ifndef FILE_UTILS +#define FILE_UTILS + +/************************************************************************************ +* @author Jacob McCloughan +* @version 3/03/2017 +* +* Header file for file utilities for reading in a file to an array and then +* writing that file backwards to another file. +*************************************************************************************/ + +/* +* Reads the .txt file passed as a parameter into the array (buffer) +* pointed to as a parameter. +* +* @param filename, the name of the .txt file being read from. +* @param buffer, a pointer to the array that will have the data read in. +* @return 0 if the operation failed and the number of bytes if it succeeded. +*/ +int read_file(char* filename, char** buffer); + +/* +* Writes to the filename passed as a parameter from the array (buffer) +* passed as a parameter. The size of the array must be specified. +* +* @param filename, the name of the .txt file being written to. +* @param buffer, the array that the data is being written from. +* @param size, the size of the array. If read in properly by the read_file +* function, this should have been returned. +* @return 0 if the operation failed and 1 if it succeeded. +*/ +int write_file(char* filename, char* buffer, int size); + +#endif diff --git a/gamelogic.c b/gamelogic.c new file mode 100644 index 0000000..90ba4d7 --- /dev/null +++ b/gamelogic.c @@ -0,0 +1,396 @@ +#include +#include +#include "gamelogic.h" +#include "file_utils.h" +#include + +/************************************************************************************ +* @author Jacob McCloughan +* @version 3/03/2017 +* @see gamelogic.h +* +* Implementation for the Connect Four game logic. Includes all of the functions +* for operating the game and the variables for storing the game's state. +* +* This post taught me how to use colored text in Linux. +* http://stackoverflow.com/questions/3219393/stdlib-and-colored-output-in-c +*************************************************************************************/ + +/* +* Doubly array that will store the game board. +*/ +char** gameBoard; + +/* +* Height of the game board. +*/ +int height; + +/* +* Width of the game board. +*/ +int width; + +/* +* Length to win the game. +*/ +int winLength; + +/* +* Sets the board height to the parameter passed. +* +* @param h, the board height to be set to. +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int setHeight(int h) { + height = h; + return 1; +} + +/* +* Sets the board width to the parameter passed. +* +* @param w, the board width to be set to. +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int setWidth(int w) { + width = w; + return 1; +} + +/* +* Sets the win length to the parameter passed. +* +* @param l, the win length to be set to. +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int setWinLength(int l) { + winLength = l; + return 1; +} + +/* +* Prints the current state of the board to the terminal. +* +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int printBoard() { + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + if (gameBoard[i][j] == 'o') { + printf("\x1b[34m%c\x1b[0m", gameBoard[i][j]); + } else if (gameBoard[i][j] == 'x') { + printf("\x1b[31m%c\x1b[0m", gameBoard[i][j]); + } else { + printf("%c", gameBoard[i][j]); + } + } + printf("\n"); + } + + return 1; +} + +/* +* Resets the board with the current board parameters. +* +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int populateBoard() { + gameBoard = malloc(sizeof(char*) * height); + for (int i = 0; i < height; i++) { + gameBoard[i] = malloc(sizeof(char) * width); + } + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + gameBoard[i][j] = '*'; + } + } + + return 1; +} + +/* +* Places a chip in the desired column. Designed to emulate +* putting a chip into a column in the actual game of Connect +* Four. +* +* @param col, the desired column. +* @param p, the player placing the chip. +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int placeChip(int col, int p) { + // Handles bad arguments + if (col > width - 1 || col < 0) { + return 0; + } + + // Checks to make sure the move is valid and places if so + for (int i = height - 1; i >= 0; i--) { + if (i > 0 && (gameBoard[i][col] == 'o' || gameBoard[i][col] == 'x')) { + continue; + } + else if (i == 0 && gameBoard[i][col] != '*') { + return 0; + } + else { + if (p == 1) { + gameBoard[i][col] = 'o'; + } + if (p == 2) { + gameBoard[i][col] = 'x'; + } + break; + } + } + + return 1; +} + +/* +* Saves the current game. Puts the game's state into +* a text file so that it can be loaded later on. +* +* @param filename, the name of the file to be saved to. +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int saveGame(char* filename) { + // Variables and character array setup + char* buffer; + int count = 33; + buffer = malloc((sizeof(char) * width * height) + (33 * sizeof(char))); + char* widthVal = malloc(10 * sizeof(char)); + char* heightVal = malloc(10 * sizeof(char)); + char* winVal = malloc(10 * sizeof(char)); + + // Stores board parameter and win length as strings + sprintf(widthVal, "%010d", width); + sprintf(heightVal, "%010d", height); + sprintf(winVal, "%010d", winLength); + + /* Copies the board parameters and win length character + by character to the buffer to be saved to memory */ + buffer[0] = widthVal[0]; + buffer[1] = widthVal[1]; + buffer[2] = widthVal[2]; + buffer[3] = widthVal[3]; + buffer[4] = widthVal[4]; + buffer[5] = widthVal[5]; + buffer[6] = widthVal[6]; + buffer[7] = widthVal[7]; + buffer[8] = widthVal[8]; + buffer[9] = widthVal[9]; + buffer[10] = '\n'; + buffer[11] = heightVal[0]; + buffer[12] = heightVal[1]; + buffer[13] = heightVal[2]; + buffer[14] = heightVal[3]; + buffer[15] = heightVal[4]; + buffer[16] = heightVal[5]; + buffer[17] = heightVal[6]; + buffer[18] = heightVal[7]; + buffer[19] = heightVal[8]; + buffer[20] = heightVal[9]; + buffer[21] = '\n'; + buffer[22] = winVal[0]; + buffer[23] = winVal[1]; + buffer[24] = winVal[2]; + buffer[25] = winVal[3]; + buffer[26] = winVal[4]; + buffer[27] = winVal[5]; + buffer[28] = winVal[6]; + buffer[29] = winVal[7]; + buffer[30] = winVal[8]; + buffer[31] = winVal[9]; + buffer[32] = '\n'; + + // Saves the state of the board to the buffer + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + buffer[count] = gameBoard[i][j]; + count++; + } + if (count >(width * height + 32)) { + break; + } + } + + count = 0; + + // Writes the buffer into memory. + write_file(filename, buffer, (sizeof(char) * width * height) + (33 * sizeof(char))); + + return 1; +} + +/* +* Loads a previous game. Reloads a previous game's +* state from a text file so that a past game can be +* continued. +* +* @param filename, the name of the file to be loaded from. +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int loadGame(char* filename) { + // Variables + char* buffer; + int count = 33; + char* sub; + + // Reads in the previous game to memory + if (read_file(filename, &buffer) == 0) { + return 0; + } + + // Copies the board and victory data + sub = malloc(10 * sizeof(char)); + strncpy(sub, buffer, 10); + setWidth(atoi(sub)); + strncpy(sub, buffer + 11, 10); + setHeight(atoi(sub)); + strncpy(sub, buffer + 22, 10); + setWinLength(atoi(sub)); + + // Resets board with new board parameters + populateBoard(); + + // Copies past game's state to board. + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + gameBoard[i][j] = buffer[count]; + count++; + } + } + + return 1; +} + +/* +* Checks if the current state of the board is a victory +* for the player token passed as a parameter. +* +* @param p, the player token being checked for. +* @return 0 if the player passed has not won, and 1 if the +* player passed has won. +*/ +int checkWin(char p) { + // Variables for the diagonal check algorithm + int row = 0; + int col = 0; + + // Handles 1 by 1 boards + if (height == 1 && width == 1 && winLength == 1 ) { + if (gameBoard[0][0] == p) { + return 1; + } + else { + return 0; + } + } + + + // Checks horizontally + int count = 0; + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + if (gameBoard[i][j] == p) { + count++; + } else { + count = 0; + } + if (count == winLength) { + return 1; + } + } + count = 0; + } + + // Checks vertically + count = 0; + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + if (gameBoard[j][i] == p) { + count++; + } else { + count = 0; + } + if (count == winLength) { + return 1; + } + } + count = 0; + } + + // Checks negative slope diagonal + count = 0; + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + row = i; + col = j; + count = 0; + while (row < height && col < width) { + if (gameBoard[row][col] == p) { + count++; + row++; + col++; + } else { + row++; + col++; + count = 0; + } + if (count == winLength) { + return 1; + } + } + } + } + + // Checks positive slope diagonal + count = 0; + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + row = i; + col = j; + count = 0; + while (row >= 0 && col < width) { + if (gameBoard[row][col] == p) { + count++; + row--; + col++; + } else { + row--; + col++; + count = 0; + } + if (count == winLength) { + return 1; + } + } + } + } + + return 0; +} + +/* +* Modifies the position passed on the board to whatever +* character is passed. Mostly used for testing. +* +* @param row, the row of the position. +* @param col, the column of the position. +* @param c, the character that the position will be set to. +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int modifyPostition(int row, int col, char c) { + gameBoard[row][col] = c; + return 1; +} diff --git a/gamelogic.h b/gamelogic.h new file mode 100644 index 0000000..483ee87 --- /dev/null +++ b/gamelogic.h @@ -0,0 +1,129 @@ +#ifndef GAME_LOGIC +#define GAME_LOGIC + +/************************************************************************************ +* @author Jacob McCloughan +* @version 3/03/2017 +* +* Header file for the Connect Four game logic. +*************************************************************************************/ + +/* +* Doubly array that will store the game board. +*/ +char** gameBoard; + +/* +* Height of the game board. +*/ +int height; + +/* +* Width of the game board. +*/ +int width; + +/* +* Length to win the game. +*/ +int winLength; + +/* +* Sets the board height to the parameter passed. +* +* @param h, the board height to be set to. +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int setHeight(int h); + +/* +* Sets the board width to the parameter passed. +* +* @param w, the board width to be set to. +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int setWidth(int w); + +/* +* Sets the win length to the parameter passed. +* +* @param l, the win length to be set to. +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int setWinLength(int l); + +/* +* Prints the current state of the board to the terminal. +* +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int printBoard(); + +/* +* Resets the board with the current board parameters. +* +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int populateBoard(); + +/* +* Places a chip in the desired column. Designed to emulate +* putting a chip into a column in the actual game of Connect +* Four. +* +* @param col, the desired column. +* @param p, the player placing the chip. +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int placeChip(int col, int p); + +/* +* Saves the current game. Puts the game's state into +* a text file so that it can be loaded later on. +* +* @param filename, the name of the file to be saved to. +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int saveGame(char* filename); + +/* +* Loads a previous game. Reloads a previous game's +* state from a text file so that a past game can be +* continued. +* +* @param filename, the name of the file to be loaded from. +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int loadGame(char* filename); + +/* +* Checks if the current state of the board is a victory +* for the player token passed as a parameter. +* +* @param p, the player token being checked for. +* @return 0 if the player passed has not won, and 1 if the +* player passed has won. +*/ +int checkWin(char p); + +/* +* Modifies the position passed on the board to whatever +* character is passed. Mostly used for testing. +* +* @param row, the row of the position. +* @param col, the column of the position. +* @param c, the character that the position will be set to. +* @return 0 if the operation failed, and 1 if the operation +* succeeded. +*/ +int modifyPostition(int row, int col, char c); + +#endif \ No newline at end of file