summaryrefslogtreecommitdiffstats
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c358
1 files changed, 358 insertions, 0 deletions
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..93a71c2
--- /dev/null
+++ b/main.c
@@ -0,0 +1,358 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <termios.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+
+#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
+}