// STUBLDlg.cpp : implementation file
//
#include "stdafx.h"
#include "STUBL.h"
#include "STUBLDlg.h"
#include "SudokuPuzzle.h"
#include <sstream>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()
// CSTUBLDlg dialog
CSTUBLDlg::CSTUBLDlg(CWnd* pParent /*=NULL*/)
: CDialog(CSTUBLDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CSTUBLDlg::DoDataExchange(CDataExchange* pDX)
{
DDX_Control(pDX, IDC_BUTTON_SHOWHINTS, m_buttonHints);
DDX_Control(pDX, IDC_LIST_REASONS, m_lbReasons);
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CSTUBLDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_CTLCOLOR()
ON_WM_DROPFILES()
ON_CONTROL_RANGE(EN_CHANGE, 4000, 4091, OnSquareChanged)
//}}AFX_MSG_MAP
ON_COMMAND(ID_FILE_MYOPEN, &CSTUBLDlg::OnFileOpen)
ON_COMMAND(ID_FILE_SAVEAS, &CSTUBLDlg::OnFileSaveAs)
ON_COMMAND(ID_FILE_TYPEINPUZZLE, &CSTUBLDlg::OnFileTypeInPuzzle)
ON_COMMAND(ID_FILE_MYCLOSE, &CSTUBLDlg::OnFileClose)
ON_COMMAND(ID_PUZZLE_CLEAR, &CSTUBLDlg::OnPuzzleClear)
ON_COMMAND(ID_PUZZLE_RESET, &CSTUBLDlg::OnPuzzleReset)
ON_COMMAND(ID_HELP_ABOUTSTUBL, &CSTUBLDlg::OnHelpAboutStubl)
ON_BN_CLICKED(IDC_BUTTON_SOLVE, &CSTUBLDlg::OnBnClickedButtonSolve)
ON_BN_CLICKED(IDC_BUTTON_SHOWHINTS, &CSTUBLDlg::OnBnClickedButtonShowhints)
ON_BN_CLICKED(IDC_BUTTON_HISTORY, &CSTUBLDlg::OnBnClickedButtonHistory)
ON_BN_CLICKED(IDC_BUTTON_STEP, &CSTUBLDlg::OnBnClickedButtonStep)
ON_BN_CLICKED(IDC_BUTTON_STEP_UNTIL_ANSWERFOUND, &CSTUBLDlg::OnBnClickedButtonStepUntilAnswerfound)
ON_BN_CLICKED(IDC_BUTTON_SHOWANSWERS, &CSTUBLDlg::OnBnClickedButtonShowanswers)
END_MESSAGE_MAP()
// CSTUBLDlg message handlers
BOOL CSTUBLDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
pPuzzle = new Sudoku();
cbAnswer = new CBrush(RGB(0xE6,0xE8,0xFA));
cbHint = new CBrush(RGB(0xAF, 0xEE, 0xEE));
cbBlank = new CBrush(RGB(255, 255, 255));
cbImposs = new CBrush(RGB(0x8e, 0x23, 0x23));
cdDlgBgColor = new CBrush(RGB(0xEB, 0xC7, 0x9E));
const int xdim = 36;
const int ydim = 36;
cf = new CFont();
cf->CreateFont(
xdim,
0,
0,
0,
FW_NORMAL,
FALSE,
FALSE,
0,
ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_SWISS,
"Arial");
cfAnswer = new CFont();
cfAnswer->CreateFont(
xdim,
0,
0,
0,
FW_BOLD,
FALSE,
FALSE,
0,
ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_SWISS,
"Arial");
cfHints = new CFont();
cfHints->CreateFont(
14,
0,
0,
0,
FW_NORMAL,
FALSE,
FALSE,
0,
ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_SWISS,
"Arial");
CSudokuEdit *tmpEdit;
int id = 4000;
CRect rStart(10, 10, 10+xdim, 10+ydim);
CRect rPos = rStart;
for (int row = 0; row < 9; ++row)
{
rPos.left = rStart.left;
rPos.right = rStart.right;
for (int col = 0; col < 9; ++col)
{
tmpEdit = new CSudokuEdit();
DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_CENTER | ES_MULTILINE | ES_NUMBER;
if ((row == 0) && (col == 0))
dwStyle |= WS_GROUP;
tmpEdit->Create(dwStyle, rPos, this, ++id);
tmpEdit->SetCellBrushes(cbBlank, cbAnswer, cbHint, cbImposs);
tmpEdit->SetCellFonts(cf, cfAnswer, cfHints, cf);
tmpEdit->SetCellTextColors(RGB(0,0,0), RGB(0x8E, 0x23, 0x23), RGB(0x21, 0x5E, 0x21), RGB(0x00, 0xff, 0x7f));
tmpEdit->SetFont(cf);
tmpEdit->LimitText(1);
tmpEdit->SetPuzzlePtr(pPuzzle);
tmpEdit->SetCellNum(row*9 + col);
tmpEdit->SetCellState(CS_NORMAL);
tmpEdit->SetLB(&m_lbReasons);
Boxes[row][col] = tmpEdit;
rPos.left += (xdim + 2);
rPos.right += (xdim + 2);
if ((col == 2) || (col == 5))
{
rPos.left += 4;
rPos.right += 4;
}
}
rPos.bottom += (ydim + 2);
rPos.top += (ydim + 2);
if ((row == 2) || (row == 5))
{
rPos.bottom += 4;
rPos.top += 4;
}
}
SetHints(false);
for (int i=0; i<81; ++i)
m_puzzleDef[i] = 0;
GetDlgItem(IDC_BUTTON_HISTORY)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_SHOWHINTS)->EnableWindow(FALSE);
GetDlgItem(IDC_LIST_REASONS)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_STEP)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_SOLVE)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_STEP_UNTIL_ANSWERFOUND)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_SHOWANSWERS)->EnableWindow(FALSE);
m_playState = SPS_UNINIT; // no puzzle entered yet
return TRUE; // return TRUE unless you set the focus to a control
}
void CSTUBLDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CSTUBLDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CSTUBLDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CSTUBLDlg::OnBnClickedButtonSolve()
{
if (pPuzzle->SolvePuzzle())
MessageBox("Puzzle Solved");
else
MessageBox("Unable to solve puzzle!");
OnBnClickedButtonShowanswers();
}
void CSTUBLDlg::Restore(char *fName)
{
OnPuzzleClear();
CFile file;
char cBuf[256];
if (file.Open(fName, CFile::modeRead))
{
int fSize = file.Read(cBuf, 256);
file.Close();
int iCell = 0;
int index = 0;
char answerBuf[2];
answerBuf[1] = '\0';
while ((index < fSize) && (iCell != 81))
{
int answer;
bool bValidChar = true;
switch (cBuf[index])
{
case '0':
case '.':
answer = 0;
answerBuf[0] = ' ';
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
answer = cBuf[index] - '0';
answerBuf[0] = cBuf[index];
break;
default:
bValidChar = false;
break;
}
if (bValidChar)
{
m_puzzleDef[iCell] = answer;
Boxes[iCell/9][iCell%9]->SetWindowTextA(answerBuf);
if (answer != 0)
{
pPuzzle->SetAnswer(iCell, answer, std::string("Read from File"));
Boxes[iCell/9][iCell%9]->SetCellState(CS_ANSWER);
}
Boxes[iCell/9][iCell%9]->RefreshCell(false, true);
++iCell;
}
++index;
}
}
GetDlgItem(IDC_BUTTON_HISTORY)->EnableWindow(TRUE);
GetDlgItem(IDC_BUTTON_SHOWHINTS)->EnableWindow(TRUE);
GetDlgItem(IDC_LIST_REASONS)->EnableWindow(TRUE);
GetDlgItem(IDC_BUTTON_STEP)->EnableWindow(TRUE);
GetDlgItem(IDC_BUTTON_SOLVE)->EnableWindow(TRUE);
GetDlgItem(IDC_BUTTON_STEP_UNTIL_ANSWERFOUND)->EnableWindow(TRUE);
GetDlgItem(IDC_BUTTON_SHOWANSWERS)->EnableWindow(TRUE);
m_playState = SPS_PLAYING;
PerformInitialCheck();
//if (pPuzzle->SolvePuzzle())
// MessageBox("Puzzle Solved");
//else
// MessageBox("Unable to solve puzzle!");
}
bool CSTUBLDlg::GetFileName(bool save, CString& cs)
{
char szFilters[]="Sudoku Files (*.sudoku)|*.sudoku|All Files (*.*)|*.*||";
CFileDialog *fileDlg;
if (save)
{
fileDlg = new CFileDialog(FALSE, "Sudoku", "*.Sudoku", OFN_OVERWRITEPROMPT |
OFN_PATHMUSTEXIST, szFilters, this);
}
else
{
fileDlg = new CFileDialog(TRUE, "Sudoku", "*.Sudoku",
OFN_FILEMUSTEXIST| OFN_HIDEREADONLY, szFilters, this);
}
// Display the file dialog. When user clicks OK, fileDlg.DoModal()
// returns IDOK.
if( fileDlg->DoModal ()==IDOK )
{
cs = fileDlg->GetPathName();
return true;
}
return false;
};
void CSTUBLDlg::OnSquareChanged( UINT nID )
{
int square = nID - 4001;
CSudokuEdit* tmpEdit = Boxes[square/9][square%9];
if (m_playState == SPS_UNINIT)
{
MessageBox("Please enter a puzzle in one of three ways:\n\r"
"1. By selecting File->Type In Puzzle\n\r"
"2. By selecting File->Open\n\r"
"3. By dragging and dropping a sudoku file");
tmpEdit->SetWindowTextA("");
return;
}
tmpEdit->SetChanged(false);
if (tmpEdit->GetCellState() == CS_HINT)
{
}
else
{
CString csNum;
tmpEdit->GetWindowText(csNum);
int iVal = atoi(csNum.GetBuffer());
csNum.ReleaseBuffer();
if (m_playState == SPS_ENTERING_PUZZLE)
m_puzzleDef[square] = iVal;
if (iVal == 0)
{
//pPuzzle->ClearAnswer(square);
tmpEdit->SetCellState(CS_NORMAL);
tmpEdit->SetGuess(0);
}
else
{
if (pPuzzle->IsDigitPossible(square, iVal))
{
if (m_playState == SPS_ENTERING_PUZZLE)
{
tmpEdit->SetCellState(CS_GUESS);
tmpEdit->SetGuess(iVal);
//pPuzzle->SetAnswer(square, iVal, "Puzzle Input");
//tmpEdit->SetCellState(CS_ANSWER);
}
else
{
int realAnswer = pPuzzle->GetAnswer(square);
if ((realAnswer != 0) && (realAnswer == iVal))
{
tmpEdit->SetCellState(CS_ANSWER);
}
else
{
tmpEdit->SetCellState(CS_GUESS);
tmpEdit->SetGuess(iVal);
}
}
}
else
tmpEdit->SetCellState(CS_IMPOSSIBLE);
//if (pPuzzle->SetAnswer(square, iVal, std::string("Entered by user")))
// tmpEdit->SetCellState(CS_GUESS);
//else
// tmpEdit->SetCellState(CS_IMPOSSIBLE);
tmpEdit->RefreshCell(false);
}
}
}
HBRUSH CSTUBLDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
// Call the base class implementation first! Otherwise, it may
// undo what we're trying to accomplish here.
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
if (nCtlColor == CTLCOLOR_DLG)
{
// Set the text color to red
pDC->SetTextColor(RGB(255, 0, 0));
// Set the background mode for text to transparent
// so background will show thru.
pDC->SetBkMode(TRANSPARENT);
// Return handle to our CBrush object
hbr = *cdDlgBgColor;
}
return hbr;
}
void CSTUBLDlg::SetHints(bool hintsOn)
{
bShowHints = hintsOn;
if (bShowHints)
m_buttonHints.SetWindowTextA("HIDE POSSIBILITIES");
else
m_buttonHints.SetWindowTextA("SHOW POSSIBILITIES");
}
void CSTUBLDlg::OnBnClickedButtonShowhints()
{
SetHints(bShowHints == false);
for (int cell=0; cell<81; ++cell)
Boxes[cell/9][cell%9]->RefreshCell(bShowHints);
}
void CSTUBLDlg::OnDropFiles(HDROP hDropInfo)
{
char fName[1024];
DragQueryFile(hDropInfo, 0, fName, 1024);
Restore(fName);
}
void CSTUBLDlg::OnBnClickedButtonHistory()
{
m_lbReasons.ResetContent();
std::vector<std::string> history;
std::vector<std::string>::iterator historyIter;
pPuzzle->GetHistory(history);
for (historyIter = history.begin(); historyIter != history.end(); ++historyIter)
m_lbReasons.AddString(historyIter->c_str());
}
void CSTUBLDlg::OnBnClickedButtonStep()
{
std::vector<int> vChangedCells;
std::vector<int>::iterator vChangedIter;
for (int cell=0; cell<81; ++cell)
Boxes[cell/9][cell%9]->SetChanged(false);
pPuzzle->TakeStep(vChangedCells);
for (vChangedIter = vChangedCells.begin(); vChangedIter != vChangedCells.end(); vChangedIter++)
{
Boxes[(*vChangedIter)/9][(*vChangedIter)%9]->SetChanged(true);
}
//if (!pPuzzle->TakeStep())
//{
// MessageBox("Puzzle could not be solved! Try the Desperate or Really Desperate buttons.");
//}
for (int cell=0; cell<81; ++cell)
Boxes[cell/9][cell%9]->RefreshCell(bShowHints, true);
OnBnClickedButtonHistory();
}
void CSTUBLDlg::OnFileOpen()
{
CString cs;
OnPuzzleClear();
if (GetFileName(false, cs))
{
Restore(cs.GetBuffer());
}
}
void CSTUBLDlg::OnFileSaveAs()
{
char buf[8];
CString cs;
char cBuf[96];
CFile file;
int counter=0;
if (GetFileName(true, cs))
{
if (file.Open(cs.GetBuffer(), CFile::modeCreate | CFile::modeWrite))
{
for (int row=0; row<9; ++row)
{
for (int col=0; col<9; ++col)
{
(Boxes[row][col])->GetWindowText(buf, 8);
if (buf[0] == '\0')
cBuf[counter] = '0';
else
cBuf[counter] = buf[0];
counter++;
}
}
file.Write(cBuf, 81);
file.Close();
}
}
}
void CSTUBLDlg::OnFileTypeInPuzzle()
{
if (m_playState == SPS_ENTERING_PUZZLE)
{
m_playState = SPS_PLAYING;
// Now change all the guesses to answers
for (int i=0; i<81; ++i)
{
CSudokuEdit* tmpEdit = Boxes[i/9][i%9];
int answer = tmpEdit->GetGuess();
if (answer != 0)
{
pPuzzle->SetAnswer(i, answer, "Puzzle Input By User");
tmpEdit->SetCellState(CS_ANSWER);
}
else
tmpEdit->SetCellState(CS_NORMAL);
}
//if (pPuzzle->SolvePuzzle())
// MessageBox("Puzzle Solved");
//else
// MessageBox("Unable to solve puzzle!");
GetDlgItem(IDC_BUTTON_HISTORY)->EnableWindow(TRUE);
GetDlgItem(IDC_BUTTON_SHOWHINTS)->EnableWindow(TRUE);
GetDlgItem(IDC_LIST_REASONS)->EnableWindow(TRUE);
GetDlgItem(IDC_BUTTON_STEP)->EnableWindow(TRUE);
GetDlgItem(IDC_BUTTON_STEP_UNTIL_ANSWERFOUND)->EnableWindow(TRUE);
GetDlgItem(IDC_BUTTON_SOLVE)->EnableWindow(TRUE);
GetDlgItem(IDC_BUTTON_SHOWANSWERS)->EnableWindow(TRUE);
PerformInitialCheck();
}
else
{
MessageBox("Enter digits into cells. Select this menu item again to indicate "
"you have completed your input.");
OnPuzzleClear();
m_playState = SPS_ENTERING_PUZZLE;
GetDlgItem(IDC_BUTTON_HISTORY)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_SHOWHINTS)->EnableWindow(FALSE);
GetDlgItem(IDC_LIST_REASONS)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_STEP)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_STEP_UNTIL_ANSWERFOUND)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_SOLVE)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_SHOWANSWERS)->EnableWindow(FALSE);
}
}
void CSTUBLDlg::OnFileClose()
{
OnOK();}
void CSTUBLDlg::OnPuzzleClear()
{
if (pPuzzle)
delete pPuzzle;
for (int i=0; i<81; ++i)
m_puzzleDef[i] = 0;
pPuzzle = new Sudoku();
SetHints(false);
for (int cell=0; cell<81; ++cell)
{
Boxes[cell/9][cell%9]->SetWindowTextA("");
Boxes[cell/9][cell%9]->SetPuzzlePtr(pPuzzle);
Boxes[cell/9][cell%9]->SetCellState(CS_NORMAL);
Boxes[cell/9][cell%9]->RefreshCell(false);
}
}
void CSTUBLDlg::OnPuzzleReset()
{
if (pPuzzle)
delete pPuzzle;
pPuzzle = new Sudoku();
SetHints(false);
std::stringstream ss;
for (int cell=0; cell<81; ++cell)
{
ss.str("");
ss << m_puzzleDef[cell];
Boxes[cell/9][cell%9]->SetWindowTextA(ss.str().c_str());
pPuzzle->SetAnswer(cell, m_puzzleDef[cell], std::string("User Reset"));
Boxes[cell/9][cell%9]->SetPuzzlePtr(pPuzzle);
if (m_puzzleDef[cell] == 0)
Boxes[cell/9][cell%9]->SetCellState(CS_NORMAL);
else
Boxes[cell/9][cell%9]->SetCellState(CS_NORMAL);
Boxes[cell/9][cell%9]->RefreshCell(false, true);
}
}
void CSTUBLDlg::OnHelpAboutStubl()
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
void CSTUBLDlg::OnBnClickedButtonStepUntilAnswerfound()
{
std::vector<int> vChangedCells;
std::vector<int>::iterator vChangedIter;
for (int cell=0; cell<81; ++cell)
Boxes[cell/9][cell%9]->SetChanged(false);
pPuzzle->TakeStep(vChangedCells, true);
for (vChangedIter = vChangedCells.begin(); vChangedIter != vChangedCells.end(); vChangedIter++)
{
if (pPuzzle->GetAnswer(*vChangedIter) != 0)
Boxes[(*vChangedIter)/9][(*vChangedIter)%9]->SetChanged(true);
}
//if (!pPuzzle->TakeStep())
//{
// MessageBox("Puzzle could not be solved! Try the Desperate or Really Desperate buttons.");
//}
for (int cell=0; cell<81; ++cell)
Boxes[cell/9][cell%9]->RefreshCell(bShowHints, false);
//OnBnClickedButtonHistory();
}
void CSTUBLDlg::PerformInitialCheck()
{
std::vector<int> vChangedCells;
std::vector<int>::iterator vChangedIter;
for (int cell=0; cell<81; ++cell)
Boxes[cell/9][cell%9]->SetChanged(false);
pPuzzle->RunAlgsForNewPuzzle(vChangedCells);
for (vChangedIter = vChangedCells.begin(); vChangedIter != vChangedCells.end(); vChangedIter++)
{
if (pPuzzle->GetAnswer(*vChangedIter) != 0)
Boxes[(*vChangedIter)/9][(*vChangedIter)%9]->SetChanged(true);
}
for (int cell=0; cell<81; ++cell)
Boxes[cell/9][cell%9]->RefreshCell(bShowHints, false);
}
void CSTUBLDlg::OnBnClickedButtonShowanswers()
{
SetHints(false);
for (int cell=0; cell<81; ++cell)
Boxes[cell/9][cell%9]->RefreshCell(false, true);
}