#include #include #include #include #include #include #include #define clear() printf("\033[H\033[J") //the board as a 1D array. It's global due to the want to easily access in from any function. char board[] ={"|---|---|---|\n| - | - | - |\n|---|---|---|\n| - | - | - |\n|---|---|---|\n| - | - | - |\n|---|---|---|\n"}; struct termios oldt,newt;//the struct used by the terminal mode changer. Global due to want of easy access from any function. int pos[3][3] = { {16,20,24}, {44,48,52}, {72,76,80} }; //each number matches up to the position where the pieces should go on the board int moves = 0; //the amount of moves done by the computer. void checkWin(void)//check if the board is currently in a winning state { //leftright int x; int y; int Xcount = 0; int Ocount = 0; for (x=0;x<3;++x) { for (y=0;y<3;++y) { if(board[pos[x][y]] == 'X'){++Xcount;} if(board[pos[x][y]] == 'O'){++Ocount;} if (Xcount == 3){goto Xwin;} if (Ocount == 3){goto Owin;} } Xcount = 0; Ocount = 0; } //up/down for (x=0;x<3;++x) { for (y=0;y<3;++y) { if(board[pos[y][x]] == 'X'){++Xcount;} if(board[pos[y][x]] == 'O'){++Ocount;} if (Xcount == 3){goto Xwin;} if (Ocount == 3){goto Owin;} } Xcount = 0; Ocount = 0; } //across topleft-bottomright if ((board[pos[0][0]] == 'X') && (board[pos[1][1]] == 'X') && (board[pos[2][2]] == 'X') ){goto Xwin;} if ((board[pos[0][0]] == 'O') && (board[pos[1][1]] == 'O') && (board[pos[2][2]] == 'O') ){goto Owin;} //across bottomleft-topright if ((board[pos[2][0]] == 'X') && (board[pos[1][1]] == 'X') && (board[pos[0][2]] == 'X') ){goto Xwin;} if ((board[pos[2][0]] == 'O') && (board[pos[1][1]] == 'O') && (board[pos[0][2]] == 'O') ){goto Owin;} goto skip;//Skip over the winning exit code //exit here due to lack of want to exit 2(?) layers deep of loops Xwin: printf("Pathetic.\n"); tcsetattr( STDIN_FILENO, TCSANOW, &oldt ); exit(0); //return the terminal back to it's old state and exit Owin: printf("You win! (Good job)\n"); tcsetattr( STDIN_FILENO, TCSANOW, &oldt ); exit(0); //return the terminal back to it's old state and exit skip:; } void otherPlayer(void)//The other player takes a move when this function is called. { int x; int y; bool tie = true; for (x=0;x<3;++x) { for (y=0;y<3;++y) { if (board[pos[x][y]] == '-'){tie = false;} } } if (tie){printf("It's a tie. \n"); tcsetattr(STDIN_FILENO,TCSANOW,&oldt); exit(0);} //if it's the second turn, there are a limited amount of moves to make if (!moves) { if (board[pos[1][1]] == 'O')//if first move was center, a move in the 4 non-diaginal places should be made { int randNum = rand() % 4;//generate a pseudorandom number from 0 to 3. switch(randNum) { case 0: board[pos[0][0]] = 'X'; goto skip; case 1: board[pos[2][0]] = 'X'; goto skip; case 2: board[pos[0][2]] = 'X'; goto skip; case 3: board[pos[2][2]] = 'X'; goto skip; } } //if the player placed anywhere else, place in the center board[pos[1][1]] = 'X'; goto skip; } //leftright int Xcount = 0; for (x=0;x<3;++x) { for (y=0;y<3;++y) { if(board[pos[x][y]] == 'X'){++Xcount;} if ((Xcount == 2)&&(board[pos[x][y+1]] == '-')){board[pos[x][y+1]] = 'X'; goto skip;} } Xcount = 0; } //up/down for (x=0;x<3;++x) { for (y=0;y<3;++y) { if(board[pos[y][x]] == 'X'){++Xcount;} if ((Xcount == 2)&&(board[pos[x+1][y]] == '-')){board[pos[x+1][y]] = 'X'; goto skip;} } Xcount = 0; } //across topleft-bottomright if ((board[pos[0][0]] == 'X') && (board[pos[1][1]] == 'X') && (board[pos[2][2]] == '-') ){board[pos[2][2]] = 'X'; goto skip;} if ((board[pos[0][0]] == '-') && (board[pos[1][1]] == 'X') && (board[pos[2][2]] == 'X') ){board[pos[0][0]] = 'X'; goto skip;} //across bottomleft-topright if ((board[pos[2][0]] == '-') && (board[pos[1][1]] == 'X') && (board[pos[0][2]] == 'X') ){board[pos[2][0]] = 'X'; goto skip;} if ((board[pos[2][0]] == 'X') && (board[pos[1][1]] == 'X') && (board[pos[0][2]] == '-') ){board[pos[0][2]] = 'X'; goto skip;} //copy the position of O's into an array for later processing bool Ostate[3][3]; for (x=0;x<3;++x) { for (y=0;y<3;++y) { if (board[pos[x][y]] == 'O'){Ostate[x][y] = true;} else {Ostate[x][y] = false;} } } bool Nstate[3][3]; //state is empty for (x=0;x<3;++x) { for (y=0;y<3;++y) { if (board[pos[x][y]] == '-'){Nstate[x][y] = true;} else {Nstate[x][y] = false;} } } //check for double moves including the center piece if (Ostate[1][1]) { int x; int y; int newx; int newy; for (x=0;x<3;++x) { for (y=0;y<3;++y) { if (Ostate[x][y]) { //calculate move by applying these rules; if (x == 0){x=2;} if (x == 1){x=1;} if (x == 2){x=0;} if (!x){newx=2;} if (x==1){newx=1;} if (x==2){newx=0;} if (!y){newy=2;} if (y==1){newy=1;} if (y==2){newy=0;} if (board[pos[newx][newy]] == '-'){board[pos[newx][newy]] = 'X'; goto skip;} } } } } //check for double moves including gap in space if ((Ostate[0][0])&&(Ostate[0][2])&&(Nstate[0][1])){board[pos[0][1]] = 'X'; goto skip;} if ((Ostate[0][2])&&(Ostate[2][2])&&(Nstate[1][2])){board[pos[1][2]] = 'X'; goto skip;} if ((Ostate[2][2])&&(Ostate[2][0])&&(Nstate[2][1])){board[pos[2][1]] = 'X'; goto skip;} if ((Ostate[2][0])&&(Ostate[0][0])&&(Nstate[1][0])){board[pos[1][0]] = 'X'; goto skip;} //check for double moves not including center diagonally and vertically //top row if ((Ostate[0][0])&&(Ostate[0][1])&&(Nstate[0][2])){board[pos[0][2]] = 'X'; goto skip;} if ((Ostate[0][2])&&(Ostate[0][1])&&(Nstate[0][0])){board[pos[0][0]] = 'X'; goto skip;} //right down if ((Ostate[0][2])&&(Ostate[1][2])&&(Nstate[2][2])){board[pos[2][2]] = 'X'; goto skip;} if ((Ostate[2][2])&&(Ostate[1][2])&&(Nstate[0][2])){board[pos[0][2]] = 'X'; goto skip;} //bottom row if ((Ostate[2][0])&&(Ostate[2][1])&&(Nstate[2][2])){board[pos[2][2]] = 'X'; goto skip;} if ((Ostate[2][2])&&(Ostate[2][1])&&(Nstate[2][0])){board[pos[2][0]] = 'X'; goto skip;} //left down if ((Ostate[2][0])&&(Ostate[1][0])&&(Nstate[0][0])){board[pos[0][0]] = 'X'; goto skip;} if ((Ostate[0][0])&&(Ostate[1][0])&&(Nstate[2][0])){board[pos[2][0]] = 'X'; goto skip;} //if all above was passed, just peform a random move redo: x = rand() % 4;//generate a pseudorandom number from 0 to 3. y = rand() % 4;//ditto if (board[pos[x][y]] == '-'){board[pos[x][y]] = 'X';} else{goto redo;}//generate random numbers again if the space wasn't free skip: clear();//clear the terminal printf("%s", board);//print the new board ++moves; } int main(int argc, char *argv[]) { if (argc > 1) { printf("Usage: %s\n\n", argv[0]); printf("Controls:\nArrow keys to move cursor.\nPress any other key to take a move.\n\n"); printf("I am aware that this is badly written.\n"); exit(0); } srand(time(NULL));//seed a srandom number generator very poorly with the time(should only be done once) //modified is simply a copy of the board with the cursor written on it char modified[] = { "|---|---|---|\n| - | - | - |\n|---|---|---|\n| - | - | - |\n|---|---|---|\n| - | - | - |\n|---|---|---|\n" }; char c;//char used to recieve user input tcgetattr(STDIN_FILENO, &oldt);//get the current state of the terminal newt = oldt;//set the new state to the old state newt.c_lflag &= ~( ICANON | ECHO );//disable ICANON and ECHO tcsetattr(STDIN_FILENO,TCSANOW,&newt);//set the terminal to the new state int cursor = 48;//the position of the cursor on the board(default is the center) modified[cursor] = '#'; //Write the marker char to the current location of the cursor clear(); //clear the screen ready for the board printf("%s", modified);//print board with marker on it while(1)//loop until a win or tie { if (getchar() == '\033')//ingnore escape char in front the symbol used to carry arrow key presses { getchar();//ignore '[' c = getchar();//Get th arrow state. switch(c)//an arrow is pressed if 'A', 'B', 'C' or 'D' is recieved. { case 'A': if (cursor-14 < 0){break;}//Don't try to go up if we are on the first row cursor-=14;//move cursor to previous row memcpy(modified,board,strlen(board)+1);//refresh modified with board modified[cursor]='#';//write a marking char to the position of cursor clear();//clear the screen printf("%s", modified);//print the modified board //printf("Up arrow\n"); break; case 'B': if (cursor+14 >= strlen(board)){break;}//If we are on the last row, don't increment further cursor+=14;//move x to next row memcpy(modified,board,strlen(board)+1);//refresh bord with a blank one modified[cursor]='#';//write a marking char to the position clear();//clear the screen printf("%s", modified);//print the modified board //printf("Down arrow\n"); break; case 'C': if (cursor==(strlen(board+1))){break;}//don't go past the end of the board ++cursor;//move cursor right if ((board[cursor]) == '\n'){--cursor; break;}//If going right will lead to a newline, don't memcpy(modified,board,strlen(board)+1);//refresh board with the default modified[cursor]='#';//write a marking char to the position clear();//clear the screen printf("%s", modified);//print the modified board //printf("Right arrow\n"); break; case 'D': if (cursor-1<0){break;}//don't attempt to go to -1 in board --cursor;//move cursor left if ((board[cursor]) == '\n'){++cursor; break;}//If going left will lead to a newline, don't memcpy(modified,board,strlen(board)+1);//refresh board with the default modified[cursor]='#';//write a marking char to the position clear();//clear the screen printf("%s", modified);//print the modified board //printf("Left arrow\n"); break; default: clear(); //to correct an issue where is seems that any key resulted in the last pressed arrow key direction, the board is cleared and reset. printf("%s", modified); break; } } else //if any other key than an arrow key was pressed, we end up here { int x;//x cord. int y;//y cord. for(x=0;x<3;++x) { for(y=0;y<3;++y) { if (pos[x][y] == cursor){goto checkBoard;}//If cursor is the position of a valid move place, keep the value of x and y } } goto noMove; //if the above goto doesn't trigger, cursor is not in a valid move place checkBoard: if (board[pos[x][y]] != '-'){goto noMove;}//move has already been made board[pos[x][y]] = 'O';//write an 'O' to the board. modified[pos[x][y]] = 'O';//write that to the modified board too. clear();//clear the terminal printf("%s", modified);//print the modified board checkWin();//check if the move made was a winning move. otherPlayer();//make the CPU take a move checkWin();//check if the CPU move made was a winning one. noMove: ; } } tcsetattr(STDIN_FILENO,TCSANOW,&oldt);//return the terminal back to it's old state, this should never be reached though. return 1;//if we exit here, something has gone wrong }