 |
|
 |
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
|
|
|
|
 |
|
 |
See the post by 69 Bay lower down
Graham
Librarians rule, Ook!
|
|
|
|
 |
|
 |
Excellent. Also please include code for generating puzzle to make it complete Sudoku.
Thanks
|
|
|
|
 |
|
 |
Excellent!
I take the occasion to wish you a happy 2007 and all the best in your life!
SkyWalker
|
|
|
|
 |
|
 |
Nice job very fast proof. The best i have seen on the web.
Best Regards,
Mark Edgar Software Engineer interconnect www.onedollarsudoku.com
|
|
|
|
 |
|
 |
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!
|
|
|
|
 |
|
 |
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: if ((pMsg->wParam >= _T('0')) && (pMsg->wParam <= _T('9')) && !(GetKeyState(VK_CONTROL) & 0x8000))
Graham
Librarians rule, Ook!
|
|
|
|
 |
|
 |
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!
|
|
|
|
 |
|
 |
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
|
|
|
|
 |
|
 |
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
|
|
|
|
 |
|
|
 |
|
 |
The program is very nice. It needs a New Game item that generates a game (I can do that) But on my computer the thing doesn't look quite right because I have large fonts installed. There is a screenshot at: http://www.geocities.com/jerry_jeremiah/sudoku.bmp[^] Any suggestion how I would fix that?
Thanks,
Jerry
|
|
|
|
 |
|
 |
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
|
|
|
|
 |
|
 |
I would be interested in your extension
|
|
|
|
 |
|
 |
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
#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) (x|=(1<<(y))) #define ClearBit(x,y) (x&=~(1<<(y))) #define TestBit(x,y) (x&(1<<(y)))
#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 #define ACTION_CODE_USER_ENTRY_REMOVE_ALL 2
#define ACTION_CODE_HINT 3 #define ACTION_CODE_HINT_REMOVE_ALL 4
BYTE action_value; BYTE row, col; BYTE hints[9][9]; BYTE user_entries[9][9]; }; #pragma pack(pop)
#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
BEGIN_MESSAGE_MAP(CXSudokuWnd, CWnd) 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) 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::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("");
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; }
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::~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; } } }
UINT CXSudokuWnd::OnGetDlgCode() { return DLGC_WANTALLKEYS; }
BOOL CXSudokuWnd::OnEraseBkgnd(CDC* pDC) { EraseBkgnd(pDC); return CWnd::OnEraseBkgnd(pDC); }
void CXSudokuWnd::EraseBkgnd(CDC* pDC) { CRect rect; GetClientRect(&rect); pDC->FillSolidRect(rect, m_rgbWindowBackground); }
void CXSudokuWnd::OnPaint() { CPaintDC dc(this);
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);
}
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; }
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);
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; }
void CXSudokuWnd::PaintCell(CDC * pDC, int row, int col) { CRect rectCell = GetCellRect(row, col); pDC->FillSolidRect(&rectCell, GetCellBackgroundColor(row, col)); }
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;
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);
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); }
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); }
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
{ continue; }
pDC->SetTextColor(rgbValue);
pDC->ExtTextOut(rectValue.left, rectValue.top, ETO_CLIPPED|ETO_OPAQUE, &rectValue, &g_szDigits[nValue], 1, NULL); } }
pDC->SelectObject(pOldFont); }
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; }
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; }
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; }
WORD CXSudokuWnd::GetUnPenciledCandidates(int row, int col) { return (~m_nUnPencil[row][col])&0x3FE; }
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); TRACE(_T("Candidates for %d,%d: 0x%X\n"), row, col, wBitFlags);
return wBitFlags; }
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;
return rectCell; }
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;
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); }
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); } }
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; }
void CXSudokuWnd::OnLButtonDown(UINT nFlags, CPoint point) { TRACE(_T("in CXSudokuWnd::OnLButtonDown\n"));
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); }
void CXSudokuWnd::OnLButtonDblClk(UINT nFlags, CPoint point) { TRACE(_T("in CXSudokuWnd::OnLButtonDblClk\n"));
ShowPopup(point);
CWnd::OnLButtonDblClk(nFlags, point); }
void CXSudokuWnd::OnRButtonDown(UINT nFlags, CPoint point) {
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); }
void CXSudokuWnd::OnRButtonUp(UINT nFlags, CPoint point) { ShowPopup(point);
CWnd::OnRButtonUp(nFlags, point); }
BOOL CXSudokuWnd::PreTranslateMessage(MSG* pMsg) { switch (pMsg->message) { case WM_KEYUP: if ((pMsg->wParam >= _T('0')) && (pMsg->wParam <= _T('9'))) { if ((m_nCurRow != -1) && (m_nCurCol != -1) && (m_nGivens[m_nCurRow][m_nCurCol] == 0)) { 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;
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; }
m_nHints[m_nCurRow][m_nCurCol] = 0; Invalidate(FALSE); } return TRUE; } else if (pMsg->wParam == VK_RETURN) { OnRightClick(); } break;
case WM_KEYDOWN: 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); }
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;
m_hAccel = ::LoadAccelerators(ExtensionDLL.hModule, MAKEINTRESOURCE(IDR_ACCELERATOR)); ASSERT(m_hAccel);
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; }
CString CXSudokuWnd::GetSudoku(LPCTSTR lpszPuzzle ) { SWITCH_RESOURCE;
CString strSudoku = _T("");
CSudokuEntryDlg dlg;
if (lpszPuzzle) dlg.m_strSudoku = lpszPuzzle;
if (dlg.DoModal() == IDOK) { strSudoku = dlg.m_strSudoku; }
return strSudoku; }
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; } } }
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; } } }
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();
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; }
BOOL CXSudokuWnd::LoadFromClipboard() { BOOL ok = FALSE;
m_strFile = _T("");
CClipboard clip; DWORD dwLen = clip.GetTextLength(); TRACE(_T("dwLen=%d\n"), dwLen);
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; }
BOOL CXSudokuWnd::LoadFromString(LPCTSTR lpszString, DWORD dwLen) { BOOL ok = FALSE;
Reset(TRUE);
TRACE(_T("dwLen=%d\n"), dwLen); int nCount = 0;
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')) break;
if (c == _T('.')) { nValue = 0; } else if (_istdigit(c)) { nValue = c - _T('0'); } else
{ continue; }
m_nGivens[row][col++] = nValue; nCount++; if (nCount == 81) break; if (col >= 9) { row++; col = 0; } } }
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;
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; }
BOOL CXSudokuWnd::ShowColorPrefsDlg() { SWITCH_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; }
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(); ::ReleaseDC(m_hWnd, hdc);
return bRet; }
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;
DOCINFO di; ::ZeroMemory (&di, sizeof (DOCINFO)); di.cbSize = sizeof (DOCINFO); di.lpszDocName = _T("XSudoku");
BOOL bPrintingOK = dcPrint.StartDoc(&di);
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);
CPrintInfo Info; Info.SetMaxPage(1); int maxw = dcPrint.GetDeviceCaps(HORZRES); int maxh = dcPrint.GetDeviceCaps(VERTRES); Info.m_rectDraw.SetRect(0, 0, maxw, maxh); UINT page = 1;
dcPrint.StartPage(); 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) {
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);
if (bPrintingOK) dcPrint.EndDoc(); else
dcPrint.AbortDoc();
return TRUE; }
CString CXSudokuWnd::ActionCodeToString(BYTE action_code) { CString str = _T("");
#ifdef _DEBUG switch (action_code) { case 0: 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; }
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]; } }
m_nUndoIndex++;
m_nUndoLastEntry = m_nUndoIndex;
if (m_nUndoIndex >= m_nUndoSize) { 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; }
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) { 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; WORD wPencilCandidates = ~GetUnPenciledCandidates(row, col) & 0x3FE;
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")));
CPoint pt = point; ClientToScreen(&pt); VERIFY(popup.TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x+1, pt.y+1, this)); } }
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;
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); } }
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); } } }
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;
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;
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) { SWITCH_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) { SWITCH_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); } }
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); }
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); }
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(); }
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: 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; CAboutDlg dlg; dlg.DoModal(); }
|
|
|
|
 |
|
 |
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
|
|
|
|
 |
|
 |
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
|
|
|
|
 |
|
 |
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
|
|
|
|
 |
|
 |
Thanks for sharing.
-- Ricky Marek (AKA: rbid)
-- "Things are only impossible until they are not" --- Jean-Luc Picard
My articles
|
|
|
|
 |
|
 |
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!
|
|
|
|
 |
|
 |
)
|
|
|
|
 |
|
 |
Hi very nice article. good job. Infact marvelous.By the way why you implemented control into DLL.
|
|
|
|
 |
|
 |
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.
|
|
|
|
 |
|
|
 |
|
 |
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.
|
|
|
|
 |
|