In: Computer Science
Complete the project functions for this week. These functions are winner(), save_game() and load_game(). To complete these functions, first remove the library function call from the function in the template (save_game_lib, etc), then replace that with your own code to complete the function. You can test your functions separately from the project by creating a small main() function that calls them (with appropriate inputs), or you can compile the entire project and see if it works.
winner(): The winner function is rather long, as it needs to check in 4 different directions to 1305ENG (Programming Laboratory 8) Page 5 of 5 see if the game has been won: horizontally, vertically, and diagonally in two directions. Each of these requires loops to pick the starting position, then a statement to compare the 4 squares in the chosen direction to see if they are equal (and not equal to 0). Once you’ve found a single winning combination there’s no need to look any further, just return that result. If you search all four directions without finding a winner, return 0. HINTS: consider where the first square of a winning run of 4 tokens could possibly start. For example, horizontally these 4 tokens could start on the far left of the board, but only up to the 4th last on the right (COLS3). Any further right would not allow for 4 tokens to be in a row. Diagonally the possible starting positions are even fewer – the diagonal downward combinations can only start in the top 3 (ROWS-3) rows, and the leftmost 4 (COLS-3) columns, and similarly for the diagonally upwards direction except the bottom 3 rows. Once you know which possible starting points to loop through, you can then check the 4 tokens in the row starting from the spot.
save_game(): This function saves the specified game to the specified file. Note that a "Game" variable is a struct that has 5 fields: player1 and player2 are integers specifying whether the player is a human (1) or computer (0). The board field contains a COLSxROWS array of integers. The turn field defines whose turn it is in the game (1 or 2), and finally winner is an integer which stores the winner of the game (1 or 2) or 0 if nobody has won yet. All of these fields should be written to the file in the format:
player1 player2 turn winner
[board]
Where board is the entire board, one row at time. This should be printed out in a very similar way to the way the board is displayed, except use the numbers 0 (empty), 1 (player1) and 2 (player2) to represent each square, and there are no other characters such as | or – to format it. See the game.txt file on the website to get an example of what the file should look like. If the file is written successfully, 0 should be returned by the function. If there is any error, -1 should be returned.
load_game(): This function takes a pointer to a Game structure and a filename, and reads the game data from the specified file and puts it into the supplied struct. The format is identical to the save_game function. If the file is read successfully, 0 should be returned. If there is any error, -1 should be returned. More information on this week's functions can be found in the project instructions, and hints on the algorithms can be found in the template file
TEMPLATE FILE:
#include #include #include #include #include #include "connect4.h" int main ( void ){ int option ; Game g ; // intitialise random seed srand(time(NULL)); while (( option = main_menu()) != -1 ){ if ( option == 1 ){ // setup a new game setup_game ( &g ) ; // now play this game play_game ( &g ) ; } else if ( option == 2 ){ // attempt to load the game from the save file if ( load_game ( &g, "game.txt" ) == 0 ){ // if the load went well, resume this game play_game ( &g ) ; } else { printf ( "Loading game failed.\n") ; } } else if ( option == -1 ){ printf ( "Exiting game, goodbye!\n") ; } } } // WEEK 1 TASKS // main_menu() // column_full() // get_move() // displays the welcome screen and main menu of the game, and prompts the user to enter an option until // a valid option is entered. // Returns 1 for new game, 2 for load game, -1 for quit int main_menu ( void ){ // Dipslay Welcome message // Continue asking for an option until a valid option (n/l/q) is entered // if 'n', return 1 // if 'l', return 2 // if 'q', return -1 // if anything else, give error message and ask again.. return main_menu_lib () ; } // Returns TRUE if the specified column in the board is completely full // FALSE otherwise // col should be between 1 and COLS int column_full ( int board[COLS][ROWS], int col ){ // check the TOP spot in the specified column (remember column is between 1 and COLS, NOT 0 and COLS-1 so you'll need to modify slightly // if top spot isn't empty (0 is empty) then the column is full, return 1 // otherwise, return 0 return column_full_lib ( board, col ) ; } // prompts the user to enter a move, and checks that it is valid // for the supplied board and board size // Returns the column that the user has entered, once it is valid (1-COLS) // note that this value is betweeen 1 and COLS (7), NOT between 0 and 6!! // If the user enters 'S' or 's' the value -1 should be returned, indicating that the game should be saved // If the user enters 'Q' or 'q' the value -2 should be returned, indicating that the game should be abandoned int get_move ( int board[COLS][ROWS] ){ // repeat until valid input is detected: // read a line of text from the user // check if the user has entered either 's' (return -1) or 'q' (return -2) // if not, read a single number from the inputted line of text using sscanf // if the column is valid and not full, return that column number // otherwise, give appropriate error message and loop again return get_move_lib ( board ) ; } // END OF WEEK 1 TASKS // WEEK 2 TASKS // board_full() // display_board() // add_move() // adds a token of the given value (1 or 2) to the board at the // given column (col between 1 and COLS inclusive) // Returns 0 if successful, -1 otherwise int add_move ( int board[COLS][ROWS], int col, int colour ){ // check that the column isn't full - if it is, return -1 and abort // start at the bottom of the board, and move upwards in the specified column until an empty spot is found // put a token of the specified colour at that location return add_move_lib ( board, col, colour ) ; } // determines if the board is completely full or not. // Return TRUE if full, false otherwise int board_full ( int board[COLS][ROWS] ){ // loop through each column // if a column ISN'T full, return 0 (board can't be full if a single column isnt // if all columns are checked and full, return 1 return board_full_lib ( board ) ; } // displays the board to the screen int display_board ( int board[COLS][ROWS] ){ // loop through each row and column of the board and display in appropriate format // use | and - characters to draw boundaries of the board, and put numbers at the bottom to indicate the column numbers return display_board_lib ( board ) ; } // END OF WEEK 2 TASKS // WEEK 3 TASKS // winner() // save_game() // load_game() // determines who (if anybody) has won. Returns the player id of the // winner (1 or 2), otherwise 0 if nobody has won yet int winner ( int board[COLS][ROWS] ){ // using loops, check all possible HORIZONTAL winning locations // check every row, for each row: // for starting vlaues of 1 to 4 (COLS-4+1), check if 4 squares are all the same player's colour // if so, return that colour as the result // do a similar process for VERTICAL winning combinations // do a similar process for DIAGONAL UPWARDS combinations // do a simlar process for DIAGONAL DOWNWARDS combinations // if no winner found, return 0 return winner_lib ( board ) ; } // saves the game to the specified file. The file is text, with the following format // player1 player2 turn winner // board matrix, each row on a separate line // Example: // //1 0 1 0 player 1 human, player 2 computer, player 1's turn, nobody has won //0 0 0 0 0 0 0 board state - 1 for player 1's moves, 2 for player 2's moves, 0 for empty squares //0 0 0 0 0 0 0 //0 0 0 2 0 0 0 //0 0 0 2 0 0 0 //0 2 1 1 1 0 0 //0 2 2 1 1 2 1 int save_game ( Game g, char filename[] ){ return save_game_lib ( g, filename ) ; } // loads a saved game into the supplied Game structure. Returns 0 if successfully loaded, -1 otherwise. // Format is identical to the save format described above, obviously. int load_game ( Game *g, char filename[] ){ return load_game_lib ( g, filename ) ; } // END OF WEEK 3 TASKS // WEEK 4-5 TASKS // setup_game() // play_game() // computer_move() // calcualtes a column for the computer to move to, using artificial "intelligence" // The 'level' argument describes how good the computer is, with higher numbers indicating better play // 0 indicates very stupid (random) play, 1 is a bit smarter, 2 smarter still, etc.. int computer_move ( int board[COLS][ROWS], int colour, int level ){ // If level 0, this is a 'dumb' opponent, pick a random column that isn't full and return that // If level 1, this is slightly smarter - if the computer's move can win the game, choose that column, otherwise random // If level 2, slightly smarter again. If computer can win it will do that, otherwise it will block an opponent's winning move, otherwise random // Higher levels are up to you! // Hint - you can copy the board into a 'temporary' board and trial putting a token in a column, then simply call the winner function to see // if that move has won the game. Doing this for the current player will check if that player can win, doing it for the opponent will see if the opponent can win next turn // You can use "lookahead" logic to search for the 'best' move many moves ahead return computer_move_lib ( board, colour, level ) ; } // sets up the game to a new state // prompts the user if each player should be a human or computer, and initialises the relevant fields // of the game structure accordingly int setup_game ( Game *g ){ // prompt the user to enter whether each player is a human or computer (h or c) // set the player1 and player2 fields of the struct accordingly (1 for human, 0 for computer) // initialise the board to all zeros // set winner to 0 ( no winner yet!) // set turn to either 1 or 2 (randomly) return setup_game_lib ( g ) ; } // Starts or resumes playing the Game g. Continues until the game is over or the user quits. int play_game ( Game *g ){ return play_game_lib ( g ) ; }
int load_game_lib ( Game *g, char filename[] ) { FILE *fp; fp = fopen(filename, "r"); // read mode if (fp == NULL) { return 0; } fscanf(fp, "%d %d %d %d", &g->player1, &g->player2, &g->turn, &g->winner); for(int i=0; i<COLS; i++) { for(int j=0; j<ROWS; j++) { fscanf(fp, "%d", &g->board[i][j]); } } return 1; } int save_game_lib ( Game g, char filename[] ) { FILE *fp; fp = fopen(filename, "w"); // read mode if (fp == NULL) { return 0; } fprintf(fp, "%d %d %d %d\n", g.player1, g.player2, g.turn, g.winner); for(int i=0; i<COLS; i++) { for(int j=0; j<ROWS; j++) { fprintf(fp, "%d ", g.board[i][j]); } fprintf(fp, "\n"); } return 1; } // loop through each row and column of the board and display in appropriate format // use | and - characters to draw boundaries of the board, and put numbers at the bottom to indicate the column numbers // return display_board_lib ( board ) ; int winner (int data[COLS][ROWS] ) { // Check to see if four consecutive cells in a row match. // check rows for (int r = 0; r < COLS; r++) { for (int c = 0; c < ROWS - 3; c++) { if ( data[r][c] == data[r][c + 1] && data[r][c] == data[r][c + 2] && data[r][c] == data[r][c + 3] && data[r][c] != 0){ return data[r][c]; } } } // Check to see if four columns in the same row match // check columns for (int r = 0; r < COLS - 3; r++) { for (int c = 0; c < ROWS; c++) { if ( data[r][c] == data[r + 1][c] && data[r][c] == data[r + 2][c] && data[r][c] == data[r + 3][c] && data[r][c] != 0) { return data[r][c]; } } } // Check to see if four diagonals match (top left to bottom right) // check major diagonal for (int r = 0; r < COLS - 3; r++) { for (int c = 0; c < ROWS - 3; c++) { if ( data[r][c] == data[r + 1][c + 1] && data[r][c] == data[r + 2][c + 2] && data[r][c] == data[r + 3][c + 3] && data[r][c] != 0) { return data[r][c]; } } } // Check to see if four diagonals in the other direction match (top right to // bottom left) // check minor diagonal for (int r = 0; r < COLS - 3; r++) { for (int c = 3; c < ROWS; c++) { if ( data[r][c] == data[r + 1][c - 1] && data[r][c] == data[r + 2][c - 2] && data[r][c] == data[r + 3][c - 3] && data[r][c] != 0) { return data[r][c]; } } } return 0; }
**************************************************
Answering the functions related to save and load, Since seems you are asking for them.. Your other functions are not completed, so i can not test the functionality. Let me know if i can be of any further help.