#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--;
}}