#include "board.h"
#include "os.h"
#include <string.h>
CBoard::CBoard(void)
{
	PreCalcPaths();
	ClearBoard();
}
CBoard::~CBoard(void)
{
}
void CBoard::PreCalcPaths()
{
	for (int i=0; i<BOARD_SIZE; i++)
	{
		Path	&row	= m_paths[i];
		Path	&col	= m_paths[BOARD_SIZE+i];
		Path	&box	= m_paths[BOARD_SIZE*2+i];
		uint32 sU = (i%3)*3;
		uint32 sV = (i/3)*3;
		for (int x=0; x<BOARD_SIZE; x++)
		{
			row[x].u = x;
			row[x].v = i;
			col[x].u = i;
			col[x].v = x;
			box[x].u = sU + (x%3);
			box[x].v = sV + (x/3);
		}
	}
}
void CBoard::ClearBoard()
{
	memset(m_board, 0, sizeof(m_board));
}
BoardStatusType	CBoard::GetBoardStatus()
{
	bool	foundIncompleteness = false;
	int		nineBitsOn			= 0x3fe;
	for (int p=0; p<NUM_PATHS; p++)
	{
		Path	&path	= m_paths[p];
		int		sum		= 0;
		int		bit;
		sum = 0;
		for (int i=0; i<BOARD_SIZE; i++)
		{
			bit = 1 << GET_FIXED(m_board[path[i].v][path[i].u]);
			if (sum & nineBitsOn & bit)
			{
				return BS_ERROR;
			}
            sum |= bit;
		}
		if (sum != nineBitsOn) foundIncompleteness = true;
	}	return foundIncompleteness ? BS_INCOMPLETE : BS_COMPLETE;
}
void CBoard::SetItem(int u, int v, int val, bool isOption, bool onOff)
{	if (u < 0 || u >= BOARD_SIZE || v < 0 || v >= BOARD_SIZE || val > 10) return;	uint16&	item = m_board[v][u];
	if (!isOption)
	{
		item = val;	}
	else
	{
		uint16 shift = BOARD_ITEM_OPTIONS_START_BIT + val - 1;
		if (onOff)	item |= (1<<shift);		else		item &= ~(1<<shift);	}
}
uint16 CBoard::GetItem(int u, int v)
{	if (u < 0 || u >= BOARD_SIZE || v < 0 || v >= BOARD_SIZE) return 0xffff;
	return m_board[v][u];
}
void CBoard::FillPencilMarks()
{	for (int v=0; v<BOARD_SIZE; v++)
	{
		for (int u=0; u<BOARD_SIZE; u++)
		{
			uint16& b = m_board[v][u];
			if (HAS_FIXED(b))	b &= ~BOARD_ITEM_OPTIONS_MASK;			else				b |= BOARD_ITEM_OPTIONS_MASK;		}
	}
	int	nineBitsOn = 0x3fe;
	for (int p=0; p<NUM_PATHS; p++)
	{
		Path	&path	= m_paths[p];
		int		sum		= 0;		for (int i=0; i<BOARD_SIZE; i++) sum |= (1 << GET_FIXED(m_board[path[i].v][path[i].u]));		if ((sum&(~1)) != nineBitsOn)
		{			sum = (~((sum >> 1) << BOARD_ITEM_OPTIONS_START_BIT)) | ~BOARD_ITEM_OPTIONS_MASK;			for (int i=0; i<BOARD_SIZE; i++) m_board[path[i].v][path[i].u] &= sum;
		}
	}
}
int CBoard::ApplyRule1()
{
	int numSolved = 0;	for (int v=0; v<9; v++)
	{
		for (int u=0; u<9; u++)
		{
			uint16	&b = m_board[v][u];			if (HAS_FIXED(b)) continue;
			uint16	o = GET_OPTIONS(b);			int		numOptions = 0;
			uint16	optionVal;
			for (int x=0; x<BOARD_SIZE; x++)
			{
				if ((o>>x) & 1)
				{
					numOptions++;
					optionVal = x+1;
				}
			}			if (numOptions == 1)
			{
				b |= optionVal;
				numSolved++;
			}
		}
	}
	return numSolved;
}
int CBoard::ApplyRule2()
{
	int numSolved = 0; 	for (int p=0; p<NUM_PATHS; p++)
	{
		Path	&path = m_paths[p];
		struct
		{
			Point	lastCell;			int	counter;		} options[BOARD_SIZE];
		memset(&options, 0, sizeof options);		for (int i=0; i<BOARD_SIZE; i++)
		{
			uint16	b = m_board[path[i].v][path[i].u];
			int tttt = GET_FIXED(b);			if (NO_FIXED(b))
			{
				int cellOptions = GET_OPTIONS(b);				for (int x=0; x<BOARD_SIZE; x++)
				{
					if ((cellOptions>>x) & 1)
					{
						options[x].lastCell = path[i];
						options[x].counter++;
					}
				}
			}			else
			{
				options[GET_FIXED(b) - 1].counter = 2;
			}
		}		for (int x=0; x<BOARD_SIZE; x++)
		{
			if (options[x].counter == 1)
			{				uint16& b = m_board[options[x].lastCell.v][options[x].lastCell.u];				b &= ~BOARD_ITEM_FIXED_MASK;
				b |= x+1;
				numSolved++;
			}
		}
	}
	return numSolved;
}static void RandomifyOptions(uint16 options, int* result, int* numResults)
{
	int	num = 0;
	int	x;	for (x=0; x<BOARD_SIZE; x++)
	{		if ((options >> x) & 1)	result[num++] = x+1;
	}	for (x=0; x<num-1; x++)
	{
		int src = x + 1 + (OS_RAND % (num-x-1));		int	tmp = result[x];
		result[x] = result[src];
		result[src] = tmp;
	}	*numResults = num;
}
SolutionType CBoard::Solve(bool determineIfMultiple)
{
	int	numSolved				= 1;
	int	numSolved1, numSolved2;
	int	numSteps				= 0;
	for (;;)
	{		BoardStatusType	boardStatus = GetBoardStatus();
		if		(boardStatus == BS_COMPLETE)	return SOLUTOIN_SINGLE;		else if	(boardStatus == BS_ERROR)		return SOLUTOIN_NONE;		else if (numSolved == 0)				break;		FillPencilMarks();		numSolved1 = ApplyRule1();
		numSolved2 = ApplyRule2();		numSolved = numSolved1 + numSolved2;
		numSteps++;	}	int		u, v;
	int		found;
	for (v=0; v<BOARD_SIZE; v++)
	{
		for (u=0; u<BOARD_SIZE; u++)
		{
			if ((found = NO_FIXED(m_board[v][u])) != 0) break;
		}
		if (found) break;
	}	uint16	cellOptions = GET_OPTIONS(m_board[v][u]);
	int		options[BOARD_SIZE];
	int		numOptions;
	int		foundSol = -1;
	CBoard	tempBoard;	RandomifyOptions(cellOptions, options, &numOptions);	for (int x=0; x<numOptions; x++)
	{		tempBoard = *this;		tempBoard.SetItem(u, v, options[x]);		SolutionType sol = tempBoard.Solve(determineIfMultiple);		if ((sol == SOLUTOIN_MULTPLE) ||			(sol == SOLUTOIN_SINGLE && !determineIfMultiple) ||			(sol == SOLUTOIN_SINGLE && determineIfMultiple && foundSol != -1))		{
			*this = tempBoard;
			return (sol == SOLUTOIN_MULTPLE || foundSol != -1) ? SOLUTOIN_MULTPLE : SOLUTOIN_SINGLE;
		}		if (sol == SOLUTOIN_SINGLE && determineIfMultiple && foundSol == -1)
		{			foundSol = x;
		}	}	if (foundSol == -1) return SOLUTOIN_NONE;	SetItem(u, v, options[foundSol]);
	Solve();
	return SOLUTOIN_SINGLE;
}
SolutionType CBoard::IsSolveable()
{
	CBoard	temp = *this;
	return temp.Solve(true);
}
void CBoard::Generate(uint16 seed, int difficulty){
	ClearBoard();	OS_SRAND(seed);	Solve();	int	numVisibleCells = BOARD_SIZE*BOARD_SIZE;
	int	targetVisible	= 50;
	while (numVisibleCells > targetVisible)
	{
		int u = OS_RAND % BOARD_SIZE;
		int v = OS_RAND % BOARD_SIZE;		if (NO_FIXED(m_board[v][u])) continue;
		CBoard	tempBoard = *this;
		
		tempBoard.SetItem(u, v, 0);
		if (tempBoard.Solve(true) != SOLUTOIN_SINGLE) continue;		m_board[v][u] = 0;
		numVisibleCells--;
	}}