#pragma once
#include "grid.h"
….
private:
CGrid m_Grid;
protected:
afx_msg LRESULT OnSetValue(WPARAM wParam, LPARAM lParam);
if(!m_bInitialised)
{
…CURRENT CODE IS HERE…
//Now create the data structure contents
for(int iRow = 0; iRow < 9; iRow++)
{
for(int iCol = 0; iCol < 9; iCol++)
{
//The return value ought to be checked, this just assumes it works
m_Grid.AddCell(iRow, iCol, (UINT_PTR)&m_arWndButtons[iRow * 9 + iCol]);
}
}
}
BEGIN_MESSAGE_MAP(CSudokuView, CFormView)
ON_MESSAGE(SUD_SETVALUE, &CSudokuView::OnSetValue)
END_MESSAGE_MAP()
LRESULT CSudokuView::OnSetValue(WPARAM wParam, LPARAM lParam)
{
//Update the grid, note casting the WPARAM and LPARAM into the type of values expected by the function
m_Grid.SetValue((UINT_PTR)wParam, (int)lParam);
//Now flag each cell as having a valid entry
for(int i = 0; i < 81; i++)
m_arWndButtons[i].SetValid(true);
//Set a flag that the window should be repainted - WHEN NOT BUSY
Invalidate();
//If there is any cell with a zero then the game is not yet finished, no further validity checking required
for(int i = 0; i < 81; i++)
{
if(m_arWndButtons[i].GetValue() == 0)
return 1;
}
//All the cells contain a non zero value, is the game correctly finished?
//We must check each button and set the internal flag
for(int i = 0; i < 81; i++)
{
if(!m_Grid.CheckValid((UINT_PTR)&m_arWndButtons[i])
m_arWndButtons[i].SetValid(false);
}
return 1;
}
class CGrid
{
public:
CGrid(void) {};
~CGrid(void);
bool AddCell(int iRow, int iCol, UINT_PTR uiID);
void SetValue(UINT_PTR uiID, int iValue);
bool CheckValid(UINT_PTR uiID);
void PrepareAllows(UINT_PTR uiID, bool* pbAllowed);
//Internal data structure declarations
private:
struct stInfo //Structure nested within the CGrid class - contains all 81 cells in the grid
{
stInfo() : m_uiID(0), m_iValue(0) {}; //Initialise variables
UINT_PTR m_uiID;
int m_iValue;
};
CArray<stInfo*, stInfo*>m_arCells;
class CBlock //class nested within the CGrid class
{
public:
CBlock(void) {};
~CBlock(void) {};
bool AddCell(stInfo* pCell);
bool Contains(UINT_PTR uiID, int& iValue);
bool CheckValue(int iValue);
bool PrepareAllowedValues(UINT_PTR uiID, bool* pbAllowed);
UINT_PTR GetID(int iIndex) { return m_arCells[iIndex]->m_uiID; } ;
int GetValue(int iIndex) { return m_arCells[iIndex]->m_iValue; } ;
private:
CArray<stInfo*, stInfo*>m_arCells; //POINTERS to the cells from the CGrid class
};
//Actual data structures
CBlock m_Row[9];
CBlock m_Col[9];
CBlock m_Square[9];
bool CheckBlock(CBlock* pBlock, UINT_PTR uiID);
void ProcessAllowsBlock(CBlock* pBlock, UINT_PTR uiID, bool* pbAllowed);
};
CGrid::~CGrid(void)
{
//Release any memory assigned with new - prevent memory leaks
for(int i = 0; i < m_arCells.GetCount(); i++)
delete m_arCells[i];
m_arCells.RemoveAll();
}
bool CGrid::AddCell(int iRow, int iCol, UINT_PTR uiID)
{
stInfo* pCell = new stInfo;
//Sanity - check the ID for uniqueness
for(int i = 0; i < m_arCells.GetCount(); i++)
{
ASSERT(m_arCells[i]->m_uiID != uiID);
}
pCell->m_uiID = uiID; //set the identifier
m_arCells.Add(pCell);
ASSERT(m_arCells.GetCount() <= 81); //Max 81 cells in the array
//Assign cells into blocks
if(!m_Row[iRow].AddCell(pCell))
return false;
if(!m_Col[iCol].AddCell(pCell))
return false;
int iSquare = (3 * (iRow / 3)) + (iCol / 3); //Work out which square this row/col combination belongs to
if(!m_Square[iSquare].AddCell(pCell))
return false;
return true;
}
void CGrid::SetValue(UINT_PTR uiID, int iValue)
{
//Loop through all the cells, et the value of the cell with that ID
for(int i = 0; i < m_arCells.GetCount(); i++)
{
if(m_arCells[i]->m_uiID == uiID)
{
m_arCells[i]->m_iValue = iValue;
return;
}
}
ASSERT(FALSE); //Cell not found for some reason
}
bool CGrid::CheckValid(UINT_PTR uiID)
{
//Perform a validity check, each digit should only appear once in a block
//This needs to be done for all the blocks
//Note each block is an array so the variable itself is a POINTER to the first member of the array
if(!CheckBlock(m_Row, uiID)) return false;
if(!CheckBlock(m_Col, uiID)) return false;
if(!CheckBlock(m_Square, uiID)) return false;
return true;
}
bool CGrid::CheckBlock(CBlock* pBlock, UINT_PTR uiID)
{
//There are nine individual blocks inside each array of blocks, check each if the cell with this ID is part of it
int iValue;
for(int i = 0; i < 9; i++)
{
if(pBlock[i].Contains(uiID, iValue)) //is it in this block? If yes then what value does it contain
{
return pBlock[i].CheckValue(iValue);
}
}
return true;
}
void CGrid::PrepareAllows(UINT_PTR uiID, bool* pbAllowed)
{
ProcessAllowsBlock(m_Row, uiID, pbAllowed);
ProcessAllowsBlock(m_Col, uiID, pbAllowed);
ProcessAllowsBlock(m_Square, uiID, pbAllowed);
}
void CGrid::ProcessAllowsBlock(CBlock* pBlock, UINT_PTR uiID, bool* pbAllowed)
{
//Each array of blocks has nine entries
for(int i = 0; i < 9; i++)
{
//delegate to the block to check
//A cell can only belong to one row, one col and one square - a return of true meant
//the uiID belonged to a cell in this block
if(pBlock[i].PrepareAllowedValues(uiID, pbAllowed))
return;
}
}
//Internal CBlock class
bool CGrid::CBlock::AddCell(stInfo* pCell)
{
//Only can have 9 cells at most
if(m_arCells.GetCount() > 9)
{
ASSERT(FALSE);
return false;
}
m_arCells.Add(pCell);
return true;
}
bool CGrid::CBlock::Contains(UINT_PTR uiID, int& iValue)
{
iValue = 0;
for(int i = 0; i < m_arCells.GetCount(); i++)
{
if(GetID(i) == uiID)
{
iValue = GetValue(i);
return true;
}
}
return false;
}
bool CGrid::CBlock::CheckValue(int iValue)
{
//Value should appear at most once in the block
int iCount = 0; //initially no instances of this value found
//Loop through all cells, increment the number of times we find the value being used
for(int i = 0; i < m_arCells.GetCount(); i++)
{
if(GetValue(i) == iValue)
iCount++;
}
//used once or less and the value is OK for this block
return (iCount <= 1);
}
bool CGrid::CBlock::PrepareAllowedValues(UINT_PTR uiID, bool* pbAllowed)
{
//returns true if the uiID matches with a member cell of this block
//Check if the cell is in fact in this block, process if it is
int iValue;
if(Contains(uiID, iValue))
{
//See which digits are used elsewhere in the block
//the lParam passed in the message is the adress of the bool array
//cast it to a bool pointer fo accessing it
//remember the array is zero based - so if digit 2 is in use we want the array member 1
for(int i = 0; i < 9; i++)
{
int iValue = GetValue(i);
//If the value is non zero (used) then set the allowed flag to false
//A cell can be in other blocks, we can only turn it off - do NOT set to true
if(iValue > 0)
pbAllowed[iValue - 1] = false;
}
return true;
}
//cell not found in block
return false;
}
void CGridButton::SetValue(int i)
{
m_iValue = i;
if(GetValue() > 0)
{
CString s((char)(GetValue() + '0'));
SetWindowText(s);
}
else
SetWindowText("");
}
void CGridButton::SetValue(int i)
{
m_iValue = i;
if(GetValue() > 0)
{
CString s((char)(GetValue() + '0'));
SetWindowText(s);
}
else
SetWindowText("");
GetParent()->SendMessage(SUD_SETVALUE, (WPARAM)this, (LPARAM)GetValue());
}
else
{
//Draw hint
CString s;
CRect rc;
GetClientRect(&rc);
int iWidth = rc.Width() / 3;
int iHeight = rc.Height() / 3;
dc.SetTextColor(RGB(128,128,0));
bool bAllow[9];
//Set the flags all to be true
memset(bAllow, true, sizeof(bAllow));
GetParent()->SendMessage(SUD_PREPAREALLOWS, (WPARAM)this, (LPARAM)&bAllow);
CRect rcClient;
for(int row = 0; row < 3; row++)
{
for(int col = 0; col < 3; col++)
{
if(bAllow[row*3 + col])
{
rcClient.top = row * iHeight; rcClient.bottom = rcClient.top + iHeight;
rcClient.left = col * iWidth; rcClient.right = rcClient.left + iWidth;
s.Format(_T("%d"), row*3 + col + 1); //Zero based array but contents start at one
dc.DrawText(s, s.GetLength(), &rcClient, DT_SINGLELINE|DT_VCENTER|DT_CENTER);
}
}
}
}
#define SUD_SETVALUE (WM_USER+101)
#define SUD_PREPAREALLOWS (WM_USER+102)
afx_msg LRESULT OnPrepareAllows(WPARAM wParam, LPARAM lParam);
ON_MESSAGE(SUD_PREPAREALLOWS, &CSudokuView::OnPrepareAllows)
LRESULT CSudokuView::OnPrepareAllows(WPARAM wParam, LPARAM lParam)
{
m_Grid.PrepareAllows((UINT_PTR)wParam, (bool*)lParam);
return 1;
}
void PrepareAllows(UINT_PTR uiID, bool* pbAllowed);
void ProcessAllowsBlock(CBlock* pBlock, UINT_PTR uiID, bool* pbAllowed);
void CGrid::PrepareAllows(UINT_PTR uiID, bool* pbAllowed)
{
ProcessAllowsBlock(m_Row, uiID, pbAllowed);
ProcessAllowsBlock(m_Col, uiID, pbAllowed);
ProcessAllowsBlock(m_Square, uiID, pbAllowed);
}
void CGrid::ProcessAllowsBlock(CBlock* pBlock, UINT_PTR uiID, bool* pbAllowed)
{
//Each array of blocks has nine entries
for(int i = 0; i < 9; i++)
{
//delegate to the block to check
//A cell can only belong to one row, one col and one square - a return of true meant
//the uiID belonged to a cell in this block
if(pBlock[i].PrepareAllowedValues(uiID, pbAllowed))
return;
}
}
bool PrepareAllowedValues(UINT_PTR uiID, bool* pbAllowed);
bool CGrid::CBlock::PrepareAllowedValues(UINT_PTR uiID, bool* pbAllowed)
{
//returns true if the uiID matches with a member cell of this block
//Check if the cell is in fact in this block, process if it is
int iValue;
if(Contains(uiID, iValue))
{
//See which digits are used elsewhere in the block
//the lParam passed in the message is the adress of the bool array
//cast it to a bool pointer for accessing it
//remember the array is zero based - so if digit 2 is in use we want the array member 1
for(int i = 0; i < 9; i++)
{
int iValue = GetValue(i);
//If the value is non zero (used) then set the allowed flag to false
//A cell can be in other blocks, we can only turn it off - do NOT set to true
if(iValue > 0)
pbAllowed[iValue - 1] = false;
}
return true;
}
//cell not found in block
return false;
}
if(pMsg->message == WM_KEYDOWN)
if(pMsg->message == WM_MOUSEMOVE)
ProcessMouseMove(pMsg->pt); //screen co-ordinates
else if(pMsg->message == WM_KEYDOWN)
void ProcessMouseMove(CPoint pt);
void CSudokuView::ProcessMouseMove(CPoint pt)
{
ScreenToClient(&pt);
static CWnd* spLastWindow = NULL; //reduce flickering when mouse moving on a button
CWnd* pChild = ChildWindowFromPoint(pt);
if((pChild != spLastWindow) && (pChild != this))
{
if(spLastWindow != NULL)
{
//Unset - mouse moving away from this window
static_cast<CGridButton*>(spLastWindow)->ShowHints(false);
spLastWindow->Invalidate();
spLastWindow = NULL;
}
if(pChild != NULL)
{
//Find which button, if any, it is
for(int i = 0; i < 81; i++)
{
if(&m_arWndButtons[i] == pChild)
{
m_arWndButtons[i].ShowHints(true);
m_arWndButtons[i].Invalidate();
spLastWindow = &m_arWndButtons[i];
}
}
}
}
}
public:
void ShowHints(bool bFlag) {m_bShowHint = bFlag; };
private:
bool m_bShowHint;
else
{
//Draw hint
else if(m_bShowHint)
{
//Draw hint
, m_bValid(true)
, m_bShowHint(false)
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (0)