/*
 * Faculty of Applied Science and Engineering, University of Toronto
 * CSC181: Introduction to Computer Programming, Fall 2000
 *
 * File: rtournament.cpp
 * Contains: Implementation for RTBoard Connect Four class (for
 * tournament play).
 * Author: Ray Ortigas (rayo@dgp.toronto.edu)
 */

#include <iostream.h>
#include <assert.h>
#include "rtournament.h"

#define RED_PIECE ('R')
#define BLACK_PIECE ('B')

RTBoard::RTBoard(TPlayer* rp, TPlayer* bp) {
	red = rp;
	black = bp;

	for (int r = 0; r < T_NUM_ROWS; r++) {
		for (int c = 0; c < T_NUM_COLUMNS; c++) {
			board[r][c] = 0;
		}
	}
	for (int c = 0; c < T_NUM_COLUMNS; c++) {
		columnCounts[c] = 0;
	}
}

RTBoard::RTBoard(const RTBoard& b) {
	red = b.red;
	black = b.black;

	for (int r = 0; r < T_NUM_ROWS; r++) {
		for (int c = 0; c < T_NUM_COLUMNS; c++) {
			board[r][c] = b.board[r][c];
		}
	}
	for (int c = 0; c < T_NUM_COLUMNS; c++) {
		columnCounts[c] = b.columnCounts[c];
	}
}

TBoard* RTBoard::clone() const { return new RTBoard(*this); }

TPlayer* RTBoard::getRed() const { return red; }

TPlayer* RTBoard::getBlack() const { return black; }

TPlayer* RTBoard::getPiece(int r, int c) const { 
	assert ((0 <= r) && (r < T_NUM_ROWS)
		&& (0 <= c) && (c < T_NUM_COLUMNS));
	return board[r][c]; 
}

TPlayer* RTBoard::getPiece(const TPosition& p) const {
	assert ((0 <= p.r) && (p.r < T_NUM_ROWS)
		&& (0 <= p.c) && (p.c < T_NUM_COLUMNS));
	return board[p.r][p.c]; 
}

int RTBoard::countRed(const TPosition ps[], int n) const {
	int result = 0;
	for (int i = 0; i < n; i++) {
		if (getPiece(ps[i]) == red)
			result++;
	}
	return result;
}

int RTBoard::countBlack(const TPosition ps[], int n) const {
	int result = 0;
	for (int i = 0; i < n; i++) {
		if (getPiece(ps[i]) == black)
			result++;
	}
	return result;
}

TPlayer* RTBoard::getWinner() const {
	TPosition winningLine[T_NUM_TO_CONNECT];
	int r;
	int c;
	int i;
	
	// SW-NE diagonals
	for (r = 0; r <= T_NUM_ROWS-T_NUM_TO_CONNECT; r++) {
		for (c = 0; c <= T_NUM_COLUMNS-T_NUM_TO_CONNECT; c++) {
			for (i = 0; i < T_NUM_TO_CONNECT; i++) {
				winningLine[i].r = r+i;
				winningLine[i].c = c+i;
			}
			if (countRed(winningLine, T_NUM_TO_CONNECT) == T_NUM_TO_CONNECT)
				return red;
			if (countBlack(winningLine, T_NUM_TO_CONNECT) == T_NUM_TO_CONNECT)
				return black;
		}
	}

	// NW-SE diagonals
	for (r = T_NUM_ROWS-1; r >= T_NUM_TO_CONNECT-1; r--) {
		for (c = 0; c <= T_NUM_COLUMNS-T_NUM_TO_CONNECT; c++) {
			for (i = 0; i < T_NUM_TO_CONNECT; i++) {
				winningLine[i].r = r-i;
				winningLine[i].c = c+i;
			}
			if (countRed(winningLine, T_NUM_TO_CONNECT) == T_NUM_TO_CONNECT)
				return red;
			if (countBlack(winningLine, T_NUM_TO_CONNECT) == T_NUM_TO_CONNECT)
				return black;
		}
	}
	
	// Rows
	for (r = 0; r < T_NUM_ROWS; r++) {
		for (c = 0; c <= T_NUM_COLUMNS-T_NUM_TO_CONNECT; c++) {
			for (i = 0; i < T_NUM_TO_CONNECT; i++) {
				winningLine[i].r = r;
				winningLine[i].c = c+i;
			}
			if (countRed(winningLine, T_NUM_TO_CONNECT) == T_NUM_TO_CONNECT)
				return red;
			if (countBlack(winningLine, T_NUM_TO_CONNECT) == T_NUM_TO_CONNECT)
				return black;
		}
	}
	
	// Columns
	for (r = 0; r <= T_NUM_ROWS-T_NUM_TO_CONNECT; r++) {
		for (c = 0; c < T_NUM_COLUMNS; c++) {
			for (i = 0; i < T_NUM_TO_CONNECT; i++) {
				winningLine[i].r = r+i;
				winningLine[i].c = c;
			}
			if (countRed(winningLine, T_NUM_TO_CONNECT) == T_NUM_TO_CONNECT)
				return red;
			if (countBlack(winningLine, T_NUM_TO_CONNECT) == T_NUM_TO_CONNECT)
				return black;
		}
	}

	return 0;
}

bool RTBoard::isLegalMove(int c) const {
	return (c >= 0) && (c < T_NUM_COLUMNS) 
		&& (columnCounts[c] < T_NUM_ROWS);
}

bool RTBoard::hasLegalMoves() const {
	bool result = false;
	for (int c = 0; c < T_NUM_COLUMNS && !result; c++) {
		if (isLegalMove(c))
			result = true;
	}
	return result;
}

void RTBoard::dropPiece(TPlayer* p, int c) {
	assert ((c >= 0) && (c < T_NUM_COLUMNS) 
		&& (columnCounts[c] < T_NUM_ROWS));
	board[columnCounts[c]][c] = p;
	columnCounts[c]++;
}

TPlayer* RTBoard::removeTopPiece(int c) {
	TPlayer* result;
	assert ((0 <= c) && (c < T_NUM_COLUMNS)
		&& (columnCounts[c] > 0));
	columnCounts[c]--;
	result = board[columnCounts[c]][c];
	board[columnCounts[c]][c] = 0;
	return result;
}

ostream& operator<< (ostream& os, const TBoard& b) {
	int r, c;

	os << " ";
	for (c = 0; c < T_NUM_COLUMNS; c++) 
		os << "+-";
	os << "+" << endl;
	
	for (r = T_NUM_ROWS-1; r >= 0; r--) {
		os << r;
		for (c = 0; c < T_NUM_COLUMNS; c++) {
			TPlayer* piece = b.getPiece(r, c);
			if (piece == b.getRed())
				os << "|" << RED_PIECE;
			else if (piece == b.getBlack())
				os << "|" << BLACK_PIECE;
			else
				os << "| ";
		}
		os << "|" << endl;
		os << " ";
		for (c = 0; c < T_NUM_COLUMNS; c++) 
			os << "+-";
		os << "+" << endl;
	}
	
	os << " ";
	for (c = 0; c < T_NUM_COLUMNS; c++) 
		os << " " << c;
	os << endl;

	return os;
}
