/*
 * Simple Tic Tac Toe game to demonstrate 2D-arrays.
 * This version only stores the players' moves in the array.
 * Consequently, the board consists of a 3x3 array. The display
 * function draws the board with cout statements.
 * Tabs are set to 8-spaces.
 */


#include <iostream>
using namespace std;


// _MSC_BUILD is a Visual Studio predefined macro
#ifdef _MSC_BUILD
	// extended ASCII - Windows only
	const	char	VERT = (char)179;
	const	char	HORIZ = (char)196;
	const	char	CROSS = (char)197;
	void clear() { system("cls"); }		// clears the Windows console
#else
	// basic ASCII - all systems
	const	char	VERT = '|';
	const	char	HORIZ = '-';
	const	char	CROSS = '-';
	void clear() { system("clear"); }	// clears the POSIX console
#endif


// Horizontal line separating the three rows. Constructed from the constants above.
const	char	HLINE[] = { ' ', ' ', HORIZ, CROSS, HORIZ, CROSS,  HORIZ, '\0' };


void display(char board[][3]);
void init_board(char board[][3]);
void get_move(char player, char board[][3]);
void test_win(char board[][3]);
char test_board(char board[][3]);



int main()
{
	char	board[3][3];
	int	moves = 9;				// total number of moves
	char	player = 'X';				// current player

	init_board(board);				// initialize the game board

	while (moves > 0)				// while moves remain
	{
		display(board);
		get_move(player, board);
		test_win(board);
		moves--;				// one move taken
		player = (player == 'X') ? 'O' : 'X';	// change player's turn
	}

	display(board);					// no winner
	cout << "Draw!" << endl;

	return 0;
}


/*
 * Initializes the game board.
 * The board only contains playing spaces, which the function initializes to spaces.
 * Compare to ttt1.cpp and tt2.cpp init_board.
 */

void init_board(char board[][3])
{
	for (int row = 0; row < 3; row++)		// initialize spaces
		for (int col = 0; col < 3; col++)
			board[row][col] = ' ';
}


/*
 * Displays game board.
 * The board is a two-dimensional structure, so the program draws it with two
 * for-loops, one nested in the other. The display function a two-dimensional
 * fence post solution: The playing spaces represent the fence posts, while the
 * board characters represent the fence spans. Consequently, the outer loop
 * draws the first two rows, while the last row is treated as a special case.
 * The inner loop draws the first two columns, and the function draws the last
 * column separately.
 */

void display(char board[][3])
{
	clear();

	cout << "  ";					// offset for col numbers
	for (int i = 0; i < 3; i++)			// print col numbers
		cout << i << " ";
	cout << endl;

	for (int row = 0; row < 2; row++)		// draws the first two rows
	{
		cout << row << " " ;			// row number offset
		for (int col = 0; col < 2; col++)	// draws first 2 columns
			cout << board[row][col] << VERT;
		cout << board[row][2] << endl;		// draws the last column

		cout << HLINE << endl;			// draws a horizontal line between rows
	}

	// draws the last row; treats "board" as a string
	cout << "2 " << board[2][0] << VERT << board[2][1] << VERT << board[2][2] << endl << endl;
}


/* Get the next player's move. */

void get_move(char player, char board[][3])
{
	int	row;
	int	col;

	cout << "Move for player: " << player << endl;
	do
	{
		cout << "Row [0-2, -1 to end]: ";
		cin >> row;
		if (row == -1)
			exit(0);				// end early

		cout << "Col [0-2, -1 to end]: ";
		cin >> col;
		if (col == -1)
			exit(0);				// end early

	} while (board[row][col] != ' ' ||			// board space not empty
		row < 0 || row > 3 || col < 0 || col > 3);	// row or col out of bounds

	board[row][col] = player;
}


/* Checks to see if either player has won yet. */

void test_win(char board[][3])
{
	char result = test_board(board);

	if (result == 'X' || result == 'O')
	{
		display(board);
		cout << result << " wins!" << endl;
		exit(0);
	}
}


/*
 * Tests the board to see if either player has three marks in a row, column, or diagonal.
 * Returns the symbol of the winning player or space if there is no winner.
 */

char test_board(char board[][3])
{
	for (int row = 0; row < 3; row++)		// checks rows
	{
		if (board[row][0] == ' ')
			continue;
		if (board[row][0] == board[row][1] && board[row][1] == board[row][2])
			return board[row][0];		// winner found
	}

	for (int col = 0; col < 3; col++)		// checks columns
	{
		if (board[0][col] == ' ')
			continue;
		if (board[0][col] == board[1][col] && board[1][col] == board[2][col])
			return board[0][col];		// winner found
	}


	// check the prime diagonal
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2])
		return board[0][0];			// winner found

	// check the non-prime diagonal
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0])
		return board[0][2];			// winner found


	return ' ';					// no winner yet
}

