//Name: Aaron Gadberry

#include<iostream.h>
#include<stdlib.h>
#include<math.h>
#include<iomanip.h>
#include<fstream.h>
#include<string.h>

bool same_row[10][10];
bool same_col[10][10];
bool same_diag[10][10];
bool adjacent[10][10];
bool diagonal[10][10];
bool touching[10][10];
char template_board[3][3];

//initialization
int clear( char board[3][3] );
int init( );

//gameplay
int game_loop( char board[3][3] );
int user_move( char board[3][3] );
int computer_move( char board[3][3] );
int get_possible_moves( char board[3][3], int possible_moves[10] );
int get_move_priorities( char board[3][3], int possible_moves[10] );
int get_best_move( char board[3][3], int possible_moves[10] );
int is_winner( char board[3][3] );
int col_winner( char board[3][3] );
int row_winner( char board[3][3] );
int diag_winner( char board[3][3] );
int next_move_win( char board[3][3], char symbol );
int col( int move );
int row( int move );
int move( int col, int row );

//rules
int rule_01( char board[3][3], int possible_moves[10] );
int rule_02( char board[3][3], int possible_moves[10] );
int rule_03( char board[3][3], int possible_moves[10] );
int rule_04( char board[3][3], int possible_moves[10] );
int rule_05( char board[3][3], int possible_moves[10] );
int rule_06( char board[3][3], int possible_moves[10] );
int rule_07( char board[3][3], int possible_moves[10] );

//utility
int display( char board[3][3] );
int display_board( char board[3][3] );
int copy_board( char board[3][3], char copy[3][3] );
void error( char * msg );

int main( void )
{
	char board[3][3];
	int result = 0;
	if( !( clear( board ) ) )
		error( "Error in clear()" );
	if( !( init( ) ) )
		error( "Error in init()" );
	result = game_loop( board );
	if( result == 0 )
	{
		cout << "Tie Game!" << endl;
	}
	else if( result == 1 )
	{
		cout << "Computer Wins!" << endl;
	}
	else
	{
		cout << "You Win!" << endl;
	}

	return 0;
}

/***************************************
************INITIALIZATION**************
***************************************/

int clear( char board[3][3] )
{
	for( int row = 0; row < 3; row++ )
	{
		for( int col = 0; col < 3; col++ )
		{
			board[col][row] = ' ';
		}
	}
	return 1;
}

int init( )
{
	for( int focus = 1; focus < 10; focus++ )
	{
		for( int compared = 1; compared < 10; compared++ )
		{
			if( col( focus ) == col( compared ) && ( ( row( focus ) - row( compared ) ) == 1 || ( row( focus ) - row( compared ) ) == -1 ) )
			{
				adjacent[focus][compared] = 1;
				same_col[focus][compared] = 1;
				touching[focus][compared] = 1;
			}
			else if( row( focus ) == row( compared ) && ( ( col( focus ) - col( compared ) ) == 1 || ( col( focus ) - col( compared ) ) == -1 ) )
			{
				adjacent[focus][compared] = 1;
				same_row[focus][compared] = 1;
				touching[focus][compared] = 1;
			}
			if( ( col( focus ) - col( compared ) == 1 || col( focus ) - col( compared ) == -1 ) && ( row( focus ) - row( compared ) == 1 || row( focus ) - row( compared ) == -1 ) )
			{
				diagonal[focus][compared] = 1;
				same_diag[focus][compared] = 1;
				touching[focus][compared] = 1;
			}
		}
	}

	//template_board
	int cell = 1;
	for( int row = 2; row >= 0; row-- )
	{
		for( int col = 0; col < 3; col++ )
		{
			template_board[col][row] = char( 48 + cell++ );
		}
	}
	
	return 1;
}

/***************************************
**************GAMEPLAY******************
***************************************/

int game_loop( char board[3][3] )
{
	int num_moves = 0;

	while( 1 )
	{
		if( !( display( board ) ) )
			error( "Error in display()" );

		if( is_winner( board ) )
			return 1;

		if( !( user_move( board ) ) )
			error( "Error in user_move()" );

		num_moves++;
		
		if( !( display( board ) ) )
			error( "Error in display()" );

		if( is_winner( board ) )
			return 2;

		if( num_moves > 8 )
		{
			return 0;
		}

		if( !( computer_move( board ) ) )
			error( "Error in computer_move()" );

		num_moves++;
	}
	return 1;
}

int user_move( char board[3][3] )
{
	int move;
	move = 0;
	while( ( move < 1 || move > 9 ) || board[col( move )][row( move )] != ' ' )
	{
		cout << "Move:";
		cin >> move;
	}
	board[col( move )][row( move )] = 'X';
	cout << "user moved in " << move << endl;
	return 1;
}

int computer_move( char board[3][3] )
{
	int move;
	int possible_moves[10];

	if( !( get_possible_moves( board, possible_moves ) ) )
		error( "Error in get_possible_moves()" );

	if( !( get_move_priorities( board, possible_moves ) ) )
		error( "Error in get_move_priorities()" );

	if( ( move = get_best_move( board, possible_moves ) ) < 1 )
		error( "Error in get_best_move" );

	board[col( move )][row( move )] = 'O';
	cout << "computer moved in " << move << " with priority " << possible_moves[move] << endl;
	return 1;
}

int get_possible_moves( char board[3][3], int possible_moves[10] )
{
	for( int move = 1; move < 10; move++ )
	{
		if( board[col( move )][row( move )] == ' ' )
		{
			possible_moves[move] = 1;
		}
		else
		{
			possible_moves[move] = 0;
		}
	}
	return 1;
}

int get_move_priorities( char board[3][3], int possible_moves[10] )
{
	int move;

	move = rule_07( board, possible_moves );
	if( move )
	{
		cout << "Rule 7 fired for move " << move << endl;
		possible_moves[move] = 2;
	}

	move = rule_06( board, possible_moves );
	if( move )
	{
		cout << "Rule 6 fired for move " << move << endl;
		possible_moves[move] = 3;
	}

	if( move = rule_05( board, possible_moves ) )
	{
		cout << "Rule 5 fired for move " << move << endl;
		possible_moves[move] = 4;
	}

	if( move = rule_04( board, possible_moves ) )
	{
		cout << "Rule 4 fired for move " << move << endl;
		possible_moves[move] = 5;
	}

	if( move = rule_03( board, possible_moves ) )
	{
		cout << "Rule 3 fired for move " << move << endl;
		possible_moves[move] = 5;
	}

	if( move = rule_02( board, possible_moves ) )
	{
		cout << "Rule 2 fired for move " << move << endl;
		possible_moves[move] = 8;
	}

	if( move = rule_01( board, possible_moves ) )
	{
		cout << "Rule 1 fired for move " << move << endl;
		possible_moves[move] = 9;
	}

	return 1;
}

int get_best_move( char board[3][3], int possible_moves[10] )
{
	int largest = 0;
	possible_moves[largest] = 0;
	for( int move = 1; move < 10; move++ )
	{
		if( possible_moves[move] > possible_moves[largest] )
		{
			largest = move;
		}
		cout << "Move " << move << " has " << possible_moves[move] << " priority" << endl;
	}
	while( largest == 0 || board[col( largest )][row( largest )] != ' ' )
	{
		largest = rand() % 10;
		cout << "Random move generated " << largest << endl;
	}
	return largest;
}

int is_winner( char board[3][3] )
{
	if( col_winner( board ) || row_winner( board ) || diag_winner( board ) )
		return 1;
	else
		return 0;
}

int col_winner( char board[3][3] )
{
	for( int col = 0; col < 3; col++ )
	{
		if( ( board[col][0] == board[col][1] ) && ( board[col][1] == board[col][2] ) && ( board[col][0] != ' ' ) )
		{
			return 1;
		}
	}
		
	return 0;
}

int row_winner( char board[3][3] )
{
	for( int row = 0; row < 3; row++ )
	{
		if( ( board[0][row] == board[1][row] ) && ( board[1][row] == board[2][row] ) && ( board[0][row] != ' ' ) )
		{
			return 1;
		}
	}
		
	return 0;
}

int diag_winner( char board[3][3] )
{
	if( ( board[0][0] == board[1][1] ) && ( board[1][1] == board[2][2] ) && ( board[0][0] != ' ' ) )
	{
			return 1;
	}
	else if( ( board[2][0] == board[1][1] ) && ( board[1][1] == board[0][2] ) && ( board[2][0] != ' ' ) )
	{
			return 1;
	}
		
	return 0;
}

int next_move_win( char board[3][3], char symbol )
{
	//check for horizontal next move wins
	for( int col = 0; col < 3; col++ )
	{
		if( ( board[col][0] == board[col][1] ) && ( board[col][0] == symbol ) && ( board[col][2] == ' ' ) )
		{
			return move( col, 2 );
		}
		else if( ( board[col][1] == board[col][2] ) && ( board[col][1] == symbol ) && ( board[col][0] == ' ' ) )
		{
			return move( col, 0 );
		}
		else if( ( board[col][0] == board[col][2] ) && ( board[col][0] == symbol ) && ( board[col][1] == ' ' ) )
		{
			return move( col, 1 );
		}
	}

	//check for vertical next move wins
	for( int row = 0; row < 3; row++ )
	{
		if( ( board[0][row] == board[1][row] ) && ( board[0][row] == symbol ) && ( board[2][row] == ' ' ) )
		{
			return move( 2, row );
		}
		else if( ( board[1][row] == board[2][row] ) && ( board[1][row] == symbol ) && ( board[0][row] == ' ' ) )
		{
			return move( 0, row );
		}
		else if( ( board[0][row] == board[2][row] ) && ( board[0][row] == symbol ) && ( board[1][row] == ' ' ) )
		{
			return move( 1, row );
		}
	}

	//check for diagonal next move wins
	if( ( board[0][0] == board[1][1] ) && ( board[0][0] == symbol ) && ( board[2][2] == ' ' ) )
	{
		return move( 2, 2 );
	}
	else if( ( board[1][1] == board[2][2] ) && ( board[1][1] == symbol ) && ( board[0][0] == ' ' ) )
	{
		return move( 0, 0 );
	}
	else if( ( board[0][0] == board[2][2] ) && ( board[0][0] == symbol ) && ( board[1][1] == ' ' ) )
	{
		return move( 1, 1 );
	}

	if( ( board[2][0] == board[1][1] ) && ( board[1][1] == symbol ) && ( board[0][2] == ' ' ) )
	{
		return move( 0, 2 );
	}
	else if( ( board[0][2] == board[1][1] ) && ( board[1][1] == symbol ) && ( board[2][0] == ' ' ) )
	{
		return move( 2, 0 );
	}
	else if( ( board[2][0] == board[0][2] ) && ( board[0][2] == symbol ) && ( board[1][1] == ' ' ) )
	{
		return move( 1, 1 );
	}

	return 0;
}

int col( int move )
{
	if( move % 3 == 1 )
		return 0;
	else if( move % 3 == 2 )
		return 1;
	else
		return 2;
}

int row( int move )
{
	if( move < 4 )
		return 2;
	else if( move < 7 )
		return 1;
	else
		return 0;
}

int move( int col, int row )
{
	if( row == 0 )
		row = 2;
	else if( row == 2 )
		row = 0;
	return( ( row * 3 ) + col + 1 );
}

/***************************************
****************RULES*******************
***************************************/

int rule_01( char board[3][3], int possible_moves[10] )
{	
	if( int move = next_move_win( board, 'O' ) )
	{
		return move;
	}
	return 0;
}

int rule_02( char board[3][3], int possible_moves[10] )
{
	if( int move = next_move_win( board, 'X' ) )
	{
		return move;
	}

	return 0;
}

int rule_03( char board[3][3], int possible_moves[10] )
{
	char temp_board[3][3];
	copy_board( board, temp_board );

	if( board[1][1] == 'O' )
	{
		for( int square_1 = 1; square_1 < 10; square_1++ )
		{
			if( diagonal[5][square_1] && board[col( square_1 )][row( square_1 )] == 'O' )
			{
				for( int square_2 = 1; square_2 < 10; square_2++ )
				{
					if( adjacent[square_1][square_2] && board[col( square_2 )][row( square_2 )] == ' ' )
					{ 
						temp_board[col( square_2 )][row( square_2 )] = 'O';
						if( next_move_win( temp_board, 'O' ) )
						{
							return square_2;
						}
					}
				}
			}
		}
	}

	return 0;
}

int rule_04( char board[3][3], int possible_moves[10] )
{
	if( board[col( 1 )][row( 1 )] == 'O' && board[col( 9 )][row( 9 )] == 'O' )
	{
		if( ( board[col( 4 )][row( 4 )] == ' ' ) && ( board[col( 8 )][row( 8 )] == ' ' ) && board[col( 7 )][row( 7 )] == ' ' )
		{
			return 7;
		}
		else if( ( board[col( 2 )][row( 2 )] == ' ' ) && ( board[col( 6 )][row( 6 )] == ' ' ) && board[col( 3 )][row( 3 )] == ' ' ) 
		{
			return 3;
		}
	}
	
	if( board[col( 3 )][row( 3 )] == 'O' && board[col( 7 )][row( 7 )] == 'O' )
	{
		if( ( board[col( 2 )][row( 2 )] == ' ' ) && ( board[col( 4 )][row( 4 )] == ' ' ) && board[col( 1 )][row( 1 )] == ' ' )
		{
			return 1;
		}
		else if( ( board[col( 6 )][row( 6 )] == ' ' ) && ( board[col( 8 )][row( 8 )] == ' ' ) && board[col( 9 )][row( 9 )] == ' ' ) 
		{
			return 9;
		}
	}

	return 0;
}

int rule_05( char board[3][3], int possible_moves[10] )
{
	char temp_board[3][3];
	copy_board( board, temp_board );

	if( board[1][1] == 'O' )
	{
		for( int square = 1; square < 10; square++ )
		{
			if( diagonal[5][square] && board[col( square )][row( square )] == ' ' )
			{
				temp_board[col( square )][row( square )] = 'O';
				if( next_move_win( temp_board, 'O' ) )
				{
					return square;
				}
			}
		}
	}

	return 0;
}

int rule_06( char board[3][3], int possible_moves[10] )
{
	if( board[col( 1 )][row( 1 )] == 'O' && board[col( 9 )][row( 9 )] == ' ' )
	{
		if( ( board[col( 4 )][row( 4 )] == ' ' ) && ( board[col( 8 )][row( 8 )] == ' ' ) && board[col( 7 )][row( 7 )] == ' ' )
		{
			return 9;
		}
		else if( ( board[col( 2 )][row( 2 )] == ' ' ) && ( board[col( 6 )][row( 6 )] == ' ' ) && board[col( 3 )][row( 3 )] == ' ' ) 
		{
			return 9;
		}
	}

	if( board[col( 9 )][row( 9 )] == 'O' && board[col( 1 )][row( 1 )] == ' ' )
	{
		if( ( board[col( 4 )][row( 4 )] == ' ' ) && ( board[col( 8 )][row( 8 )] == ' ' ) && board[col( 7 )][row( 7 )] == ' ' )
		{
			return 1;
		}
		else if( ( board[col( 2 )][row( 2 )] == ' ' ) && ( board[col( 6 )][row( 6 )] == ' ' ) && board[col( 3 )][row( 3 )] == ' ' ) 
		{
			return 1;
		}
	}
	
	if( board[col( 3 )][row( 3 )] == 'O' && board[col( 7 )][row( 7 )] == ' ' )
	{
		if( ( board[col( 2 )][row( 2 )] == ' ' ) && ( board[col( 4 )][row( 4 )] == ' ' ) && board[col( 1 )][row( 1 )] == ' ' )
		{
			return 7;
		}
		else if( ( board[col( 6 )][row( 6 )] == ' ' ) && ( board[col( 8 )][row( 8 )] == ' ' ) && board[col( 9 )][row( 9 )] == ' ' ) 
		{
			return 7;
		}
	}

	if( board[col( 7 )][row( 7 )] == 'O' && board[col( 3 )][row( 3 )] == ' ' )
	{
		if( ( board[col( 2 )][row( 2 )] == ' ' ) && ( board[col( 4 )][row( 4 )] == ' ' ) && board[col( 1 )][row( 1 )] == ' ' )
		{
			return 3;
		}
		else if( ( board[col( 6 )][row( 6 )] == ' ' ) && ( board[col( 8 )][row( 8 )] == ' ' ) && board[col( 9 )][row( 9 )] == ' ' ) 
		{
			return 3;
		}
	}

	return 0;
}

int rule_07( char board[3][3], int possible_moves[10] )
{
	if( possible_moves[move( 1, 1 )] )
	{
		return move( 1, 1 );
	}

	return 0;
}

/***************************************
***************UTILITY******************
***************************************/

int display( char board[3][3] )
{
	system("clear");

	cout << endl;

	if( !( display_board( template_board ) ) )
		error( "Error in display_board()" );

	cout << endl;

	if( !( display_board( board ) ) )
		error( "Error in display_board()" );

	cout << endl;

	return 1;
}

int display_board( char board[3][3] )
{
	for( int row = 0; row < 3; row++ )
	{
		if( row != 0 )
			cout << "-+-+-" << endl;
		for( int col = 0; col < 3; col++ )
		{
			if( col != 0 )
				cout << "|";
			cout << board[col][row];
		}
		cout << endl;
	}

	return 1;
}

int copy_board( char board[3][3], char copy[3][3] )
{
	for( int row = 0; row < 3; row++ )
	{
		for( int col = 0; col < 3; col++ )
		{
			copy[col][row] = board[col][row];
		}
	}

	return 1;
}

void error( char * msg )
{
	cout << msg;
}
