 |
|
 |
Very nice. Solving these manualy includes the process of elimination - the ability to eliminate individual candidates that are actualy not possible even if they are currently valid. For example, if a 9x9 box has candidates of 2's in only one row (or column), then the solution must have a 2 in that row (or column) of that box. So any 2's in the same row (or column) outside that box can be ruled out. The user should be allowed to remove it/them. lforster@wowway.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
|
 |
|
 |
 Nice job very fast proof. The best i have seen on the web.
Best Regards,
Mark Edgar Software Engineer interconnect www.onedollarsudoku.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
There's a small error in the gui:
When I use the shortcuts for highlighting cells and press Ctrl+ the is printed into the active cell.
None the less a great article!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
To fix this you need to check for the control key in the PreTranslateMessage function using GetKeyState, e.g.
BOOL CXSudokuWnd::PreTranslateMessage(MSG* pMsg) { switch (pMsg->message) { case WM_KEYUP: //TRACE(_T("WM_KEYUP\n")); if ((pMsg->wParam >= _T('0')) && (pMsg->wParam <= _T('9')) && !(GetKeyState(VK_CONTROL) & 0x8000))
Graham
Librarians rule, Ook!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
There's a small error in the gui:
When I use the shortcuts for highlighting cells and press Ctrl+ eg. the is printed into the active cell.
None the less a great article!
|
| Sign In·View Thread·PermaLink | 3.00/5 (1 vote) |
|
|
|
 |
|
 |
Thank you for the great article. I am very new to programming and have found Code Project a great resource. With your solution, have you though of adding the ability to define the 3 x 3 grid to allow for Jigsaw Sudoku puzzles like the ones at www.sudoku.org.uk/jigsaw.asp.
There is a HTA application at http://groups.yahoo.com/group/sudoku-tutor that uses vbscript to do this, but I don't know enough to get it working in C++ (or .net) as yet.
thanks again for the article and information. Rated 5 all the way.
-- modified at 15:57 Saturday 8th July, 2006
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Have you thought of adding an abbreviated input format? For example, the example you give is:
7.8 ... 3.. ... 2.1 ... 5.. ... ...
.4. ... .26 3.. .8. ... ... 1.. .9.
.9. 6.. ..4 ... .7. 5.. ... ... ...
This could be input as follows:
708//300 /201 500 40//26 300/80 /100/90 90/600/4 /70/500 //
Others may be able to devise an even more economical format.
Nigel
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
Great Solution - it makes solving Sudoku too easy.
I have found when solving a puzzle that it can you can see that certain numbers cannot be in certain locations. I have extended the application slightly by allowing the user to rub out numbers from the grid, or add them back in again (Right Click on box, and the select Rub Out, or Pencil In as required). Would you be interested in this - if so, how do you go about posting it?
Thanks for the app.
Mike
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Hi
I havnt been able to work out how to post an attachment, so I have included XSudokuWnd.cpp within this reply - sorry if this isnt the right way to go about this!
The changes that I have made are : 1 Added m_nUnPencil[9][9] to XSudokuWnd.h 2 Defined new popuup menu items ID_POPUP_UNPENCIL_1 - 9 & ID_POPUP_PENCIL_1 - 9, and added in the relevant ON_COMMAND_RANGE to the Message Map 3 Added additional functions OnPopupUnPencil(), OnPopupPencil(), GetUnPenciledCandidates() 4 Altered ShowPopup() function to display Rub Out [x], or Pencil In [X] options.
Again I apologise if I havnt posted this properly - this is my first time!
Mike
// XSudokuWnd.cpp Version 1.2 // // Author: Hans Dietrich // hdietrich@gmail.com // // Description: // XSudokuWnd implements CXSudokuWnd, a class that displays a Sudoku puzzle. // // History // Version 1.2 - 2006 January 17 // - Initial public release // // Public APIs: // NAME DESCRIPTION // --------------------- ----------------------------------------- // CaptureBitmap() Copy window bitmap to clipboard // Get3x3Gridline() Get 3x3 gridline color // GetCellBackground() Get cell background color // GetCurCellBorder() Get current cell border color // GetGivens() Get color for given values // GetGivenValues() Get given values // GetHighlightNumber() Get highlight number // GetHighlightNumberColor() Get highlight color // GetHints() Get hint values // GetLabels() Get label color // GetPencilMarks() Get pencil marks color // GetShowPencilMarks() Get show state of pencil marks // GetShowSolution() Get show state of solution // GetSolution() Get solution color // GetSolutionValues() Get solution values // GetSudoku() Display Sudoku entry dialog // GetUndoEnable() Get enable state of undo facility // GetUserEntries() Get user entry values // GetUserEntry() Get user entry color // GetWindowBackground() Get window background color // GetWindowSize() Get minimum size of XSudokuWnd window // IsValid() Returns TRUE if Sudoku is valid (exactly 1 solution) // LoadFromClipboard() Load Sudoku from clipboard // LoadFromFile() Load Sudoku from file // LoadFromString() Load Sudoku from string // PrintBitmap() Print window bitmap // Set3x3Gridline() Set 3x3 gridline color // SetCellBackground() Set cell background color // SetCurCellBorder() Set current cell border color // SetGivens() Set color for given values // SetHighlightNumber() Set highlight number // SetHighlightNumberColor() Set highlight color // SetLabels() Set label color // SetMessageHwnd() Set hwnd for message callbacks // SetPencilMarks() Set pencil marks color // SetShowPencilMarks() Set show state of pencil marks // SetShowSolution() Set show state of solution values // SetSolution() Set solution color // SetUndoEnable() Set enable state of undo facility // SetUserEntry() Set user entry color // SetWindowBackground() Set window background color // ShowColorPrefsDlg() Show color prefs dialog // // License: // This software is released into the public domain. You are free to use // it in any way you like, except that you may not sell this source code. // // This software is provided "as is" with no expressed or implied warranty. // I accept no liability for any damage or loss of business that this // software may cause. // ///////////////////////////////////////////////////////////////////////////////
#include "stdafx.h" #include "resource.h" #include "XSudokuWnd.h" #include "FontSize.h" #include "dance.h" #include "memdc.h" #include "clipboard.h" #include "GDIUtil.h" #include "DllInstanceSwitcher.h" #include "SudokuEntryDlg.h" #include "ColorPrefDlg.h" #include "About.h"
#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif
IMPLEMENT_DYNAMIC(CXSudokuWnd,CWnd)
UINT WM_XSUDOKU = ::RegisterWindowMessage(_T("WM_XSUDOKU"));
static int g_nGridLineOffset[9] = { 0, 0, 0, 1, 1, 1, 2, 2, 2 }; static TCHAR * g_szDigits = _T("0123456789");
#define SetBit(x,y) ( =(1<<(y))) #define ClearBit(x,y) (x&=~(1<<(y))) #define TestBit(x,y) (x&(1<<(y)))
/////////////////////////////////////////////////////////////////////////////// // // undo / redo //
#define UNDO_ARRAY_GROW_BY_SIZE 20
#pragma pack(push,1) struct UNDO_BLOCK { UNDO_BLOCK() { TRACE(_T("in UNDO_BLOCK()\n")); action_code = 0; action_value = 0; row = col = 0; };
BYTE action_code; #define ACTION_CODE_USER_ENTRY 1 // action_value = 0 to remove user entry #define ACTION_CODE_USER_ENTRY_REMOVE_ALL 2 #define ACTION_CODE_HINT 3 // action_value: 0 = hide hint; else 1-9 #define ACTION_CODE_HINT_REMOVE_ALL 4
BYTE action_value; BYTE row, col; // current row & column BYTE hints[9][9]; BYTE user_entries[9][9]; }; #pragma pack(pop)
/////////////////////////////////////////////////////////////////////////////// // Popup menu items #define ID_POPUP_HIGHLIGHT_0 50000 #define ID_POPUP_HIGHLIGHT_1 50001 #define ID_POPUP_HIGHLIGHT_2 50002 #define ID_POPUP_HIGHLIGHT_3 50003 #define ID_POPUP_HIGHLIGHT_4 50004 #define ID_POPUP_HIGHLIGHT_5 50005 #define ID_POPUP_HIGHLIGHT_6 50006 #define ID_POPUP_HIGHLIGHT_7 50007 #define ID_POPUP_HIGHLIGHT_8 50008 #define ID_POPUP_HIGHLIGHT_9 50009
#define ID_POPUP_SET_1 50011 #define ID_POPUP_SET_2 50012 #define ID_POPUP_SET_3 50013 #define ID_POPUP_SET_4 50014 #define ID_POPUP_SET_5 50015 #define ID_POPUP_SET_6 50016 #define ID_POPUP_SET_7 50017 #define ID_POPUP_SET_8 50018 #define ID_POPUP_SET_9 50019
#define ID_POPUP_UNPENCIL_1 50021 #define ID_POPUP_UNPENCIL_2 50022 #define ID_POPUP_UNPENCIL_3 50023 #define ID_POPUP_UNPENCIL_4 50024 #define ID_POPUP_UNPENCIL_5 50025 #define ID_POPUP_UNPENCIL_6 50026 #define ID_POPUP_UNPENCIL_7 50027 #define ID_POPUP_UNPENCIL_8 50028 #define ID_POPUP_UNPENCIL_9 50029
#define ID_POPUP_PENCIL_1 50051 #define ID_POPUP_PENCIL_2 50052 #define ID_POPUP_PENCIL_3 50053 #define ID_POPUP_PENCIL_4 50054 #define ID_POPUP_PENCIL_5 50055 #define ID_POPUP_PENCIL_6 50056 #define ID_POPUP_PENCIL_7 50057 #define ID_POPUP_PENCIL_8 50058 #define ID_POPUP_PENCIL_9 50059
#define ID_POPUP_SHOW_HINT 50030 #define ID_POPUP_REMOVE_ALL_HINTS 50031 #define ID_POPUP_SHOW_SOLUTION 50032 #define ID_POPUP_SHOW_PENCIL_MARKS 50033 #define ID_POPUP_RESET 50034 #define ID_POPUP_REMOVE_USER_ENTRY 50035 #define ID_POPUP_REMOVE_ALL_USER_ENTRIES 50036 #define ID_POPUP_CHECK_USER_ENTRIES 50037 #define ID_POPUP_PRINT 50038 #define ID_POPUP_COPY 50039 #define ID_POPUP_UNDO 50040 #define ID_POPUP_REDO 50041
/////////////////////////////////////////////////////////////////////////////// // CXSudokuWnd Message Map
BEGIN_MESSAGE_MAP(CXSudokuWnd, CWnd) //{{AFX_MSG_MAP(CXSudokuWnd) ON_WM_ERASEBKGND() ON_WM_PAINT() ON_WM_LBUTTONDOWN() ON_WM_RBUTTONUP() ON_WM_LBUTTONDBLCLK() ON_WM_GETDLGCODE() ON_WM_RBUTTONDOWN() ON_COMMAND(ID_RIGHT_CLICK, OnRightClick) ON_COMMAND(ID_COLOR_PREFS, OnColorPrefs) ON_COMMAND(ID_LOAD_SAMPLE, OnLoadSample) ON_COMMAND(ID_SHOW_HINT, OnShowHint) ON_COMMAND(ID_SHOW_SOLUTION, OnShowSolution) ON_COMMAND(ID_SHOW_PENCIL_MARKS, OnShowPencilMarks) ON_COMMAND(ID_EDIT_UNDO, OnEditUndo) ON_COMMAND(ID_EDIT_REDO, OnEditRedo) ON_COMMAND(ID_EDIT_COPY, OnCopyWindow) ON_COMMAND(ID_FILE_PRINT, OnPrintWindow) ON_COMMAND(ID_APP_ABOUT, OnAppAbout) //}}AFX_MSG_MAP ON_COMMAND(ID_POPUP_UNDO, OnEditUndo) ON_COMMAND(ID_POPUP_REDO, OnEditRedo) ON_COMMAND(ID_POPUP_SHOW_HINT, OnPopupShowHint) ON_COMMAND(ID_POPUP_REMOVE_ALL_HINTS, OnRemoveAllHints) ON_COMMAND(ID_POPUP_SHOW_SOLUTION, OnShowSolution) ON_COMMAND(ID_POPUP_SHOW_PENCIL_MARKS, OnShowPencilMarks) ON_COMMAND(ID_POPUP_RESET, OnReset) ON_COMMAND(ID_POPUP_PRINT, OnPrintWindow) ON_COMMAND(ID_POPUP_COPY, OnCopyWindow) ON_COMMAND(ID_POPUP_REMOVE_USER_ENTRY, OnRemoveUserEntry) ON_COMMAND(ID_POPUP_REMOVE_ALL_USER_ENTRIES, OnRemoveAllUserEntries) ON_COMMAND(ID_POPUP_CHECK_USER_ENTRIES, OnCheckUserEntries) ON_COMMAND_RANGE(ID_POPUP_SET_1, ID_POPUP_SET_9, OnPopupSet) ON_COMMAND_RANGE(ID_POPUP_UNPENCIL_1, ID_POPUP_UNPENCIL_9, OnPopupUnPencil) ON_COMMAND_RANGE(ID_POPUP_PENCIL_1, ID_POPUP_PENCIL_9, OnPopupPencil) ON_COMMAND_RANGE(ID_POPUP_HIGHLIGHT_1, ID_POPUP_HIGHLIGHT_9, OnPopupHighlight) ON_COMMAND_RANGE(ID_HIGHLIGHT_0, ID_HIGHLIGHT_9, OnHighlight) END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////// // // CXSudokuWnd() // // Purpose: Construct CXSudokuWnd object // // Parameters: None // // Returns: None // // Notes: Construction is a two-step process. First, construct the // CXSudokuWnd object. Second, call CXSudokuWnd::Create to create // the CXSudokuWnd window. // CXSudokuWnd::CXSudokuWnd() { m_bIsValid = FALSE; m_hWndMessage = NULL; m_hAccel = NULL; m_bPencilMarks = FALSE; m_bHaveGivens = FALSE; m_bShowSolution = FALSE; m_rgbWindowBackground = RGB(220,20,60); m_rgbCellBackground = RGB(192,192,192); m_rgbLabels = RGB(255,255,255); m_rgbGivens = RGB(220,20,60); m_rgbSolution = RGB(0, 0, 255); m_rgbUserEntry = RGB(0, 0, 0); m_rgbPencilMarks = RGB(0, 0, 128); m_rgbHighlightNumber = RGB(135, 206, 250); m_rgbCurCellBorder = RGB(255, 255, 0); m_rgb3x3Gridline = RGB(0,0,0); m_strLabelFont = _T("Verdana"); m_nXOffset = 20; m_nYOffset = 20; m_nCellWidth = 45; m_nCellHeight = 44; m_nHighlightNumber = 0; m_nCurRow = m_nCurCol = -1; m_pointPopup = CPoint(-1,-1); m_strFile = _T("");
// initialize undo facility m_bEnableUndo = TRUE; m_nUndoIndex = 0; m_nUndoLastEntry = 0; m_nUndoSize = UNDO_ARRAY_GROW_BY_SIZE; m_Undo.SetSize(m_nUndoSize); for (int k = 0; k < m_nUndoSize; k++) { UNDO_BLOCK * pUB = new UNDO_BLOCK; ASSERT(pUB); m_Undo[k] = pUB; }
// initialize all arrays for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { m_nGivens[i][j] = 0; m_nSolution[i][j] = 0; m_nHints[i][j] = 0; m_nUserEntries[i][j] = 0; } } }
/////////////////////////////////////////////////////////////////////////////// // // ~CXSudokuWnd() // // Purpose: Destroy CXSudokuWnd object. // // Parameters: None // // Returns: None // CXSudokuWnd::~CXSudokuWnd() { if (m_fontLabels.GetSafeHandle()) m_fontLabels.DeleteObject();
if (m_fontValues.GetSafeHandle()) m_fontValues.DeleteObject();
if (m_fontPencilMarks.GetSafeHandle()) m_fontPencilMarks.DeleteObject();
if (m_fontPrint.GetSafeHandle()) m_fontPrint.DeleteObject();
for (int j = 0; j < m_nUndoSize; j++) { UNDO_BLOCK * pUB = (UNDO_BLOCK *) m_Undo[j]; ASSERT(pUB); if (pUB) {
#ifdef _DEBUG CString str = ActionCodeToString(pUB->action_code); if (!str.IsEmpty()) { TRACE(_T("deleting undo action '%s'\n"), str); } #endif delete pUB; } } }
/////////////////////////////////////////////////////////////////////////////// // // OnGetDlgCode() // // Purpose: The WM_GETDLGCODE message is sent to the window procedure // associated with a control. By default, the system handles all // keyboard input to the control; the system interprets certain // types of keyboard input as dialog box navigation keys. To // override this default behavior, the control can respond to the // WM_GETDLGCODE message to indicate the types of input it wants // to process itself. // // Parameters: None // // Returns: UINT - always DLGC_WANTALLKEYS // UINT CXSudokuWnd::OnGetDlgCode() { return DLGC_WANTALLKEYS; }
/////////////////////////////////////////////////////////////////////////////// // // OnEraseBkgnd() // // Purpose: The framework calls this member function when the CWnd object // background needs erasing. // // Parameters: pDC - pointer to device context object // // Returns: BOOL - TRUE = background was erased; otherwise FALSE // BOOL CXSudokuWnd::OnEraseBkgnd(CDC* pDC) { EraseBkgnd(pDC); return CWnd::OnEraseBkgnd(pDC); }
/////////////////////////////////////////////////////////////////////////////// // // EraseBkgnd() // // Purpose: Erase background by filling client rect with control's window // background color. // // Parameters: pDC - pointer to device context object // // Returns: None // void CXSudokuWnd::EraseBkgnd(CDC* pDC) { CRect rect; GetClientRect(&rect); pDC->FillSolidRect(rect, m_rgbWindowBackground); }
/////////////////////////////////////////////////////////////////////////////// // // OnPaint() // // Purpose: The framework calls this member function when Windows or an // application makes a request to repaint a portion of the // control's window. // // Parameters: None // // Returns: None // // Notes: OnPaint() always first fills the entire client background with // the control's background color, so there is never a need to call // Invalidate() with TRUE. // void CXSudokuWnd::OnPaint() { CPaintDC dc(this); // device context for painting
CMemDC memDC(&dc);
EraseBkgnd(&memDC);
for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { PaintCell(&memDC, i, j); } }
Paint3x3Gridlines(&memDC);
PaintLabels(&memDC);
if (m_bHaveGivens) { PaintValues(&memDC);
if (m_bPencilMarks) PaintPencilMarks(&memDC); }
PaintCurCell(&memDC);
DefWindowProc(WM_PAINT, (WPARAM)memDC->m_hDC, (LPARAM)0);
// Do not call CWnd::OnPaint() for painting messages }
/////////////////////////////////////////////////////////////////////////////// // // GetWindowSize() // // Purpose: Get size needed for XSudokuWnd client rect. // // Parameters: None // // Returns: CSize - size in pixels of client rect, including margins and // grid lines. // CSize CXSudokuWnd::GetWindowSize() { CSize size(0, 0);
size.cx = 2 * m_nXOffset + 9 * (m_nCellWidth+1) + 2; size.cy = 2 * m_nYOffset + 9 * (m_nCellHeight+1) + 2;
return size; }
/////////////////////////////////////////////////////////////////////////////// // // GetCellBackgroundColor() // // Purpose: Get current background color of a cell // // Parameters: row - row number (0 - 8) // col - column number (0 - 8) // // Returns: COLORREF - current background color for cell; this will // depend on whether the cell is highlighted. // COLORREF CXSudokuWnd::GetCellBackgroundColor(int row, int col) { COLORREF rgb = m_rgbCellBackground;
if (m_bHaveGivens && (m_nHighlightNumber != 0)) { WORD wCandidates = GetCandidates(row, col) & GetUnPenciledCandidates(row, col);//get the possible candidates, less the rubbed out values
int nGiven = m_nGivens[row][col]; int nSolution = m_nSolution[row][col]; int nHint = m_nHints[row][col]; int nUserEntry = m_nUserEntries[row][col];
if (nGiven) { if (nGiven == m_nHighlightNumber) rgb = m_rgbHighlightNumber; } else if (nHint) { if (nHint == m_nHighlightNumber) rgb = m_rgbHighlightNumber; } else if (nUserEntry) { if (nUserEntry == m_nHighlightNumber) rgb = m_rgbHighlightNumber; } else if (nSolution && m_bShowSolution) { if (nSolution == m_nHighlightNumber) rgb = m_rgbHighlightNumber; } else if (TestBit(wCandidates, m_nHighlightNumber)) { rgb = m_rgbHighlightNumber; } }
return rgb; }
/////////////////////////////////////////////////////////////////////////////// // // PaintCell() // // Purpose: Fill cell with correct background color // // Parameters: pDC - pointer to device context object // row - row number (0 - 8) // col - column number (0 - 8) // // Returns: None // void CXSudokuWnd::PaintCell(CDC * pDC, int row, int col) { CRect rectCell = GetCellRect(row, col); pDC->FillSolidRect(&rectCell, GetCellBackgroundColor(row, col)); }
/////////////////////////////////////////////////////////////////////////////// // // Paint3x3Gridlines() // // Purpose: Draw 3x3 grid lines // // Parameters: pDC - pointer to device context object // // Returns: None // void CXSudokuWnd::Paint3x3Gridlines(CDC * pDC) { CPen pen(PS_SOLID, 1, m_rgb3x3Gridline);
CPen *pOldPen = pDC->SelectObject(&pen);
CRect rectWnd, rectLine; GetClientRect(&rectWnd); rectLine.top = rectWnd.top + m_nYOffset; rectLine.bottom = rectLine.top + 9 * m_nCellHeight + 8 + 2;
// vertical lines rectLine.left = rectWnd.left + m_nXOffset + 3 * m_nCellWidth + 2; pDC->MoveTo(rectLine.left, rectLine.top); pDC->LineTo(rectLine.left, rectLine.bottom); pDC->MoveTo(rectLine.left+1, rectLine.top); pDC->LineTo(rectLine.left+1, rectLine.bottom);
rectLine.left += 2 + 3 * m_nCellWidth + 2; pDC->MoveTo(rectLine.left, rectLine.top); pDC->LineTo(rectLine.left, rectLine.bottom); pDC->MoveTo(rectLine.left+1, rectLine.top); pDC->LineTo(rectLine.left+1, rectLine.bottom);
// horizontal lines rectLine.left = rectWnd.left + m_nXOffset; rectLine.right = rectLine.left + 9 * m_nCellWidth + 8 + 2; rectLine.top += 3 * m_nCellHeight + 2; pDC->MoveTo(rectLine.left, rectLine.top); pDC->LineTo(rectLine.right, rectLine.top); pDC->MoveTo(rectLine.left, rectLine.top+1); pDC->LineTo(rectLine.right, rectLine.top+1);
rectLine.top += 3 * m_nCellHeight + 2 + 2; pDC->MoveTo(rectLine.left, rectLine.top); pDC->LineTo(rectLine.right, rectLine.top); pDC->MoveTo(rectLine.left, rectLine.top+1); pDC->LineTo(rectLine.right, rectLine.top+1);
pDC->SelectObject(pOldPen); }
/////////////////////////////////////////////////////////////////////////////// // // PaintLabels() // // Purpose: Draw row and column labels // // Parameters: pDC - pointer to device context object // // Returns: None // void CXSudokuWnd::PaintLabels(CDC * pDC) { CFont *pOldFont = pDC->SelectObject(&m_fontLabels);
pDC->SetBkColor(m_rgbWindowBackground); pDC->SetTextColor(m_rgbLabels);
CRect rectWnd, rectLabel; GetClientRect(&rectWnd); rectLabel.top = rectWnd.top + m_nYOffset - m_nLabelHeight - 2; rectLabel.bottom = rectLabel.top + m_nLabelHeight + 2; rectLabel.left = rectWnd.left + m_nXOffset + m_nCellWidth/2 - 3; rectLabel.right = rectLabel.left + m_nLabelHeight;
static TCHAR * szXLabel = _T("ABCDEFGHI"); int i = 0; for (i = 0; i < 9; i++) { rectLabel.left += g_nGridLineOffset[i]; rectLabel.right = rectLabel.left + m_nLabelHeight;
pDC->ExtTextOut(rectLabel.left, rectLabel.top, ETO_CLIPPED|ETO_OPAQUE, &rectLabel, &szXLabel[i], 1, NULL);
rectLabel.left += m_nCellWidth; }
rectLabel.top = rectWnd.top + m_nYOffset + m_nCellHeight/2 - 5; rectLabel.bottom = rectLabel.top + m_nLabelHeight + 2; rectLabel.left = rectWnd.left + m_nXOffset - m_nLabelHeight - 2; rectLabel.right = rectLabel.left + m_nLabelHeight;
for (i = 0; i < 9; i++) { rectLabel.top += g_nGridLineOffset[i]; rectLabel.bottom = rectLabel.top + m_nLabelHeight + 1;
pDC->ExtTextOut(rectLabel.left, rectLabel.top, ETO_CLIPPED|ETO_OPAQUE, &rectLabel, &g_szDigits[i+1], 1, NULL);
rectLabel.top += m_nCellHeight; }
pDC->SelectObject(pOldFont); }
/////////////////////////////////////////////////////////////////////////////// // // PaintValues() // // Purpose: Draw values in cells // // Parameters: pDC - pointer to device context object // // Returns: None // void CXSudokuWnd::PaintValues(CDC * pDC) { CFont *pOldFont = pDC->SelectObject(&m_fontValues);
CSize sizeValue = pDC->GetTextExtent(_T("0")); int x_offset = m_nCellWidth/2 - sizeValue.cx/2 + 1; int y_offset = m_nCellHeight/2 - sizeValue.cy/2 + 1;
for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { CRect rectCell = GetCellRect(i, j);
CRect rectValue; rectValue.top = rectCell.top + y_offset; rectValue.bottom = rectValue.top + m_nValueHeight; rectValue.left = rectCell.left + x_offset; rectValue.right = rectValue.left + sizeValue.cx;
pDC->SetBkColor(GetCellBackgroundColor(i, j));
int nGiven = m_nGivens[i][j]; int nSolution = m_nSolution[i][j]; int nHint = m_nHints[i][j]; int nUserEntry = m_nUserEntries[i][j];
COLORREF rgbValue; int nValue;
if (nGiven) { rgbValue = m_rgbGivens; nValue = nGiven; } else if (nHint) { rgbValue = m_rgbSolution; nValue = nHint; } else if (nUserEntry) { rgbValue = m_rgbUserEntry; nValue = nUserEntry; } else if (nSolution && m_bShowSolution) { rgbValue = m_rgbSolution; nValue = nSolution; } else { // no value in this cell continue; }
pDC->SetTextColor(rgbValue);
pDC->ExtTextOut(rectValue.left, rectValue.top, ETO_CLIPPED|ETO_OPAQUE, &rectValue, &g_szDigits[nValue], 1, NULL); } }
pDC->SelectObject(pOldFont); }
/////////////////////////////////////////////////////////////////////////////// // // GetRowCandidates() // // Purpose: Get candidates for a row // // Parameters: row - row number (0 - 8) // // Returns: WORD - lower 8 bits represent the candidate values that are // possible for a cell (bit 0 is never set). A value is // possible if no other cell in the row contains the value // (a given, a user entered value, or a hint). // WORD CXSudokuWnd::GetRowCandidates(int row) { WORD wBitFlags = 0x3FE;
for (int col = 0; col < 9; col++) { int nGiven = m_nGivens[row][col]; int nHint = m_nHints[row][col]; int nUserEntry = m_nUserEntries[row][col]; if (nGiven) { ClearBit(wBitFlags, nGiven); } else if (nHint) { ClearBit(wBitFlags, nHint); } else if (nUserEntry) { ClearBit(wBitFlags, nUserEntry); } }
return wBitFlags; }
/////////////////////////////////////////////////////////////////////////////// // // GetColCandidates() // // Purpose: Get candidates for a column // // Parameters: col - column number (0 - 8) // // Returns: WORD - lower 8 bits represent the candidate values that are // possible for a cell (bit 0 is never set). A value is // possible if no other cell in the column contains the // value (a given, a user entered value, or a hint). // WORD CXSudokuWnd::GetColCandidates(int col) { WORD wBitFlags = 0x3FE;
for (int row = 0; row < 9; row++) { int nGiven = m_nGivens[row][col]; int nHint = m_nHints[row][col]; int nUserEntry = m_nUserEntries[row][col];
if (nGiven) { ClearBit(wBitFlags, nGiven); } else if (nHint) { ClearBit(wBitFlags, nHint); } else if (nUserEntry) { ClearBit(wBitFlags, nUserEntry); } }
return wBitFlags; }
/////////////////////////////////////////////////////////////////////////////// // // GetRegionCandidates() // // Purpose: Get candidates for a 3x3 region // // Parameters: row - row number (0 - 8) // col - column number (0 - 8) // // Returns: WORD - lower 8 bits represent the candidate values that are // possible for a cell (bit 0 is never set). A value is // possible if no other cell in the region contains the // value (a given, a user entered value, or a hint). // WORD CXSudokuWnd::GetRegionCandidates(int row, int col) { WORD wBitFlags = 0x3FE;
int nStartRow = (row / 3) * 3; int nStartCol = (col / 3) * 3;
for (int i = nStartRow; i < (nStartRow + 3); i++) { for (int j = nStartCol; j < (nStartCol + 3); j++) { int nGiven = m_nGivens[i][j]; int nHint = m_nHints[i][j]; int nUserEntry = m_nUserEntries[i][j];
if (nGiven) { ClearBit(wBitFlags, nGiven); } else if (nHint) { ClearBit(wBitFlags, nHint); } else if (nUserEntry) { ClearBit(wBitFlags, nUserEntry); } } }
return wBitFlags; }
/////////////////////////////////////////////////////////////////////////////// // // GetUnPenciledCandidates() // // Purpose: Get candidates that have been previously rubbed out // // Parameters: row - row number (0 - 8) // col - column number (0 - 8) // // Returns: WORD - Inverted bits 1 - 10 (bit 0 is never set) // representing the candidates that have previously // been rubbed out.
WORD CXSudokuWnd::GetUnPenciledCandidates(int row, int col) { return (~m_nUnPencil[row][col])&0x3FE; }
/////////////////////////////////////////////////////////////////////////////// // // GetCandidates() // // Purpose: Get candidates for a row, column, and 3x3 region // // Parameters: row - row number (0 - 8) // col - column number (0 - 8) // // Returns: WORD - lower 8 bits represent the candidate values that are // possible for a cell (bit 0 is never set). A value is // possible if no other cell in the row, column, or region // contains the value (a given, a user entered value, or // a hint). // WORD CXSudokuWnd::GetCandidates(int row, int col) { WORD wRowCandidates = GetRowCandidates(row); WORD wColCandidates = GetColCandidates(col); WORD wRegionCandidates = GetRegionCandidates(row, col);
WORD wBitFlags = (WORD) (wRowCandidates & wColCandidates & wRegionCandidates);// & wUnPenciledCandidates); TRACE(_T("Candidates for %d,%d: 0x%X\n"), row, col, wBitFlags);
return wBitFlags; }
/////////////////////////////////////////////////////////////////////////////// // // GetCellRect() // // Purpose: Get rect for a cell // // Parameters: row - row number (0 - 8) // col - column number (0 - 8) // // Returns: CRect - rect of the cell // CRect CXSudokuWnd::GetCellRect(int row, int col) { CRect rectCell, rectWnd; GetClientRect(&rectWnd);
rectCell.left = rectWnd.left + m_nXOffset + col * (m_nCellWidth + 1); rectCell.left += g_nGridLineOffset[col]; rectCell.right = rectCell.left + m_nCellWidth;
rectCell.top = rectWnd.top + m_nYOffset + row * (m_nCellHeight + 1); rectCell.top += g_nGridLineOffset[row]; rectCell.bottom = rectCell.top + m_nCellHeight;// - 1;
return rectCell; }
/////////////////////////////////////////////////////////////////////////////// // // PaintPencilMarks() // // Purpose: Draw pencil marks (aka candidate values) for a cell // // Parameters: pDC - pointer to device context object // // Returns: None // void CXSudokuWnd::PaintPencilMarks(CDC * pDC) { CFont *pOldFont = pDC->SelectObject(&m_fontPencilMarks);
pDC->SetTextColor(m_rgbPencilMarks);
int nXOffset = ((m_nCellWidth - (3 * m_nLabelHeight)) / 2) + 2;
for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { pDC->SetBkColor(GetCellBackgroundColor(i, j));
int nGiven = m_nGivens[i][j]; int nSolution = m_nSolution[i][j]; int nHint = m_nHints[i][j]; int nUserEntry = m_nUserEntries[i][j];
if (nGiven || nUserEntry || nHint || (nSolution && m_bShowSolution)) continue;
// find all possible candidates for this cell WORD wCandidates = GetCandidates(i, j) & GetUnPenciledCandidates(i, j);
CRect rectCell = GetCellRect(i, j);
CRect rectPencilMarks; rectPencilMarks.left = rectCell.left + nXOffset; rectPencilMarks.right = rectPencilMarks.left + m_nLabelHeight + 2;
for (int nCandidate = 1; nCandidate <= 9; nCandidate++) { rectPencilMarks.top = rectCell.top + 3 + ((nCandidate-1)/3) * (m_nLabelHeight+1); rectPencilMarks.bottom = rectPencilMarks.top + m_nLabelHeight + 2;
if (TestBit(wCandidates, nCandidate)) { pDC->ExtTextOut(rectPencilMarks.left, rectPencilMarks.top, ETO_CLIPPED|ETO_OPAQUE, &rectPencilMarks, &g_szDigits[nCandidate], 1, NULL); } if (nCandidate == 3 || nCandidate == 6) rectPencilMarks.left = rectCell.left + nXOffset; else rectPencilMarks.left += m_nLabelHeight;
rectPencilMarks.right = rectPencilMarks.left + m_nLabelHeight + 2; } } }
pDC->SelectObject(pOldFont); }
/////////////////////////////////////////////////////////////////////////////// // // PaintCurCell() // // Purpose: Paint cell at m_nCurRow, m_nCurCol // // Parameters: pDC - pointer to device context object // // Returns: None // void CXSudokuWnd::PaintCurCell(CDC * pDC) { if ((m_nCurRow != -1) && (m_nCurCol != -1)) { CRect rectCell = GetCellRect(m_nCurRow, m_nCurCol);
CPen pen(PS_SOLID, 1, m_rgbCurCellBorder);
CPen *pOldPen = pDC->SelectObject(&pen);
pDC->MoveTo(rectCell.left, rectCell.top); pDC->LineTo(rectCell.right, rectCell.top); pDC->MoveTo(rectCell.left, rectCell.top+1); pDC->LineTo(rectCell.right, rectCell.top+1); pDC->MoveTo(rectCell.left, rectCell.top+2); pDC->LineTo(rectCell.right, rectCell.top+2);
pDC->MoveTo(rectCell.left, rectCell.bottom-3); pDC->LineTo(rectCell.right, rectCell.bottom-3); pDC->MoveTo(rectCell.left, rectCell.bottom-2); pDC->LineTo(rectCell.right, rectCell.bottom-2); pDC->MoveTo(rectCell.left, rectCell.bottom-1); pDC->LineTo(rectCell.right, rectCell.bottom-1);
pDC->MoveTo(rectCell.left, rectCell.top); pDC->LineTo(rectCell.left, rectCell.bottom); pDC->MoveTo(rectCell.left+1, rectCell.top); pDC->LineTo(rectCell.left+1, rectCell.bottom); pDC->MoveTo(rectCell.left+2, rectCell.top); pDC->LineTo(rectCell.left+2, rectCell.bottom);
pDC->MoveTo(rectCell.right-1, rectCell.top); pDC->LineTo(rectCell.right-1, rectCell.bottom); pDC->MoveTo(rectCell.right-2, rectCell.top); pDC->LineTo(rectCell.right-2, rectCell.bottom); pDC->MoveTo(rectCell.right-3, rectCell.top); pDC->LineTo(rectCell.right-3, rectCell.bottom);
pDC->SelectObject(pOldPen); } }
/////////////////////////////////////////////////////////////////////////////// // // CellFromPoint() // // Purpose: Get cell row, column for a point // // Parameters: point - point that lies within a cell // row - returned row number (0 - 8) // col - returned column number (0 - 8) // // Returns: BOOL - TRUE = point lies within a cell // BOOL CXSudokuWnd::CellFromPoint(CPoint point, int& nRow, int& nCol) { nRow = nCol = -1;
BOOL bCellFound = FALSE;
CRect rectCell;
for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { rectCell = GetCellRect(i, j); if (rectCell.PtInRect(point)) { nRow = i; nCol = j; bCellFound = TRUE; break; } } if (bCellFound) break; }
return bCellFound; }
/////////////////////////////////////////////////////////////////////////////// // // OnLButtonDown() // // Purpose: Handle WM_LBUTTONDOWN message, to enable drawing the // selection border // // Parameters: nFlags - indicates whether various virtual keys are down // point - specifies the x- and y-coordinate of the cursor // // Returns: None // void CXSudokuWnd::OnLButtonDown(UINT nFlags, CPoint point) { TRACE(_T("in CXSudokuWnd::OnLButtonDown\n"));
// change current cell
int nOldRow = m_nCurRow; int nOldCol = m_nCurCol;
if (CellFromPoint(point, m_nCurRow, m_nCurCol)) { if ((nOldRow != -1) && (nOldCol != -1)) { CRect rectOld = GetCellRect(nOldRow, nOldCol); InvalidateRect(&rectOld, FALSE); } CRect rectNew = GetCellRect(m_nCurRow, m_nCurCol); InvalidateRect(&rectNew, FALSE); SetFocus(); }
CWnd::OnLButtonDown(nFlags, point); }
/////////////////////////////////////////////////////////////////////////////// // // OnLButtonDblClk() // // Purpose: Handle WM_LBUTTONDBLCLK message, to display right-click menu // // Parameters: nFlags - indicates whether various virtual keys are down // point - specifies the x- and y-coordinate of the cursor // // Returns: None // void CXSudokuWnd::OnLButtonDblClk(UINT nFlags, CPoint point) { TRACE(_T("in CXSudokuWnd::OnLButtonDblClk\n"));
ShowPopup(point);
CWnd::OnLButtonDblClk(nFlags, point); }
/////////////////////////////////////////////////////////////////////////////// // // OnRButtonDown() // // Purpose: Handle WM_RBUTTONDOWN message, to enable drawing the // selection border // // Parameters: nFlags - indicates whether various virtual keys are down // point - specifies the x- and y-coordinate of the cursor // // Returns: None // void CXSudokuWnd::OnRButtonDown(UINT nFlags, CPoint point) { // change current cell
int nOldRow = m_nCurRow; int nOldCol = m_nCurCol;
if (CellFromPoint(point, m_nCurRow, m_nCurCol)) { if ((nOldRow != -1) && (nOldCol != -1)) { CRect rectOld = GetCellRect(nOldRow, nOldCol); InvalidateRect(&rectOld, FALSE); } CRect rectNew = GetCellRect(m_nCurRow, m_nCurCol); InvalidateRect(&rectNew, FALSE); SetFocus(); }
CWnd::OnRButtonDown(nFlags, point); }
/////////////////////////////////////////////////////////////////////////////// // // OnRButtonUp() // // Purpose: Handle WM_RBUTTONUP message, to display right-click menu // // Parameters: nFlags - indicates whether various virtual keys are down // point - specifies the x- and y-coordinate of the cursor // // Returns: None // void CXSudokuWnd::OnRButtonUp(UINT nFlags, CPoint point) { ShowPopup(point);
CWnd::OnRButtonUp(nFlags, point); }
/////////////////////////////////////////////////////////////////////////////// // // PreTranslateMessage() // // Purpose: translate window messages before they are dispatched; used to // catch keyboard input // // Parameters: pMsg - points to a MSG structure that contains the message // to process // // Returns: BOOL - TRUE = message was translated and should not be dispatched // BOOL CXSudokuWnd::PreTranslateMessage(MSG* pMsg) { switch (pMsg->message) { case WM_KEYUP: //TRACE(_T("WM_KEYUP\n")); if ((pMsg->wParam >= _T('0')) && (pMsg->wParam <= _T('9'))) { if ((m_nCurRow != -1) && (m_nCurCol != -1) && (m_nGivens[m_nCurRow][m_nCurCol] == 0)) { // find all possible candidates for this cell WORD wCandidates = GetCandidates(m_nCurRow, m_nCurCol) & GetUnPenciledCandidates(m_nCurRow, m_nCurCol); int nDigit = pMsg->wParam - _T('0'); if (nDigit && TestBit(wCandidates, nDigit)) { SWITCH_RESOURCE; // load string from dll resource
AddUndoAction(m_nCurRow, m_nCurCol, ACTION_CODE_USER_ENTRY, nDigit); m_nUserEntries[m_nCurRow][m_nCurCol] = nDigit; CString str = _T(""); CString strColumns = _T(""); VERIFY(strColumns.LoadString(IDS_COLUMN_LABELS)); str.Format(IDS_USER_ENTRY, nDigit, m_nCurRow+1, strColumns[m_nCurCol]); int nType = 0; CString strCorrect = _T(""); if (nDigit == m_nSolution[m_nCurRow][m_nCurCol]) { VERIFY(strCorrect.LoadString(IDS_CORRECT)); } else { VERIFY(strCorrect.LoadString(IDS_INCORRECT)); nType = 1; } str += strCorrect;
if (m_hWndMessage && ::IsWindow(m_hWndMessage)) ::SendMessage(m_hWndMessage, WM_XSUDOKU, nType, (LPARAM)(LPCTSTR)str); } else if (nDigit == 0) { AddUndoAction(m_nCurRow, m_nCurCol, ACTION_CODE_USER_ENTRY, nDigit); m_nUserEntries[m_nCurRow][m_nCurCol] = nDigit; }
// NOTE: must set hint to 0 or user entry will // not be displayed m_nHints[m_nCurRow][m_nCurCol] = 0; Invalidate(FALSE); } return TRUE; } else if (pMsg->wParam == VK_RETURN) { OnRightClick(); } break;
case WM_KEYDOWN: //TRACE(_T("WM_KEYDOWN\n")); int nOldRow = m_nCurRow; int nOldCol = m_nCurCol; BOOL bUpdate = FALSE; switch (pMsg->wParam) { case VK_DOWN: TRACE(_T("VK_DOWN\n"));
if (m_nCurRow < 8) { m_nCurRow++; bUpdate = TRUE; } break;
case VK_UP: TRACE(_T("VK_UP\n"));
if (m_nCurRow > 0) { m_nCurRow--; bUpdate = TRUE; } break;
case VK_RIGHT: TRACE(_T("VK_RIGHT\n"));
if (m_nCurCol < 8) { m_nCurCol++; bUpdate = TRUE; } break;
case VK_LEFT: TRACE(_T("VK_LEFT\n"));
if (m_nCurCol > 0) { m_nCurCol--; bUpdate = TRUE; } break;
case VK_HOME: TRACE(_T("VK_HOME\n"));
if (m_nCurCol > 0) { m_nCurCol = 0; bUpdate = TRUE; } break;
case VK_END: TRACE(_T("VK_END\n"));
if (m_nCurCol < 8) { m_nCurCol = 8; bUpdate = TRUE; } break;
case VK_PRIOR: TRACE(_T("VK_PRIOR\n"));
if (m_nCurRow > 0) { m_nCurRow = 0; bUpdate = TRUE; } break;
case VK_NEXT: TRACE(_T("VK_NEXT\n"));
if (m_nCurRow < 8) { m_nCurRow = 8; bUpdate = TRUE; } break;
}
if (bUpdate) { if ((nOldRow != -1) && (nOldCol != -1)) { CRect rectOld = GetCellRect(nOldRow, nOldCol); InvalidateRect(&rectOld, FALSE); } CRect rectNew = GetCellRect(m_nCurRow, m_nCurCol); InvalidateRect(&rectNew, FALSE); SetFocus(); } break; }
if (m_hAccel && ::TranslateAccelerator(m_hWnd, m_hAccel, pMsg)) return TRUE;
return CWnd::PreTranslateMessage(pMsg); }
/////////////////////////////////////////////////////////////////////////////// // // Create() // // Purpose: Create XSudokuWnd window // // Parameters: lpszClassName - points to a null-terminated character string // that names the Windows class // lpszWindowName - points to a null-terminated character string // that contains the window name // dwStyle - specifies the window style attributes // rect - the size and position of the window // pParentWnd - the parent window // nID - the ID of the child window // pContext - the create context of the window // // Returns: BOOL - TRUE = window created successfully // BOOL CXSudokuWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) { TRACE(_T("in CXSudokuWnd::Create\n")); BOOL bRet = CWnd::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext);
if (bRet) { SWITCH_RESOURCE; // load dialog from dll resource
m_hAccel = ::LoadAccelerators(ExtensionDLL.hModule, MAKEINTRESOURCE(IDR_ACCELERATOR)); ASSERT(m_hAccel);
// create fonts for XSudokuWnd; all these must be deleted in dtor
LOGFONT lf; memset(&lf, 0, sizeof(lf)); CFont *pFont = GetFont(); if (pFont == NULL) pFont = GetParent()->GetFont(); pFont->GetLogFont(&lf); _tcscpy(lf.lfFaceName, m_strLabelFont); lf.lfWeight = FW_BOLD;
m_nLabelHeight = abs(lf.lfHeight);
if (m_fontLabels.GetSafeHandle()) m_fontLabels.DeleteObject();
m_fontLabels.CreateFontIndirect(&lf);
lf.lfWeight = FW_NORMAL;
if (m_fontPencilMarks.GetSafeHandle()) m_fontPencilMarks.DeleteObject();
m_fontPencilMarks.CreateFontIndirect(&lf);
lf.lfWeight = FW_BOLD; _tcscpy(lf.lfFaceName, _T("Arial")); lf.lfHeight = GetFontHeight(30); m_nValueHeight = abs(lf.lfHeight);
if (m_fontValues.GetSafeHandle()) m_fontValues.DeleteObject();
m_fontValues.CreateFontIndirect(&lf);
SetFocus(); }
return bRet; }
/////////////////////////////////////////////////////////////////////////////// // // GetSudoku() // // Purpose: Display Sudoku entry dialog // // Parameters: lpszPuzzle - optional string containing Sudoku puzzle // // Returns: CString - Sudoku puzzle // CString CXSudokuWnd::GetSudoku(LPCTSTR lpszPuzzle /*= NULL*/) { SWITCH_RESOURCE; // load dialog from dll resource
CString strSudoku = _T("");
CSudokuEntryDlg dlg;
if (lpszPuzzle) dlg.m_strSudoku = lpszPuzzle;
if (dlg.DoModal() == IDOK) { strSudoku = dlg.m_strSudoku; }
return strSudoku; }
/////////////////////////////////////////////////////////////////////////////// // // Reset() // // Purpose: Reset to initial state // // Parameters: bResetAll - TRUE = reset to "no puzzle loaded" state // FALSE = reset to "no values entered" state // // Returns: None // // Notes: The undo list will be reset; all undo actions will be lost. // void CXSudokuWnd::Reset(BOOL bResetAll) { m_nCurRow = m_nCurCol = -1; m_pointPopup = CPoint(-1,-1); m_bShowSolution = FALSE; if (bResetAll) m_bIsValid = FALSE;
for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { m_nHints[i][j] = 0; m_nUserEntries[i][j] = 0; if (bResetAll) { m_nGivens[i][j] = 0; m_nUnPencil[i][j] = 0; m_nSolution[i][j] = 0; } } }
// reset undo facility m_nUndoIndex = 0; m_nUndoLastEntry = 0;
for (int k = 0; k < m_nUndoSize; k++) { UNDO_BLOCK * pUB = (UNDO_BLOCK *) m_Undo[k]; ASSERT(pUB); if (pUB) { pUB->action_code = 0; pUB->action_value = 0; } } }
/////////////////////////////////////////////////////////////////////////////// // // LoadFromFile() // // Purpose: Load Sudoku from text file specified by lpszFile // // Parameters: lpszFile - name of file containing Sudoku puzzle // // Returns: BOOL - TRUE = Sudoku loaded ok // BOOL CXSudokuWnd::LoadFromFile(LPCTSTR lpszFile) { BOOL ok = FALSE;
ASSERT(lpszFile && (lpszFile[0] != _T('\0'))); if (lpszFile && (lpszFile[0] != _T('\0'))) { CFile file; if (file.Open(lpszFile, CFile::shareDenyNone | CFile::modeRead)) { m_strFile = lpszFile;
DWORD dwLen = file.GetLength(); // size in bytes
// should be less than 2000 chars if (dwLen > 2000) dwLen = 2000;
if (dwLen) { TCHAR * buf = new TCHAR [dwLen+10]; memset(buf, 0, (dwLen+10) * sizeof(TCHAR));
file.Read(buf, dwLen);
ok = LoadFromString(buf, dwLen/sizeof(TCHAR));
delete [] buf; }
file.Close(); } }
return ok; }
/////////////////////////////////////////////////////////////////////////////// // // LoadFromClipboard() // // Purpose: Load Sudoku from clipboard // // Parameters: None // // Returns: BOOL - TRUE = Sudoku loaded ok // BOOL CXSudokuWnd::LoadFromClipboard() { BOOL ok = FALSE;
m_strFile = _T("");
CClipboard clip; DWORD dwLen = clip.GetTextLength(); // size in bytes TRACE(_T("dwLen=%d\n"), dwLen);
// should be less than 2000 chars if (dwLen > 2000) dwLen = 2000;
if (dwLen) { TCHAR * buf = new TCHAR [dwLen+10]; memset(buf, 0, (dwLen+10) * sizeof(TCHAR));
clip.GetText(buf, dwLen+2);
ok = LoadFromString(buf, dwLen/sizeof(TCHAR));
delete [] buf; }
return ok; }
/////////////////////////////////////////////////////////////////////////////// // // LoadFromString() // // Purpose: Load Sudoku from string // // Parameters: lpszString - string buffer containing Sudoku puzzle // dwLen - length of string // // Returns: BOOL - TRUE = Sudoku loaded ok // // Notes: All the other LoadXXX() functions call this one eventually. // BOOL CXSudokuWnd::LoadFromString(LPCTSTR lpszString, DWORD dwLen) { BOOL ok = FALSE;
Reset(TRUE);
TRACE(_T("dwLen=%d\n"), dwLen); int nCount = 0; // count of values read
ASSERT(dwLen); ASSERT(lpszString && (lpszString[0] != _T('\0')));
if ((dwLen >= 81) && lpszString && (lpszString[0] != _T('\0'))) { TRACE(_T("lpszString=<%s>\n"), lpszString);
int row = 0; int col = 0;
for (DWORD dwIndex = 0; dwIndex < dwLen; dwIndex++) { TCHAR c = lpszString[dwIndex];
int nValue = 0;
if (c == _T('\0')) // check for eol break;
if (c == _T('.')) // . = 0 { nValue = 0; } else if (_istdigit(c)) { nValue = c - _T('0'); } else { continue; }
m_nGivens[row][col++] = nValue; //TRACE(_T("%d: nValue=%d\n"), nCount, nValue); nCount++; if (nCount == 81) break; if (col >= 9) { row++; col = 0; } } // for }
TRACE(_T("nCount=%d\n"), nCount);
if (nCount == 81) { m_bHaveGivens = TRUE;
memcpy(m_nSolution, m_nGivens, sizeof(m_nSolution));
DLX dlx; int n = dlx.sudoku_solve(m_nSolution);
SWITCH_RESOURCE; // load string from dll resource
int nType = 0; CString str = _T(""); if (n == 0) { nType = 1; VERIFY(str.LoadString(IDS_SUDOKU_UNSOLVABLE)); } else if (n == 1) { VERIFY(str.LoadString(IDS_SUDOKU_OK)); m_bIsValid = TRUE; } else { nType = 1; VERIFY(str.LoadString(IDS_SUDOKU_INVALID)); } if (m_hWndMessage && ::IsWindow(m_hWndMessage)) ::SendMessage(m_hWndMessage, WM_XSUDOKU, nType, (LPARAM)(LPCTSTR)str);
ok = TRUE; }
return ok; }
/////////////////////////////////////////////////////////////////////////////// // // ShowColorPrefsDlg() // // Purpose: Display color prefs dialog // // Parameters: None // // Returns: BOOL - TRUE = user ended color prefs dialog with OK button // BOOL CXSudokuWnd::ShowColorPrefsDlg() { SWITCH_RESOURCE; // load dialog from dll resource
BOOL ok = FALSE;
CColorPrefDlg dlg;
dlg.m_rgbWindowBackground = m_rgbWindowBackground; dlg.m_rgbCellBackground = m_rgbCellBackground; dlg.m_rgbLabels = m_rgbLabels; dlg.m_rgbGivens = m_rgbGivens; dlg.m_rgbSolution = m_rgbSolution; dlg.m_rgbUserEntry = m_rgbUserEntry; dlg.m_rgbPencilMarks = m_rgbPencilMarks; dlg.m_rgbHighlightNumber = m_rgbHighlightNumber; dlg.m_rgbCurCellBorder = m_rgbCurCellBorder; dlg.m_rgb3x3Gridline = m_rgb3x3Gridline;
if (dlg.DoModal() == IDOK) { m_rgbWindowBackground = dlg.m_rgbWindowBackground; m_rgbCellBackground = dlg.m_rgbCellBackground; m_rgbLabels = dlg.m_rgbLabels; m_rgbGivens = dlg.m_rgbGivens; m_rgbSolution = dlg.m_rgbSolution; m_rgbUserEntry = dlg.m_rgbUserEntry; m_rgbPencilMarks = dlg.m_rgbPencilMarks; m_rgbHighlightNumber = dlg.m_rgbHighlightNumber; m_rgbCurCellBorder = dlg.m_rgbCurCellBorder; m_rgb3x3Gridline = dlg.m_rgb3x3Gridline;
Invalidate(FALSE);
ok = TRUE; }
return ok; }
/////////////////////////////////////////////////////////////////////////////// // // CaptureBitmap() // // Purpose: Copy bitmap of XSudokuWnd window to clipboard // // Parameters: None // // Returns: BOOL - TRUE = bitmap copied to clipboard ok // BOOL CXSudokuWnd::CaptureBitmap() { CDC dc; HDC hdc = ::GetDC(m_hWnd); dc.Attach(hdc);
CDC memDC; memDC.CreateCompatibleDC(&dc);
CBitmap bm; CRect r; GetClientRect(&r);
CSize sz(r.Width(), r.Height()); bm.CreateCompatibleBitmap(&dc, sz.cx, sz.cy); CBitmap * oldbm = memDC.SelectObject(&bm); memDC.BitBlt(0, 0, sz.cx, sz.cy, &dc, 0, 0, SRCCOPY);
BOOL bRet = FALSE;
if (::OpenClipboard(m_hWnd)) { ::EmptyClipboard(); ::SetClipboardData(CF_BITMAP, bm.m_hObject); ::CloseClipboard(); bRet = TRUE; }
memDC.SelectObject(oldbm); bm.Detach(); // make sure bitmap not deleted with CBitmap object ::ReleaseDC(m_hWnd, hdc);
return bRet; }
/////////////////////////////////////////////////////////////////////////////// // // PrintBitmap() // // Purpose: Print bitmap of XSudokuWnd window // // Parameters: None // // Returns: BOOL - TRUE = bitmap printed ok // BOOL CXSudokuWnd::PrintBitmap() { CPrintDialog printDlg(FALSE, PD_ALLPAGES | PD_NOPAGENUMS | PD_NOSELECTION | PD_HIDEPRINTTOFILE);
if (printDlg.DoModal() == IDCANCEL) return FALSE;
Invalidate(FALSE);
CDC dcPrint;
if (!dcPrint.Attach(printDlg.GetPrinterDC())) { AfxMessageBox(_T("No printer found!")); return FALSE; }
dcPrint.m_bPrinting = TRUE;
// Initialise print document details DOCINFO di; ::ZeroMemory (&di, sizeof (DOCINFO)); di.cbSize = sizeof (DOCINFO); di.lpszDocName = _T("XSudoku");
BOOL bPrintingOK = dcPrint.StartDoc(&di); // Begin a new print job
CRect rectClient; GetWindowRect(rectClient); ScreenToClient(&rectClient); CClientDC dcClient(this);
dcPrint.SetMapMode(MM_ANISOTROPIC);
CSize sz(dcClient.GetDeviceCaps(LOGPIXELSX), dcClient.GetDeviceCaps(LOGPIXELSY)); dcPrint.SetWindowExt(sz); int nMargin = sz.cx; sz = CSize(dcPrint.GetDeviceCaps(LOGPIXELSX), dcPrint.GetDeviceCaps(LOGPIXELSY)); dcPrint.SetViewportExt(sz);
// Get the printing extents and store in the m_rectDraw field of a // CPrintInfo object CPrintInfo Info; Info.SetMaxPage(1); // just one page int maxw = dcPrint.GetDeviceCaps(HORZRES); int maxh = dcPrint.GetDeviceCaps(VERTRES); Info.m_rectDraw.SetRect(0, 0, maxw, maxh); UINT page = 1;//Info.GetMinPage();
dcPrint.StartPage(); // begin new page Info.m_nCurPage = page;
GHandle dib; dib.Attach(GDIUtil::GrabDIB(&dcClient, rectClient)); if (dib.GetHandle() == NULL) return FALSE;
GLock lock(dib); BITMAPINFOHEADER *pBMI = (BITMAPINFOHEADER*)(LPVOID) lock;
CRect rectPrint = Info.m_rectDraw; rectPrint.left += nMargin; rectPrint.right -= nMargin; rectPrint.top += nMargin;
CFont *pOldFont = NULL;
if (m_fontPrint.GetSafeHandle() == NULL) { // create font for printing
LOGFONT lf; memset(&lf, 0, sizeof(lf)); _tcscpy(lf.lfFaceName, _T("Times New Roman")); lf.lfCharSet = DEFAULT_CHARSET; lf.lfPitchAndFamily = FF_ROMAN; lf.lfWeight = FW_NORMAL; lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfHeight = GetFontHeight(14); m_fontPrint.CreateFontIndirect(&lf); pOldFont = dcPrint.SelectObject(&m_fontPrint); }
int h = dcPrint.DrawText(m_strFile, &rectPrint, DT_LEFT); CString strTime = _T(""); CTime t = CTime::GetCurrentTime(); strTime = t.Format(_T("Printed on %A, %B %#d, %Y")); rectPrint.top += h; h = dcPrint.DrawText(strTime, &rectPrint, DT_LEFT); rectPrint.top += h;
int nColors = 0; if (pBMI->biBitCount <= 8) nColors = (1<< pBMI->biBitCount);
::StretchDIBits(dcPrint.GetSafeHdc(), rectPrint.left, rectPrint.top + nMargin, pBMI->biWidth, pBMI->biHeight, 0, 0, pBMI->biWidth, pBMI->biHeight, (LPBYTE)pBMI + (pBMI->biSize + nColors * sizeof(RGBQUAD)), (BITMAPINFO*)pBMI, DIB_RGB_COLORS, SRCCOPY);
dcPrint.SelectObject(pOldFont);
bPrintingOK = (dcPrint.EndPage() > 0); // end page
if (bPrintingOK) dcPrint.EndDoc(); // end a print job else dcPrint.AbortDoc(); // abort job.
return TRUE; }
/////////////////////////////////////////////////////////////////////////////// // // ActionCodeToString() // // Purpose: Convert undo action code to string // // Parameters: action_code - undo action code, see definition of UNDO_BLOCK // // Returns: CString - action code string; empty if action code = 0 // CString CXSudokuWnd::ActionCodeToString(BYTE action_code) { CString str = _T("");
#ifdef _DEBUG switch (action_code) { case 0: // unused entry break; case ACTION_CODE_USER_ENTRY: str = _T("ACTION_CODE_USER_ENTRY"); break; case ACTION_CODE_USER_ENTRY_REMOVE_ALL: str = _T("ACTION_CODE_USER_ENTRY_REMOVE_ALL"); break; case ACTION_CODE_HINT: str = _T("ACTION_CODE_HINT"); break; case ACTION_CODE_HINT_REMOVE_ALL: str = _T("ACTION_CODE_HINT_REMOVE_ALL"); break; default: str = _T("ERROR - unknown action code"); break; } #else UNUSED_ALWAYS(action_code); #endif
return str; }
/////////////////////////////////////////////////////////////////////////////// // // AddUndoAction() // // Purpose: Add action to undo list // // Parameters: row - current row number for action // col - current column number for action // action_code - what action // action_value - value associated with action // // Returns: BOOL - TRUE = action successfully added // BOOL CXSudokuWnd::AddUndoAction(int row, int col, int action_code, int action_value) { BOOL ok = FALSE;
if (!m_bEnableUndo) return ok;
ASSERT((action_code >= 1) && (action_code <= 4));
UNDO_BLOCK * pUB = (UNDO_BLOCK *) m_Undo[m_nUndoIndex]; ASSERT(pUB);
if (pUB) { pUB->action_code = (BYTE)action_code; pUB->action_value = (BYTE)action_value; pUB->row = (BYTE)row; pUB->col = (BYTE)col; int i, j; for (i = 0; i < 9; i++) { for (j = 0; j < 9; j++) { pUB->hints[i][j] = (BYTE)m_nHints[i][j]; pUB->user_entries[i][j] = (BYTE)m_nUserEntries[i][j]; } }
// advance undo pointers m_nUndoIndex++;
// there may be other entries in the undo list after this one - // the user may have undone some entries, and then entered new data. // m_nUndoLastEntry always points to the last user action m_nUndoLastEntry = m_nUndoIndex;
if (m_nUndoIndex >= m_nUndoSize) { // need to grow undo array int nOldUndoSize = m_nUndoSize; m_nUndoSize += UNDO_ARRAY_GROW_BY_SIZE; BOOL bSetSizeFailed = FALSE; TRY { m_Undo.SetSize(m_nUndoSize); } CATCH (CMemoryException, e) { bSetSizeFailed = TRUE; } END_CATCH
if (bSetSizeFailed) { m_nUndoSize = nOldUndoSize; m_bEnableUndo = FALSE; TRACE(_T("ERROR - SetSize failed\n")); ASSERT(FALSE); } else { for (j = nOldUndoSize; j < m_nUndoSize; j++) { UNDO_BLOCK * pUB = new UNDO_BLOCK; ASSERT(pUB); m_Undo[j] = pUB; } ok = TRUE; } } else { ok = TRUE; } }
return ok; }
/////////////////////////////////////////////////////////////////////////////// // // ShowPopup() // // Purpose: Display right click (context) menu // // Parameters: point - location for popup menu // // Returns: None // void CXSudokuWnd::ShowPopup(CPoint point) { m_pointPopup = point;
int row, col, i; CString str = _T("");
if (m_bHaveGivens && CellFromPoint(point, row, col)) { CMenu popup; VERIFY(popup.CreatePopupMenu());
UINT nFlags = MF_STRING;
if (m_bEnableUndo) { if (m_nUndoIndex == 0) nFlags |= MF_DISABLED | MF_GRAYED; VERIFY(popup.AppendMenu(nFlags, ID_POPUP_UNDO, _T("Undo\tCtrl+Z")));
nFlags = MF_STRING;
if (m_nUndoIndex >= m_nUndoLastEntry) nFlags |= MF_DISABLED | MF_GRAYED; VERIFY(popup.AppendMenu(nFlags, ID_POPUP_REDO, _T("Redo\tCtrl+Y")));
VERIFY(popup.AppendMenu(MF_SEPARATOR)); }
if ((m_nGivens[row][col] == 0) && (m_nHints[row][col] == 0) && (m_nUserEntries[row][col] == 0) && !m_bShowSolution) { // find all possible candidates for this cell WORD wCandidates = GetCandidates(row, col);
if (wCandidates) { for (i = 0; i < 9; i++) { if (TestBit(wCandidates, i+1)) { str.Format(_T("Set to %d\t%d"), i+1, i+1);
VERIFY(popup.AppendMenu(MF_STRING, ID_POPUP_SET_1+i, str)); } } VERIFY(popup.AppendMenu(MF_SEPARATOR)); }
WORD wUnPencilCandidates = GetUnPenciledCandidates(row, col) & wCandidates; //get the possible values that can be 'un-penciled' WORD wPencilCandidates = ~GetUnPenciledCandidates(row, col) & 0x3FE; //get the possible values that can be 're-penciled' in
for (i = 0; i < 9; i++) { if (TestBit(wUnPencilCandidates, i+1)) { str.Format(_T("Rub Out %d"), i+1);
VERIFY(popup.AppendMenu(MF_STRING, ID_POPUP_UNPENCIL_1+i, str)); } else { if ((TestBit(wPencilCandidates, i+1)) & (TestBit(wCandidates, i+1))) { str.Format(_T("Pencil In %d"), i+1);
VERIFY(popup.AppendMenu(MF_STRING, ID_POPUP_PENCIL_1+i, str)); } } } VERIFY(popup.AppendMenu(MF_SEPARATOR));
}
nFlags = MF_STRING; if (m_nGivens[row][col]) nFlags |= MF_DISABLED | MF_GRAYED; if (m_nHints[row][col]) nFlags |= MF_CHECKED; else nFlags |= MF_UNCHECKED;
VERIFY(popup.AppendMenu(nFlags, ID_POPUP_SHOW_HINT, _T("Show Hint\tF2"))); VERIFY(popup.AppendMenu(MF_STRING, ID_POPUP_REMOVE_ALL_HINTS, _T("Remove All Hints"))); VERIFY(popup.AppendMenu(MF_SEPARATOR));
nFlags = MF_STRING; if (m_nUserEntries[row][col] == 0) nFlags |= MF_DISABLED | MF_GRAYED; VERIFY(popup.AppendMenu(nFlags, ID_POPUP_REMOVE_USER_ENTRY, _T("Remove User Entry\t0"))); VERIFY(popup.AppendMenu(MF_STRING, ID_POPUP_REMOVE_ALL_USER_ENTRIES, _T("Remove All User Entries"))); VERIFY(popup.AppendMenu(MF_STRING, ID_POPUP_CHECK_USER_ENTRIES, _T("Check User Entries"))); VERIFY(popup.AppendMenu(MF_SEPARATOR));
nFlags = MF_STRING; if (m_bShowSolution) nFlags |= MF_CHECKED; else nFlags |= MF_UNCHECKED; VERIFY(popup.AppendMenu(nFlags, ID_POPUP_SHOW_SOLUTION, _T("Show Solution\tF3")));
nFlags = MF_STRING; if (m_bPencilMarks) nFlags |= MF_CHECKED; else nFlags |= MF_UNCHECKED; VERIFY(popup.AppendMenu(nFlags, ID_POPUP_SHOW_PENCIL_MARKS, _T("Show Pencil Marks\tF4")));
VERIFY(popup.AppendMenu(MF_STRING, ID_POPUP_RESET, _T("Reset"))); VERIFY(popup.AppendMenu(MF_SEPARATOR));
VERIFY(popup.AppendMenu(MF_STRING, ID_POPUP_PRINT, _T("Print Window...\tCtrl+P"))); VERIFY(popup.AppendMenu(MF_STRING, ID_POPUP_COPY, _T("Copy Window to Clipboard"))); VERIFY(popup.AppendMenu(MF_SEPARATOR));
for (i = 0; i < 9; i++) { nFlags = MF_STRING; if (m_nHighlightNumber == (i+1)) nFlags |= MF_CHECKED; else nFlags |= MF_UNCHECKED;
str.Format(_T("Highlight %d\tCtrl+%d"), i+1, i+1);
VERIFY(popup.AppendMenu(nFlags, ID_POPUP_HIGHLIGHT_1+i, str)); }
VERIFY(popup.AppendMenu(MF_SEPARATOR)); VERIFY(popup.AppendMenu(MF_STRING, ID_APP_ABOUT, _T("About XSudokuWndDll...\tCtrl+F1")));
// display the menu
CPoint pt = point; ClientToScreen(&pt); VERIFY(popup.TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x+1, pt.y+1, this)); } }
/////////////////////////////////////////////////////////////////////////////// // // popup memu handlers // ///////////////////////////////////////////////////////////////////////////////
void CXSudokuWnd::OnRemoveUserEntry() { int row, col;
if (CellFromPoint(m_pointPopup, row, col)) { AddUndoAction(row, col, ACTION_CODE_USER_ENTRY, 0); m_nUserEntries[row][col] = 0; Invalidate(FALSE); } }
void CXSudokuWnd::OnRemoveAllUserEntries() { AddUndoAction(m_nCurRow, m_nCurCol, ACTION_CODE_USER_ENTRY_REMOVE_ALL, 0);
for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { m_nUserEntries[i][j] = 0; } } Invalidate(FALSE); }
void CXSudokuWnd::OnCheckUserEntries() { SWITCH_RESOURCE; // load string from dll resource
CString str = _T(""); CString strColumns = _T(""); VERIFY(strColumns.LoadString(IDS_COLUMN_LABELS));
CString strFormat = _T(""); VERIFY(strFormat.LoadString(IDS_USER_ENTRY)); CString strIncorrect = _T(""); VERIFY(strIncorrect.LoadString(IDS_INCORRECT)); strFormat += strIncorrect; strFormat += _T(".\r\n");
CString strMessage = _T(""); int nUserEntries = 0;
for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { if (m_nUserEntries[i][j] != 0) { nUserEntries++; if (m_nUserEntries[i][j] != m_nSolution[i][j]) { strMessage.Format(strFormat, m_nUserEntries[i][j], i+1, strColumns[j]); str += strMessage; } } } }
if (str.IsEmpty()) { if (nUserEntries == 0) AfxMessageBox(IDS_NO_USER_ENTRY); else AfxMessageBox(IDS_USER_ENTRY_OK, MB_ICONINFORMATION); } else { AfxMessageBox(str); } }
// called by accelerator void CXSudokuWnd::OnShowHint() { if ((m_nCurRow != -1) && (m_nCurCol != -1)) { if (m_bHaveGivens) { int nHint = (m_nHints[m_nCurRow][m_nCurCol] == 0) ? m_nSolution[m_nCurRow][m_nCurCol] : 0;
AddUndoAction(m_nCurRow, m_nCurCol, ACTION_CODE_HINT, nHint);
m_nHints[m_nCurRow][m_nCurCol] = nHint;
Invalidate(FALSE); } } }
// called by popup menu void CXSudokuWnd::OnPopupShowHint() { int row, col;
if (m_bHaveGivens && CellFromPoint(m_pointPopup, row, col)) { int nHint = (m_nHints[row][col] == 0) ? m_nSolution[row][col] : 0;
AddUndoAction(row, col, ACTION_CODE_HINT, nHint);
m_nHints[row][col] = nHint;
Invalidate(FALSE); } }
void CXSudokuWnd::OnRemoveAllHints() { AddUndoAction(m_nCurRow, m_nCurCol, ACTION_CODE_HINT_REMOVE_ALL, 0);
for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { m_nHints[i][j] = 0; } } Invalidate(FALSE); }
void CXSudokuWnd::OnReset() { SWITCH_RESOURCE; // load string from dll resource
int rc = AfxMessageBox(IDS_RESET, MB_YESNO|MB_ICONQUESTION);
if (rc == IDYES) { Reset(FALSE); Invalidate(FALSE); } }
void CXSudokuWnd::OnPrintWindow() { PrintBitmap(); }
void CXSudokuWnd::OnCopyWindow() { CaptureBitmap(); }
void CXSudokuWnd::OnShowSolution() { m_bShowSolution = !m_bShowSolution; SetShowSolution(m_bShowSolution); }
void CXSudokuWnd::OnShowPencilMarks() { m_bPencilMarks = !m_bPencilMarks; SetShowPencilMarks(m_bPencilMarks); }
void CXSudokuWnd::OnPopupSet(UINT nID) { SWITCH_RESOURCE; // load string from dll resource
int nValue = nID - ID_POPUP_SET_1 + 1;
int row, col;
if (CellFromPoint(m_pointPopup, row, col)) { AddUndoAction(row, col, ACTION_CODE_USER_ENTRY, nValue);
m_nUserEntries[row][col] = nValue;
CString str = _T(""); CString strColumns = _T(""); VERIFY(strColumns.LoadString(IDS_COLUMN_LABELS)); str.Format(IDS_USER_ENTRY, nValue, row+1, strColumns[col]); int nType = 0;
CString strCorrect = _T(""); if (nValue == m_nSolution[row][col]) { VERIFY(strCorrect.LoadString(IDS_CORRECT)); } else { VERIFY(strCorrect.LoadString(IDS_INCORRECT)); nType = 1; } str += strCorrect;
if (m_hWndMessage && ::IsWindow(m_hWndMessage)) ::SendMessage(m_hWndMessage, WM_XSUDOKU, nType, (LPARAM)(LPCTSTR)str);
Invalidate(FALSE); } }
void CXSudokuWnd::OnPopupUnPencil(UINT nID) //Rub Out a nID valu { SWITCH_RESOURCE; // load string from dll resource
int nValue = nID - ID_POPUP_UNPENCIL_1 + 1;
int row, col;
if (CellFromPoint(m_pointPopup, row, col)) { AddUndoAction(row, col, ACTION_CODE_USER_ENTRY, nValue); SetBit(m_nUnPencil[row][col],nValue); Invalidate(FALSE); } }
void CXSudokuWnd::OnPopupPencil(UINT nID) //Pencil in a value (nID) that had previously been Rubbed Out { SWITCH_RESOURCE; // load string from dll resource
int nValue = nID - ID_POPUP_PENCIL_1 + 1;
int row, col;
if (CellFromPoint(m_pointPopup, row, col)) { AddUndoAction(row, col, ACTION_CODE_USER_ENTRY, nValue); int nBit = 0; SetBit(nBit,nValue); m_nUnPencil[row][col]&=~nBit; Invalidate(FALSE); } }
// called by popup menu void CXSudokuWnd::OnPopupHighlight(UINT nID) { int nHighlightNumber = nID - ID_POPUP_HIGHLIGHT_1 + 1;
if (m_nHighlightNumber == nHighlightNumber) m_nHighlightNumber = 0; else m_nHighlightNumber = nHighlightNumber;
Invalidate(FALSE); }
// called by accelerator void CXSudokuWnd::OnHighlight(UINT nID) { int nHighlightNumber = nID - ID_HIGHLIGHT_0;
if (nHighlightNumber == 0) m_nHighlightNumber = 0; else if (m_nHighlightNumber == nHighlightNumber) m_nHighlightNumber = 0; else m_nHighlightNumber = nHighlightNumber;
Invalidate(FALSE); }
// accelerator key Shift+F10 void CXSudokuWnd::OnRightClick() { CRect rectCell = GetCellRect(m_nCurRow, m_nCurCol); CPoint point; point.x = rectCell.left + rectCell.Width()/2; point.y = rectCell.top + rectCell.Height()/2; ShowPopup(point); }
void CXSudokuWnd::OnColorPrefs() { ShowColorPrefsDlg(); }
// debug hack - F9 void CXSudokuWnd::OnLoadSample() { #ifdef _DEBUG CString strSudoku = _T("708000300000201000500000000040000026300080000000100090090600004000070500000000000"); LoadFromString(strSudoku, strSudoku.GetLength()); Invalidate(FALSE); #endif }
void CXSudokuWnd::OnEditUndo() { TRACE(_T("in CXSudokuWnd::OnEditUndo\n"));
if (!m_bEnableUndo) return;
if (m_nUndoIndex > 0) { int index = m_nUndoIndex - 1; UNDO_BLOCK * pUB = (UNDO_BLOCK *) m_Undo[index]; ASSERT(pUB); if (pUB) { m_nCurRow = (int) pUB->row; m_nCurCol = (int) pUB->col; int i, j; for (i = 0; i < 9; i++) { for (j = 0; j < 9; j++) { m_nHints[i][j] = (int) pUB->hints[i][j]; m_nUserEntries[i][j] = (int) pUB->user_entries[i][j]; } }
#ifdef _DEBUG CString str = ActionCodeToString(pUB->action_code); if (!str.IsEmpty()) { TRACE(_T("undoing action '%s'\n"), str); } #endif
m_nUndoIndex--; m_bShowSolution = FALSE; Invalidate(FALSE); } } }
void CXSudokuWnd::OnEditRedo() { TRACE(_T("in CXSudokuWnd::OnEditRedo\n"));
if (!m_bEnableUndo) return;
if (m_nUndoIndex < m_nUndoLastEntry) { int index = m_nUndoIndex; UNDO_BLOCK * pUB = (UNDO_BLOCK *) m_Undo[index]; ASSERT(pUB); if (pUB) { m_nCurRow = (int) pUB->row; m_nCurCol = (int) pUB->col; int i, j;
switch (pUB->action_code) { case 0: // unused entry break;
case ACTION_CODE_USER_ENTRY: m_nUserEntries[m_nCurRow][m_nCurCol] = pUB->action_value; break;
case ACTION_CODE_USER_ENTRY_REMOVE_ALL: for (i = 0; i < 9; i++) { for (j = 0; j < 9; j++) { m_nUserEntries[i][j] = 0; } } break;
case ACTION_CODE_HINT: m_nHints[m_nCurRow][m_nCurCol] = pUB->action_value; break;
case ACTION_CODE_HINT_REMOVE_ALL: for (i = 0; i < 9; i++) { for (j = 0; j < 9; j++) { m_nHints[i][j] = 0; } } break;
default: break; }
#ifdef _DEBUG CString str = ActionCodeToString(pUB->action_code); if (!str.IsEmpty()) { TRACE(_T("redoing undo action '%s'\n"), str); } #endif
m_nUndoIndex++; m_bShowSolution = FALSE; Invalidate(FALSE); } } }
void CXSudokuWnd::OnAppAbout() { SWITCH_RESOURCE; // load dialog from dll resource CAboutDlg dlg; dlg.DoModal(); }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
69 Bay wrote: I have found when solving a puzzle that it can you can see that certain numbers cannot be in certain locations.
I'd like to understand this better. If a number cannot be in a cell, isn't that reflected in the pencil marks? If this is not true, then perhaps you could give an example?
Thanks, Hans
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hans Dietrich wrote: If a number cannot be in a cell, isn't that reflected in the pencil marks? If this is not true, then perhaps you could give an example?
There are a number of techniques that can be used to solve really difficult (and not so difficult) that rely on elimination of possible numbers in cells. There are many web sites that desribe such techniques (e.g. look at the naked subset technique described here[^]).
As an example load the following:
4.. 7.8 396 67. ..1 ..4 3.. 6.4 1..
8.. ..5 4.. 7.. ... ..5 ..5 8.7 ...
..6 4.2 ..9 2.7 1.. 64. .84 ..6 ..2
The only cells containing 5 in ABC/123 square are 1B and 3B. Therefore no other cell in column B can contain a 5. What we want to be able to do is remove the pencil marks for 5 in cells 7B and 8B.
Cell 8B now can only contain a 3 or a 9, which together with the 3,9 in cell 8F means that we can remove the 3 & 9 from cell 8E and the 3 from cell 8I (which means that 8I must contain an 8)
Graham
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
69 Bay wrote: ... when solving a puzzle ... you can see that certain numbers cannot be in certain locations
Yes, it's a Great Solution. Thanks for sharing.
An enhancement idea: Make the messages like "user entry 9 at 4C is incorrect" optional. There are enough possibilities to "cheat", it's hard to ignore that message when trying to solve a puzzle manually.
BTW: the example is really a hard one 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Just solved the puzzle presented here by hand (i.e. I didn't use the tool). I had to post about it because it was so bleepin hard. That was a toughie!
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
LivingThoughts wrote: why you implemented control into DLL
Because of the number of files. I wanted to make it easy to "drop in" to a project.
Thanks for kind words.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
There are alot more added features than the one I referenced in VB...again a very nicely done article on such a fun and popular puzzle.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|