//========================================================================= #define PROGRAMMER "SOLUTIONS, Nofrills" #define PROG_CODE "soln" #define COURSE "ECE-1021" #define YEAR (2004) #define TERM "Spring" #define SECTION (0) #define ASSIGNMENT "HW #6B" #define REVISION (2) #define TITLE "Mancala Engine" #define SUBTITLE "Tournament Version" #define EMAIL "ece1021@eas.uccs.edu" #define FILENAME "06b0soln.c" //========================================================================= //========================================================================= // PROBLEM //========================================================================= // // // Write a Mancala Engine to allow two-player human play. // // ================================================================= // | | 1 | 2 | 3 | 4 | 5 | 6 | | // ================================================================= // | | | | | | | | | // | | 4 | 4 | 4 | 4 | 4 | 4 | | // | | | | | | | | | // | 0 |=======|=======|=======|=======|=======|=======| 0 | // | | | | | | | | | // | | 4 | 4 | 4 | 4 | 4 | 4 | | // | | | | | | | | | // ================================================================= // | | 6 | 5 | 4 | 3 | 2 | 1 | | // ================================================================= // // Player[2][7] // Player[n][0] => Player n's Kalaha // Player[n][b] => Plyaer n's bin b ( 1 <= b <= 6 ) // // PLAYER A is player 0 and is on top // PLAYER B is player 1 and is on top //========================================================================= // WAIVED COMPILER WARNINGS //========================================================================= // // Compiler Warning: Cannot create pre-compiled header: code in header // Reason for Waiver: Intentional code in header to avoid need for project. // // Linker Warning: No module definition file specified: using defaults // Reason for Waiver: Can't suppress. Does not have adverse impact. //========================================================================= // CODE SECTION //========================================================================= //== INCLUDE FILES ======================================================== #include // printf() #include // rand(), randomize(), atoi() #include // clock(), CLK_TCK #include // gotoxy(), clrscr(), wherex(), wherey() #include "mancala.h" // MANCALA structure #include "players.h" // Player Functions //== MACRO DEFINITIONS ==================================================== #define FALSE (0) #define TRUE (!FALSE) #define BLANKLINE printf("\n") #define PLAYERPICKMODE (0) //== FUNCTION PROTOTYPES (for Primary Functions) ========================== void PrintHeader(void); void InitializeBoard(char board[2][7]); int SelectStartingPlayer(int how); void PrintBoard(char board[][7], char *playerID[], int player); int IsGameOver(char board[2][7]); int GetPlayerMove(char board[2][7], int player, int (*pfunc)(MANCALA m, int p)); int ExecuteMove(char board[][7], int player, int bin); int Winner(char board[2][7]); void InitializeResultsDisplay(char *PlayerIDpool[], int players); void DisplayResults(long results[PLAYERSINPOOL][PLAYERSINPOOL], int players); void ShowMove(int player, int bin, int mode); void DisplayMatchResults(long results[PLAYERSINPOOL][PLAYERSINPOOL], char *PlayerIDpool[], int players); //== MAIN FUNCTION ======================================================== int main(void) { char board[2][7]; int player, prev_player; int bin; int i, j; int game, games; int show, show_i, show_j, OneIn; clock_t stop; double delay; int (*Playerpool[PLAYERSINPOOL+1])(MANCALA m, int p); int (*Players[2])(MANCALA m, int p); char *PlayerIDpool[PLAYERSINPOOL+1]; char *PlayersID[2]; long results[PLAYERSINPOOL][PLAYERSINPOOL]; char msg[256]; int cmd, exitflag; PrintHeader(); BLANKLINE; randomize(); do { gotoxy(2, 12); printf("Number of games to play in each match (1-9999): "); gotoxy(wherex()-10, 12); fgets(msg, 256, stdin); games = atoi(msg); } while( (games < 1) || (games > 9999) ); do { gotoxy(2, 14); printf("Rounds per game displayed? (0 to suppress): "); gotoxy(wherex()-8, wherey()); fgets(msg, 256, stdin); OneIn = atoi(msg); } while( OneIn < 0 ); do { gotoxy(2, 16); printf("Seconds per move in displayed games: "); gotoxy(wherex()-8, wherey()); fgets(msg, 256, stdin); delay = atof(msg); } while( delay < 0.0 ); gotoxy(2, 18); printf("x - exit s - slower f - faster\n"); gotoxy(2, 19); printf("Note: Only one keystroke processed per move.\n"); gotoxy(2, 21); printf("Hit any key to start tournament"); while(kbhit()) getch(); while(!kbhit()); while(kbhit()) getch(); games /= 2; clrscr(); // 1) TASK: Initial Setup Actions //ClearResults(results, PLAYERSINPOOL); for(i = 0; i < PLAYERSINPOOL; i++) for(j = 0; j < PLAYERSINPOOL; j++) results[i][j] = 0; InitializePlayerPool(Playerpool, PlayerIDpool, PLAYERSINPOOL); InitializeResultsDisplay(PlayerIDpool, PLAYERSINPOOL); DisplayResults(results, PLAYERSINPOOL); for(game = 0, exitflag = FALSE; (!exitflag && (game < games)); game++) { show_i = rand()%PLAYERSINPOOL; show_j = rand()%PLAYERSINPOOL; for(i = 0; i < PLAYERSINPOOL; i++) { for(j = 0; j < PLAYERSINPOOL; j++) { // 1) TASK: Initial Actions for a Single Game Players[0] = Playerpool[i]; Players[1] = Playerpool[j]; PlayersID[0] = PlayerIDpool[i]; PlayersID[1] = PlayerIDpool[j]; if(0 < OneIn) show = ( !(game%OneIn) && (i == show_i) && (j == show_j) ); else show = FALSE; InitializeBoard(board); player = SelectStartingPlayer(PLAYERPICKMODE); if(show) PrintBoard(board, PlayersID, player); // 2) TASK: Play a Game of Mancala cmd = 0; while(!(exitflag || IsGameOver(board)) ) { // 0) TASK: Fetch and process command if(kbhit()) cmd = getch(); switch(cmd) { case 'x': case 'X': exitflag = TRUE; break; case 'f': case 'F': delay = (delay > 0.25)? delay -= 0.25 : 0.0; break; case 's': case 'S': delay = (delay < 10.0)? delay += 0.25 : 10.0; break; } cmd = 0; bin = GetPlayerMove(board, player, Players[player]); prev_player = player; player = ExecuteMove(board, player, bin); if(show) { ShowMove(prev_player, bin, SHOW); stop = clock()+ delay*CLK_TCK; while(stop > clock()) ; PrintBoard(board, PlayersID, player); ShowMove(prev_player, bin, ERASE); } } // 3) TASK: Update the results switch(Winner(board)) { case PLAYERA: results[j][i]++; break; case PLAYERB: results[i][j]++; break; } } } DisplayResults(results, PLAYERSINPOOL); } gotoxy(1,25); printf("Match Play complete - Hit key to display results."); while(kbhit()) getch(); while(!kbhit()); while(kbhit()) getch(); DisplayMatchResults(results, PlayerIDpool, PLAYERSINPOOL); return(0); } //========================================================================= // PRIMARY FUNCTIONS (functions called directly by main() ) //========================================================================= //== FUNCTION PROTOTYPES (for Support Functions) ========================== int printc(char c, int n); int MakeMove(char board[2][7], int player, int bin); int SeedsLeft(char board[2][7], int player); int ForfeitGame(char board[2][7], int player); void SweepBoard(char board[2][7]); //== PRIMARY FUNCTIONS ==================================================== void PrintHeader(void) { printc('=', 79); printf("\n"); printf("Course....... %s-%i (%s %i)\n", COURSE, SECTION, TERM, YEAR); printf("Programmer... %s (%s)\n", PROGRAMMER, PROG_CODE); printf("Assignment... %s (Rev %i) (Source Code in %s)\n", ASSIGNMENT, REVISION, FILENAME); printf("Description.. %s\n", TITLE); printf(" %s\n", SUBTITLE); printc('=', 79); printf("\n"); return; } //========================================================================= // SUPPORT FUNCTIONS (functions not called directly by main() ) //========================================================================= int printc(char c, int n) { while(0 < n--) printf("%c", c); return(n); } //------------------------------------------------------------------------- // TASK: Initialize the Board. //------------------------------------------------------------------------- // // PASSED IN: board // 1) TASK: Emply kalahas // 1.1) SET: board[n][0] = 0 for n = {0->1} // 2) TASK: Load bins // 2.1) SET: board[n][b] = 0 for n = {0->1} and b = {1->6} // 3) RETURN: void InitializeBoard(char board[2][7]) { int player, bin; for(player = 0; player < 2; player ++) { board[player][0] = 0; for(bin = 1; bin < 7; bin++) board[player][bin] = 4; } return; } //------------------------------------------------------------------------- // TASK: Select which player will start. //------------------------------------------------------------------------- // // PASSED IN: // 1) TASK: Select starting player // 1.1) SET: player = random integer in the range (0->1) // 2) RETURN: player int SelectStartingPlayer(int how) { switch(how) { case -1: return(rand()%2); case 0: return(0); case 1: return(1); case 2: randomize(); return(SelectStartingPlayer(-1)); } return(0); } //------------------------------------------------------------------------- // TASK: Display the Game Board //------------------------------------------------------------------------- // // PASSED IN: board // 1) TASK: Display Player B Identifier // 2) TASK: Display Player B Bin Labels // 3) TASK: Display Player B Bin Contents // 4) TASK: Display Dividing Line and Kalaha Contents // 5) TASK: Display Player A Bin Contents // 6) TASK: Display Player A Bin Labels // 7) TASK: Display Player A Identifier void DisplayIdentifier(int side, int player, char *PlayerID[]); void DisplayBinLabels(int side); void DisplayBinContents(int side, char board[2][7]); void DisplayKalahas(char board[2][7]); void PrintBoard(char board[][7], char *PlayerID[], int player) { DisplayIdentifier(PLAYERA, player, PlayerID); DisplayBinLabels(PLAYERA); DisplayBinContents(PLAYERA, board); DisplayKalahas(board); DisplayBinContents(PLAYERB, board); DisplayBinLabels(PLAYERB); DisplayIdentifier(PLAYERB, player, PlayerID); fflush(stdout); return; } void DisplayIdentifier(int side, int player, char *PlayerID[]) { int c; c = (side==player)? '*' : ' '; gotoxy(BOARDEDGE - 6 + 71 * side, (BOARDCENTER - 1) - 3 + 6*side); printc(c, 6); gotoxy(BOARDEDGE - 6 + 71 * side, (BOARDCENTER) - 3 + 6*side); printf(" %4s ", PlayerID[side]); gotoxy(BOARDEDGE - 6 + 71 * side, (BOARDCENTER + 1) - 3 + 6*side); printc(c, 6); return; } void DisplayBinLabels(int side) { int bin; // Top Line gotoxy(BOARDEDGE, (BOARDCENTER - 4) + 6 * side); printc('=', 65); // Bin Labels gotoxy(BOARDEDGE, (BOARDCENTER - 3) + 6 * side); printf("| |"); switch(side) { case PLAYERA: for(bin = 1; bin <= 6; bin++) printf(" %1i |", bin); break; case PLAYERB: for(bin = 6; bin >= 1; bin--) printf(" %1i |", bin); break; } printf(" |"); // Bottom Line gotoxy(BOARDEDGE, (BOARDCENTER-2) + 6 * side); printc('=', 65); printf(""); return; } void DisplayBinContents(int side, char board[2][7]) { int bin; // Bin Contents gotoxy(BOARDEDGE, (BOARDCENTER-1) + 2 * side); printf("| |"); switch(side) { case PLAYERA: for(bin = 1; bin <= 6; bin++) printf(" %2i |", board[side][bin]); break; case PLAYERB: for(bin = 6; bin >= 1; bin--) printf(" %2i |", board[side][bin]); break; } printf(" |"); return; } void DisplayKalahas(char board[2][7]) { int bin; gotoxy(BOARDEDGE, BOARDCENTER); printf("| %2i |", board[PLAYERA][0]); for(bin = 1; bin <= 6; bin++) printf("=======|"); printf(" %2i |", board[PLAYERB][0]); return; } //------------------------------------------------------------------------- // TASK: Check if Game is over //------------------------------------------------------------------------- // // PASSED IN: board // 1) TASK: Check for any seeds in any bins other than kalahas // 1.1) IF( sum of seeds in both kalahas == 48 ) // 1.1.1) SET: gameover = TRUE // 1.2) ELSE // 1.2.1) SET: gameover = FALSE // 2) RETURN: gameover int IsGameOver(char board[2][7]) { return( 48 == (board[PLAYERA][KALAHA] + board[PLAYERB][KALAHA]) ); } //------------------------------------------------------------------------- // TASK: Get move from present player. //------------------------------------------------------------------------- // // PASSED IN: board, player // 1) TASK: Prompt player which bin they want to sow. // 2) TASK: Get number of bin to sow from player. // 3) RETURN: bin int GetPlayerMove(char board[2][7], int player, int (*pfunc)(MANCALA m, int p)) { int i; int bin; MANCALA m; for(i = 0; i < 7; i++) { (m.board)[0][i] = board[0][i]; (m.board)[1][i] = board[1][i]; } bin = (*pfunc)(m, player); return(bin); } //------------------------------------------------------------------------- // TASK: Check if move is legal //------------------------------------------------------------------------- // // PASSED IN: board, player, bin // 1) TASK: Check that the player's bin has at least one seed // 1.1) IF( board[player][bin] > 0) // 1.1.2) SET: legal = TRUE // 1.2) ELSE // 1.2.1) SET: legal = FALSE // RETURN: legal int IsMoveLegal(char board[2][7], int player, int bin) { return( (0bin) && (0 < board[player][bin]) ); } //------------------------------------------------------------------------- // TASK: Execute the move //------------------------------------------------------------------------- // // 1) IF: (move is not legal) // 1.1) TASK: Make present player forfeit game. // 1.1.1) OUT: "Illegal Move" message to user. // 1.1.2) SET: bin = 0 (to inform the Execute Move task of the forfeit. // 2) TASK: Make the move // 3) RETURN: Next player (return value from Make Move) int ExecuteMove(char board[2][7], int player, int bin) { if( !IsMoveLegal(board, player, bin) ) bin = 0; return(MakeMove(board, player, bin)); } //------------------------------------------------------------------------- // TASK: Execute the move //------------------------------------------------------------------------- // // PASSED IN: board, player, bin // 1) IF: (game is forfeit) // 1.1) TASK: Empty all bins // 1.2) TASK: Empty player's kalaha // 1.3) TASK: Place all seeds in other players kalaha // 2) ELSE: // 2.1) TASK: Empty the bin to be sown // 2.1.1) SET: board[player][bin] = 0 // 2.2) TASK: Sow the seeds around the board // 3) IF: (last seed placed in empty bin on player's side) // 3.1) TASK: Place last seed sown in player's kalaha // 3.1.1) SET: board[player][kalaha] += 1 // 3.1.2) SET: board[player][lastbin] = 0 // 3.2) TASK: Place seeds in bin opposite in player's kalaha // 3.2.1) SET: board[player][kalaha] += board[other player][7-lastbin] // 3.2.2) SET: board[other player][7 - lastbin] = 0 // 3) IF: (last seed placed in player's kalaha) // 3.1) SET: nextplayer = player // 4) ELSE: // 4.1) SET: nextplayer = other player // 5) RETURN: nextplayer int MakeMove(char board[2][7], int player, int bin) { int side, seeds; // Act on a Forfeit condition if(0 == bin) return(ForfeitGame(board, player)); // returns -1 // Perform the requested (legal) move side = player; seeds = board[player][bin]; board[player][bin] = 0; while(seeds > 0) { bin--; // Move to next counterclockwise bin if( (0 == bin) && (side != player) ) // Jump over opponents kalaha bin--; if(0 > bin) // Moved past the kalaha - got to other side { side = !side; bin = 6; } board[side][bin]++; seeds--; } // IF: last seed placed in an empty bin on player's side - capture if( (side == player) && (0 < bin) && (1 == board[side][bin]) ) { // Place last seed sown in player's kalaha board[player][KALAHA] += 1; board[player][bin] = 0; // TASK: Place seeds in bin opposite in player's kalaha board[player][KALAHA] += board[!player][7-bin]; board[!player][7-bin] = 0; } // IF: Last seed not into player's kalaha - opponent's turn if( !( (0 == bin) && (side == player) ) ) player = !player; // Check for End of Game (either player has no seeds left) if(0 == (SeedsLeft(board, PLAYERA) * SeedsLeft(board, PLAYERB)) ) SweepBoard(board); // Check for End of Game (either kalaha has more than 24 seeds) if( (24 < board[PLAYERA][KALAHA]) || (24 < board[PLAYERB][KALAHA]) ) SweepBoard(board); return(player); } int SeedsLeft(char board[2][7], int player) { int bin, seeds; for(seeds = 0, bin = 1; bin < 7; bin++) seeds += board[player][bin]; return(seeds); } int ForfeitGame(char board[2][7], int player) { int bin; board[player][0] = 0; board[!player][0] = 48; for(player = 0; player < 2; player ++) { for(bin = 1; bin < 7; bin++) board[player][bin] = 0; } return(-1); } void SweepBoard(char board[2][7]) { int player, bin; for(player = 0; player < 2; player ++) { for(bin = 1; bin < 7; bin++) { board[player][0] += board[player][bin]; board[player][bin] = 0; } } return; } //------------------------------------------------------------------------- // TASK: Display the results //------------------------------------------------------------------------- // // PASSED IN: board // 1) TASK: Display final score // 1.1) OUT: Player A: board[0][kalaha] // 1.2) OUT: Player B: board[1][kalaha] // 2) TASK: Announce winner // 2.1) IF: (board[0][kalaha] == board[1][kalaha]) // 2.1.1) OUT: Tie Game // 2.2) ELSE: // 2.2.1) IF: (board[0][kalaha] > board[1][kalaha]) // 2.2.1.1) OUT: Player A wins // 2.2.2) ELSE: // 2.2.2.1) OUT: Player B wins int Winner(char board[2][7]) { int winner; if(board[PLAYERA][KALAHA] > board[PLAYERB][KALAHA]) winner = PLAYERA; if(board[PLAYERA][KALAHA] < board[PLAYERB][KALAHA]) winner = PLAYERB; if(board[PLAYERA][KALAHA] == board[PLAYERB][KALAHA]) winner = TIEGAME; return(winner); } void InitializeResultsDisplay(char *PlayerIDpool[], int players) { int i, j; gotoxy(1,1); printf("L W|"); for(i = 0; i < players; i++) { gotoxy(6+5*i, 1); printf("%4s|", PlayerIDpool[i]); gotoxy(1, 2+i); printf("%4s|", PlayerIDpool[i]); for(j = 0; j < players; j++) printf(" |"); } return; } void DisplayResults(long results[PLAYERSINPOOL][PLAYERSINPOOL], int players) { int i, j; for(i = 0; i < players; i++) { for(j = 0; j < players; j++) { gotoxy(6+5*j, 2+i); printf("%4li", results[i][j]); } } return; } void DisplayMatchResults(long results[PLAYERSINPOOL][PLAYERSINPOOL], char *PlayerIDpool[], int players) { int player, j; int tally[PLAYERSINPOOL][4]; int points, rank, ties; for(j = 2+players; j <= 25; j++) { gotoxy(1, j); printc(' ', 79); } for(player = 0; player < players; player++) for(j = 0; j < 4; j++) tally[player][j] = 0; for(player = 0; player < players; player++) { for(j = 0; j < players; j++) { gotoxy(6+5*player, 2+j); if(results[player][j] < results[j][player]) { // WIN printf("%4s", PlayerIDpool[player]); tally[player][0]++; } if(results[player][j] > results[j][player]) { // LOSE printf("%4s", PlayerIDpool[j]); tally[player][1]++; } if(results[player][j] == results[j][player]) { printf("DRAW"); tally[player][2]++; } } } for(j = 0; j < 6; j++) { gotoxy(1, 3+players+j); switch(j) { case 0: printf(" WIN|"); break; case 1: printf("LOSE|"); break; case 2: printf("DRAW|"); break; case 3: printf("----|"); break; case 4: printf(" PTS|"); break; case 5: printf("RANK|"); break; } for(player = 0; player < players; player++) { gotoxy(6+5*player, 3+players+j); switch(j) { case 0: case 1: case 2: printf("%4i|", tally[player][j]); break; case 3: printf("----|"); break; case 4: tally[player][3] = 2*tally[player][0] + tally[player][2]; printf("%4i|", tally[player][3]); break; } } } rank = 1; for(points = 2*players; points > 0; points--) { ties = 0; for(player = 0; player < players; player++) { if(points == tally[player][3]) { gotoxy(6+5*player, 3+players+5); printf("%4i|", rank); ties++; } } rank+=ties; } return; } void ShowMove(int player, int bin, int mode) { int start; if( (bin<1)||(bin>6) ) return; switch(player) { case PLAYERA: start = BOARDEDGE + 1 + 8*bin; break; case PLAYERB: start = BOARDEDGE + 1 + 8*(7-bin); break; } gotoxy(start, (BOARDCENTER - 3) + 6 * player); switch(mode) { case SHOW: printf("**"); break; case ERASE: printf(" "); break; } gotoxy(start + 5, (BOARDCENTER - 3) + 6 * player); switch(mode) { case SHOW: printf("**"); break; case ERASE: printf(" "); break; } return; }