GitHub: https://github.com/synthVerity/PersonalAIProjects/blob/master/TicTacToeAI.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/********************************************************************* | |
* Tic-Tac-Toe game set up for experimentation of the AI player. | |
* Not that fancy, nor that well made. Most effort was made in making | |
* this work before being fancy. | |
* | |
* Problems: | |
* Game immediately shuts down on game end | |
* No chance to play again without program restart | |
* AI is not difficult or optimized | |
* Random nature is less strategic and more lucky | |
* | |
* Favorable Thoughts: | |
* AI was created without any prior experience or research | |
* Same for the game | |
* | |
* Compiled on Linux with gcc and libncurses5-dev | |
* Created by: Austin Karn(synthVerity) | |
*********************************************************************/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <ncurses.h> | |
#include <time.h> | |
void setPiece(int x, int y, int turn); | |
void printBoard(); | |
char pieceCheck(int *turn); | |
void AIPlayer(); | |
int checkWin(); | |
// Globals make me uncomfortable, but it was the best solution | |
int board[3][3] = { | |
{0, 0, 0}, | |
{0, 0, 0}, | |
{0, 0, 0} | |
}; | |
// Obvious main is obvious | |
int main(int argc, char *argv[]) | |
{ | |
// Basic set-up of the variables and screen | |
srand(time(NULL)); | |
initscr(); | |
keypad(stdscr, TRUE); | |
nodelay(stdscr, TRUE); | |
// Whoever begins is random, to take away complete first move advantages | |
int turn = rand() % 2 + 1; | |
int x = 1, y = 0; | |
int running = 1; | |
// Main loop of the game | |
while(running) | |
{ | |
// Basic set-up of ascii board and text output | |
erase(); | |
printBoard(); | |
printw("Please input grid location using arrow keys and Enter. F1 to exit.\n"); | |
printw("It's %c turn.", pieceCheck(&turn)); | |
move(y, x); | |
// Used to check whether program should terminate or not. | |
running = checkWin(); | |
// User input for movement and piece placement | |
switch(getch()) | |
{ | |
case KEY_F(1): | |
running = 0; | |
break; | |
case KEY_UP: | |
if(y != 0) | |
y -= 2; | |
break; | |
case KEY_DOWN: | |
if(y < 4) | |
y += 2; | |
break; | |
case KEY_LEFT: | |
if(x > 1) | |
x -= 4; | |
break; | |
case KEY_RIGHT: | |
if(x < 8) | |
x += 4; | |
break; | |
case 10: | |
setPiece(x, y, turn); | |
if(turn == 1) turn = 2; | |
else turn = 1; | |
break; | |
} | |
} | |
// Cleanup of program and ncurses | |
endwin(); | |
return 0; | |
} | |
// Function used to place piece according to where the cursor is located. | |
// Only can be used by human player | |
void setPiece(int x, int y, int turn) | |
{ | |
if(board[y/2][(x-1)/4] == 0) | |
{ | |
board[y/2][(x-1)/4] = turn; | |
} | |
return; | |
} | |
// Prints out the board using the board[] array | |
void printBoard() | |
{ | |
int i, j; | |
char piece; | |
for(i = 0; i < 3; i++) | |
{ | |
for(j = 0; j< 3; j++) | |
{ | |
if(board[i][j] == 0) piece = ' '; | |
else if(board[i][j] == 1) piece = 'X'; | |
else piece = 'O'; | |
if(j < 2) printw(" %c |", piece); | |
else printw(" %c ", piece); | |
} | |
if(i < 2) printw("\n———–\n"); | |
else printw("\n"); | |
} | |
return; | |
} | |
// Checks whose turn it is, calling on the AI when it's turn is up | |
char pieceCheck(int *turn) | |
{ | |
if(*turn == 1) return 'X'; | |
else | |
{ | |
AIPlayer(); | |
*turn = 1; | |
return ' '; | |
} | |
} | |
// AI function, quite long, not optimized much, easily defeated | |
// Created without research on tic-tac-toe perfect games, or outside assistance | |
// Needs to be improved | |
void AIPlayer() | |
{ | |
int i, j; | |
if(board[0][0] == board[1][1] && board[2][2] == 0 && board[0][0] != 0) | |
{ | |
board[2][2] = 2; | |
return; | |
} | |
else if(board[1][1] == board[2][2] && board[0][0] == 0 && board[1][1] != 0) | |
{ | |
board[0][0] = 2; | |
return; | |
} | |
else if(board[0][0] == board[2][2] && board[1][1] == 0 && board[0][0] != 0) | |
{ | |
board[1][1] = 2; | |
return; | |
} | |
else if(board[0][2] == board[1][1] && board[2][0] == 0 && board[0][2] != 0) | |
{ | |
board[2][0] = 2; | |
return; | |
} | |
else if(board[1][1] == board[2][0] && board[0][2] == 0 && board[1][1] != 0) | |
{ | |
board[0][2] = 2; | |
return; | |
} | |
else if(board[0][2] == board[2][0] && board[1][1] == 0 && board[0][2] != 0) | |
{ | |
board[1][1] = 2; | |
return; | |
} | |
for(i = 0; i < 3; i++) | |
{ | |
if(board[i][0] == board[i][1] && board[i][2] == 0 && board[i][0] != 0) | |
{ | |
board[i][2] = 2; | |
return; | |
} | |
else if(board[i][1] == board[i][2] && board[i][0] == 0 && board[i][1] != 0) | |
{ | |
board[i][0] = 2; | |
return; | |
} | |
else if(board[i][0] == board[i][2] && board[i][1] == 0 && board[i][0] != 0) | |
{ | |
board[i][1] = 2; | |
return; | |
} | |
else if(board[0][i] == board[1][i] && board[2][i] == 0 && board[0][i] != 0) | |
{ | |
board[2][i] = 2; | |
return; | |
} | |
else if(board[1][i] == board[2][i] && board[0][i] == 0 && board[1][i] != 0) | |
{ | |
board[0][i] = 2; | |
return; | |
} | |
else if(board[0][i] == board[2][i] && board[1][i] == 0 && board[0][i] != 0) | |
{ | |
board[1][i] = 2; | |
return; | |
} | |
} | |
// If no obvious winning moves or blocks to be done, takes the middle slot(if open) | |
// If the middle slot is not possible, then take a random slot | |
// While it's not useful, makes the game more interesting | |
for(;;) | |
{ | |
if(board[1][1] == 0) | |
{ | |
board[1][1] = 2; | |
break; | |
} | |
i = rand() % 3; | |
j = rand() % 3; | |
if(board[i][j] == 0) | |
{ | |
board[i][j] = 2; | |
break; | |
} | |
} | |
return; | |
} | |
// Checks the board to see if there are winning conditions or a draw | |
int checkWin() | |
{ | |
int i, j; | |
if(board[1][1] != 0) | |
{ | |
if(board[0][0] == board[1][1] && board[1][1] == board[2][2]) | |
return 0; | |
if(board[0][2] == board[1][1] && board[1][1] == board[2][0]) | |
return 0; | |
} | |
for(i = 0; i < 3; i++) | |
{ | |
if(board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != 0) | |
return 0; | |
if(board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != 0) | |
return 0; | |
} | |
for(i = 0; i < 3; i++) | |
{ | |
for(j = 0; j < 3; j++) | |
{ | |
if(board[i][j] == 0) | |
{ | |
return 1; | |
} | |
} | |
} | |
return 0; | |
} |