void CDlgSolve::OnBnClickedOk()
{
// modeless dialog - do not call OnOK
//OnOK();
}
void CDlgSolve::OnBnClickedCancel()
{
// modeless dialog - do not call OnCancel
//OnCancel();
}
void CSudokuView::OnUpdateEditSolve(CCmdUI *pCmdUI)
{
pCmdUI->Enable(!GetDocument()->GetInfo().IsEmpty()); //Game being played - available to be solved
}
void CSudokuView::OnEditSolve()
{
CDlgSolve dlg;
dlg.DoModal();
}
void CDlgSolve::OnBnClickedCancel()
{
// modeless dialog - do not call OnOK
//OnCancel();
DestroyWindow();
}
void CSudokuView::OnEditSolve()
{
CDlgSolve dlg;
dlg.Create(CDlgSolve::IDD);
dlg.ShowWindow(SW_SHOW);
}
void CSudokuView::OnEditSolve()
{
CDlgSolve* pDlg = new CDlgSolve;
pDlg->Create(CDlgSolve::IDD);
pDlg->ShowWindow(SW_SHOW);
}
void CDlgSolve::PostNcDestroy()
{
CDialog::PostNcDestroy();
delete this; //modelesss dialog, release memory assigned with new
}
void CSudokuView::OnEditSolve()
{
char arGame[82];
ZeroMemory(arGame, 82);
for(int i = 0; i < 81; i++)
arGame[i] = '0' + m_arWndButtons[i].GetValue();
CDlgSolve* pDlg = new CDlgSolve(m_arGame);
pDlg->Create(CDlgSolve::IDD);
pDlg->ShowWindow(SW_SHOW);
}
class CDlgSolve : public CDialog
{
DECLARE_DYNAMIC(CDlgSolve)
public:
CDlgSolve(LPCTSTR cpszGame, CWnd* pParent = NULL); // custom constructor
virtual ~CDlgSolve();
// Dialog Data
enum { IDD = IDD_DLG_SOLVE };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedOk();
afx_msg void OnBnClickedCancel();
protected:
virtual void PostNcDestroy();
private:
CString m_szGame;
public:
afx_msg void OnPaint();
};
#include "stdafx.h"
#include "Sudoku.h"
#include "DlgSolve.h"
#include "SudokuDCPainter.h"
// CDlgSolve dialog
IMPLEMENT_DYNAMIC(CDlgSolve, CDialog)
CDlgSolve::CDlgSolve(LPCTSTR cpszGame, CWnd* pParent /*=NULL*/)
: CDialog(CDlgSolve::IDD, pParent)
, m_szGame(cpszGame)
{
}
CDlgSolve::~CDlgSolve()
{
}
void CDlgSolve::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CDlgSolve, CDialog)
ON_BN_CLICKED(IDOK, &CDlgSolve::OnBnClickedOk)
ON_BN_CLICKED(IDCANCEL, &CDlgSolve::OnBnClickedCancel)
ON_WM_PAINT()
END_MESSAGE_MAP()
// CDlgSolve message handlers
void CDlgSolve::OnBnClickedOk()
{
// modeless dialog - do not call OnOK
//OnOK();
}
void CDlgSolve::OnBnClickedCancel()
{
// modeless dialog - do not call OnCancel
//OnCancel();
DestroyWindow();
}
void CDlgSolve::PostNcDestroy()
{
CDialog::PostNcDestroy();
delete this; //modelesss dialog, release memory assigned with new
}
void CDlgSolve::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
// Do not call CDialog::OnPaint() for painting messages
CSudokuDCPainter painter;
painter.Paint(this, &dc, IDC_STATIC_BOUNDARY, m_szGame);
}
struct stSolveInfo
{
HWND hWndParent;
char szGame[82];
char szGameInProgress[82];
bool bSuccess;
unsigned long ulCounter;
CCriticalSection csAccessControl;
CEvent ceCloseThread;
CEvent ceThreadIsFinished;
};
#pragma once
// CDlgSolve dialog
#include "afxmt.h" //For the CCriticalSection, CEvent classes
struct stSolveInfo
{
HWND hWndParent;
char szGame[82];
char szGameInProgress[82];
bool bSuccess;
unsigned long ulCounter;
CCriticalSection csAccessControl;
CEvent ceCloseThread;
CEvent ceThreadIsFinished;
};
class CDlgSolve : public CDialog
{
DECLARE_DYNAMIC(CDlgSolve)
public:
CDlgSolve(LPCTSTR cpszGame, CWnd* pParent = NULL); // custom constructor
virtual ~CDlgSolve();
// Dialog Data
enum { IDD = IDD_DLG_SOLVE };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedOk();
afx_msg void OnBnClickedCancel();
protected:
virtual void PostNcDestroy();
private:
CString m_szGame;
public:
afx_msg void OnPaint();
virtual BOOL OnInitDialog();
private:
afx_msg LRESULT OnSolveProgress(WPARAM wParam, LPARAM lParam);
stSolveInfo m_stSolveInfo;
public:
afx_msg void OnDestroy();
};
// DlgSolve.cpp : implementation file
//
#include "stdafx.h"
#include "Sudoku.h"
#include "DlgSolve.h"
#include "SudokuDCPainter.h"
#include "randomnumber.h"
#include "grid.h"
//comment out the following to switch between mote carlo and brute force methodds in the thread.
#define __MONTE_CARLO_THREAD__
UINT SolveSudokuProc(LPVOID pParam);
void FillTempGame(CGrid& grid, stSolveInfo *pstSolveInfo, bool bGameSuccess);
#ifndef __MONTE_CARLO_THREAD__
bool ProcessCell(CGrid& grid, int iCell, stSolveInfo *pstSolveInfo);
#endif
// CDlgSolve dialog
#define SOLVE_PROGRESS (WM_USER+105)
IMPLEMENT_DYNAMIC(CDlgSolve, CDialog)
CDlgSolve::CDlgSolve(LPCTSTR cpszGame, CWnd* pParent /*=NULL*/)
: CDialog(CDlgSolve::IDD, pParent)
, m_szGame(cpszGame)
{
}
CDlgSolve::~CDlgSolve()
{
}
void CDlgSolve::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CDlgSolve, CDialog)
ON_BN_CLICKED(IDOK, &CDlgSolve::OnBnClickedOk)
ON_BN_CLICKED(IDCANCEL, &CDlgSolve::OnBnClickedCancel)
ON_WM_PAINT()
ON_MESSAGE(SOLVE_PROGRESS, &CDlgSolve::OnSolveProgress)
ON_WM_DESTROY()
END_MESSAGE_MAP()
// CDlgSolve message handlers
void CDlgSolve::OnBnClickedOk()
{
// modeless dialog - do not call OnOK
//OnOK();
}
void CDlgSolve::OnBnClickedCancel()
{
// modeless dialog - do not call OnCancel
//OnCancel();
DestroyWindow();
}
void CDlgSolve::PostNcDestroy()
{
CDialog::PostNcDestroy();
delete this; //modelesss dialog, release memory assigned with new
}
void CDlgSolve::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
// Do not call CDialog::OnPaint() for painting messages
CSudokuDCPainter painter;
painter.Paint(this, &dc, IDC_STATIC_BOUNDARY, m_szGame);
}
BOOL CDlgSolve::OnInitDialog()
{
CDialog::OnInitDialog();
m_stSolveInfo.hWndParent = GetSafeHwnd();
ZeroMemory(m_stSolveInfo.szGame, sizeof(m_stSolveInfo.szGame));
strcpy(m_stSolveInfo.szGame, m_szGame);
memcpy(m_stSolveInfo.szGameInProgress, m_stSolveInfo.szGame, 82);
m_stSolveInfo.bSuccess = false;
m_stSolveInfo.ulCounter = 0;
m_stSolveInfo.ceCloseThread.ResetEvent();
m_stSolveInfo.ceThreadIsFinished.ResetEvent();
CWinThread* pThread = AfxBeginThread(SolveSudokuProc, &m_stSolveInfo, 0, 0, CREATE_SUSPENDED);
if(pThread != NULL)
{
pThread->m_bAutoDelete = true;
pThread->ResumeThread();
}
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
LRESULT CDlgSolve::OnSolveProgress(WPARAM wParam, LPARAM lParam)
{
//Remove multiple copies of the message should there be any in the queue
//else this function is being called continuously and hogging the main thread of the app
MSG msg;
while(PeekMessage(&msg, NULL, SOLVE_PROGRESS, SOLVE_PROGRESS, PM_REMOVE));
CString s;
m_stSolveInfo.csAccessControl.Lock();
m_szGame = m_stSolveInfo.szGameInProgress;
if(m_stSolveInfo.bSuccess)
s.LoadString(IDS_SUCCESS);
else
{
if(m_stSolveInfo.ulCounter == (unsigned long)-1)
s.LoadString(IDS_FAIL);
else
s.Format(IDS_TRIES, m_stSolveInfo.ulCounter);
}
m_stSolveInfo.csAccessControl.Unlock();
SetDlgItemText(IDC_STATIC_PROGRESS, s);
Invalidate();
UpdateWindow();
return 1;
}
void CDlgSolve::OnDestroy()
{
// Signal the thread to close
m_stSolveInfo.ceCloseThread.SetEvent();
//Wait for it actually to be closed
::WaitForSingleObject(m_stSolveInfo.ceThreadIsFinished.m_hObject, INFINITE);
CDialog::OnDestroy();
}
#ifdef __MONTE_CARLO_THREAD__
UINT SolveSudokuProc(LPVOID pParam)
{
//The pParam needs to be cast to the actual type of object it is
stSolveInfo *pstSolveInfo = (stSolveInfo*)pParam;
//Initialise the grid
CGrid grid;
int iRow, iCol, iCell, iValue;
for(iRow = 0; iRow < 9; iRow++)
{
for(iCol = 0; iCol < 9; iCol++)
{
iCell = iRow * 9 + iCol;
grid.AddCell(iRow, iCol, (UINT_PTR)iCell);
}
}
CRandomNumber random;
while(true)
{
//If the solve dialog is closing we need to stop the thread
if(::WaitForSingleObject(pstSolveInfo->ceCloseThread.m_hObject, 0) == WAIT_OBJECT_0)
{
pstSolveInfo->ceThreadIsFinished.SetEvent();
return 0;
}
//Fill the grid
for(iRow = 0; iRow < 9; iRow++)
{
for(iCol = 0; iCol < 9; iCol++)
{
iCell = iRow * 9 + iCol;
iValue = pstSolveInfo->szGame[iCell] - '0';
if(iValue == 0)
grid.SetValue((UINT_PTR)iCell, random.GetRandom(1, 9));
else
grid.SetValue((UINT_PTR)iCell, iValue);
}
}
//Now check if the game is valid
bool bGameSuccess = true;
for(iCell = 0; iCell < 81; iCell++)
{
if(!grid.CheckValid((UINT_PTR)iCell))
{
bGameSuccess = false;
break;
}
}
//Fill the game in progress info
FillTempGame(grid, pstSolveInfo, bGameSuccess);
//Inform parent, OCCASIONALLY else the main thread is overloaded
if(bGameSuccess || ((++pstSolveInfo->ulCounter & 0xFF) == 0))
{
if(bGameSuccess)
pstSolveInfo->ulCounter = (unsigned long)-1;
PostMessage(pstSolveInfo->hWndParent, SOLVE_PROGRESS, 0, 0);
}
if(bGameSuccess) //task finished
break;
}
pstSolveInfo->ceThreadIsFinished.SetEvent();
return 0;
}
#else
UINT SolveSudokuProc(LPVOID pParam)
{
//The pParam needs to be cast to the actual type of object it is
stSolveInfo *pstSolveInfo = (stSolveInfo*)pParam;
//Initialise the grid
CGrid grid;
int iRow, iCol, iCell, iValue;
for(iRow = 0; iRow < 9; iRow++)
{
for(iCol = 0; iCol < 9; iCol++)
{
iCell = iRow * 9 + iCol;
grid.AddCell(iRow, iCol, (UINT_PTR)iCell);
iValue = pstSolveInfo->szGame[iCell] - '0';
grid.SetValue((UINT_PTR)iCell, iValue);
}
}
unsigned long ulCounter = 0;
iCell = 0;
//Start the recursive procedure
ProcessCell(grid, iCell, pstSolveInfo);
//Now check if the game is valid
bool bGameSuccess = true;
for(iCell = 0; iCell < 81; iCell++)
{
if(!grid.CheckValid((UINT_PTR)iCell))
{
bGameSuccess = false;
break;
}
}
//Fill the game in progress info
FillTempGame(grid, pstSolveInfo, bGameSuccess);
pstSolveInfo->ulCounter = (unsigned long)-1;
PostMessage(pstSolveInfo->hWndParent, SOLVE_PROGRESS, 0, 0);
pstSolveInfo->ceThreadIsFinished.SetEvent();
return 0;
}
bool ProcessCell(CGrid& grid, int iCell, stSolveInfo *pstSolveInfo)
{
//If the solve dialog is closing we need to stop the thread
if(::WaitForSingleObject(pstSolveInfo->ceCloseThread.m_hObject, 0) == WAIT_OBJECT_0)
{
pstSolveInfo->ceThreadIsFinished.SetEvent();
return true; //unwind the recursion stack
}
//we work through each cell and use the possible values it could have
//and recurse into this function with the next cell
//either we get no possible values allowed or eventually the solution of the game
if(iCell == 81) //The cell before (81) had a possible value so the game is solved
return true; //unwind the recursion stack
//If this has a 0 then we need to find what is possible for it and start the processing OR we move to the next cell
bool bResult = false;
if(grid.GetValue(iCell) == 0)
{
bool bAllowed[9];
FillMemory(bAllowed, sizeof(bAllowed), true); //initially all are allowed
grid.PrepareAllows(iCell, bAllowed);
//Now loop through the allowed values, set to that then move to next cell via recursion
for(int i = 0; i < 9; i++)
{
if(bAllowed[i])
{
//This is a possible, set the cell value
grid.SetValue(iCell, i+1); //zero based counting - the value is actually one greater than the index
bResult = ProcessCell(grid, iCell + 1, pstSolveInfo);
if(bResult == true)
break;
}
}
if(bResult == false)
{
//unset the value here prior to returning
grid.SetValue(iCell, 0);
}
}
else
bResult = ProcessCell(grid, iCell + 1, pstSolveInfo);
//when starting to unwind we pump the attempted counter to the parent
//note this is really just to give a crude feedback
pstSolveInfo->ulCounter++;
if((pstSolveInfo->ulCounter & 0xFF) == 0) //every 256 increments
{
//Fill the game in progress info
FillTempGame(grid, pstSolveInfo, false);
PostMessage(pstSolveInfo->hWndParent, SOLVE_PROGRESS, 0, 0);
}
return bResult;
}
#endif
void FillTempGame(CGrid& grid, stSolveInfo *pstSolveInfo, bool bGameSuccess)
{
pstSolveInfo->csAccessControl.Lock();
for(int iCell = 0; iCell < 81; iCell++)
{
pstSolveInfo->szGameInProgress[iCell] = '0' + grid.GetValue((UINT_PTR)iCell);
}
pstSolveInfo->bSuccess = bGameSuccess;
pstSolveInfo->csAccessControl.Unlock();
}
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)