Microsoft Foundation Classes: creating a Sudoku game. Part 2.
Submitted by pavel7_7_7 on Sunday, February 9, 2014 - 18:58.
It's the second part of the article about creating a Sudoku game using MFC. In this part you will read about how to create GUI for this game.
The MFC applications are closely connected to the document-view architecture. When you create a project two classes are created: document class and view class.
The document class contains the data used in your application. The view class is a class that displays the data from the document class.
Im Sudoku project these classes are declared as:
It's the only one change to the generated document class.
The view class definition is almost unchanged. I added only 1 variable, an array of colors to draw different numbers with different colors, 3 buttons and a form to enter number(the description of this form will be shown later):
The representation of data, that is stored in the board variable is done in the function
Now let's look on the OnDraw function, because it contains the most part of the project's code.
First step is to get the pointer to the document object to draw data from the document:
Now I create some pens for work and get the clients frame rectangle:
The GUI should represent a grid of numbers. That's why we need to draw this grid first:
The board variable holds data of the Sudoku game. We need to draw the numbers from this variable in the frame:
The initialization of buttons and colors is done in the next way:
And this is an example how to set text to button:
The next step is to handle events from 3 buttons and event of mouse pressed. We have to edit the declaration of message map and add next events:
This can be done automatically by Visual Studio by adding events in the property window. The message man declares which function will handle event from every source. So now we have to implement function to handle events.
When you press the "New Game" button a form to select difficulty appears:
The onNewGame functions takes the result of your selection and sets difficulty to the game:
The implementation of the difficulty form is:
The help button simply add a number from the solution array to the array of current state:
The most interesting thing is about handling event from mouse. For the game we need to achieve the coordinates of the pressed point and check, if it's pressed on an empty cell:
Now, if the cell is empty, ask user to input a number:
In this part of code a form to input a number appears:
If the inputted number can be added to the board it's added and the main frame is redrawn.If it's an incorrect number - user will see the message of the error and nothing will be changed.
You can download the source code of the game and try it by yourself. Try to solve the game on the most difficult level.
class CSudokuDoc : public CDocument
and
class CSudokuView : public CView
In the document class we declare a variable of board
class:
- public:
- board game;
- public:
- COLORREF colors[9];
- bool editOpened;
- //buttons---------------------
- CButton newGame,
- help,
- solve,
- about;
- //--------------Buttons rects
- CRect _NewGame,
- _Help,
- _Solve,
- _About;
- //-----------------
- CEdit *enterData;
- virtual void OnDraw(CDC* pDC);
- CSudokuDoc* pDoc = GetDocument();
- ASSERT_VALID(pDoc);
- if (!pDoc)
- return;
- CPen SolidPen(PS_SOLID,3,RGB(0,0,0));
- CPen ThinPen(PS_SOLID,1,RGB(0,0,0));
- help.SetRedraw();
- CRect rcClient;
- GetClientRect(&rcClient);
- for (int i = 0; i != 4; ++i)
- {
- pDC->SelectObject(&SolidPen);
- pDC->MoveTo(0,i*rcClient.Height()/3);
- pDC->LineTo((int)(rcClient.Width()*0.75),(int)(i*rcClient.Height()/3));
- pDC->MoveTo(i*rcClient.Width()*0.25,0);
- pDC->LineTo(i*rcClient.Width()*0.25,i*rcClient.Height());
- pDC->SelectObject(&ThinPen);
- pDC->MoveTo(0,i*rcClient.Height()/3+1.0/9.0*rcClient.Height());
- pDC->LineTo(rcClient.Width()*0.75,i*rcClient.Height()/3+1.0/9.0*rcClient.Height());
- pDC->MoveTo(0,i*rcClient.Height()/3+2.0/9.0*rcClient.Height());
- pDC->LineTo(rcClient.Width()*0.75,i*rcClient.Height()/3+2.0/9.0*rcClient.Height());
- if ( i != 3){
- pDC->MoveTo(i*rcClient.Width()*0.25 + 1.0/9.0*rcClient.Width()*0.75,0);
- pDC->LineTo(i*rcClient.Width()*0.25 + 1.0/9.0*rcClient.Width()*0.75,rcClient.Height());
- pDC->MoveTo(i*rcClient.Width()*0.25 + 2.0/9.0*rcClient.Width()*0.75,0);
- pDC->LineTo(i*rcClient.Width()*0.25 + 2.0/9.0*rcClient.Width()*0.75,rcClient.Height());
- }
- }
- for(int i1 = 0; i1 != 9; ++i1){
- for(int i2 = 0; i2 != 9; ++i2){
- if(pDoc->game.numbers[i1][i2])
- {
- CString t;
- t.Format(_T("%d"),pDoc->game.numbers[i1][i2]);
- RECT posK = {i2*rcClient.Width()*0.75/9.0+25,i1*rcClient.Height()/9.0+25,(i2+1)*rcClient.Width()*0.75/9.0,(i1+1)*rcClient.Height()/9.0};
- pDC->SetBkMode(TRANSPARENT);
- pDC->SetTextColor(colors[pDoc->game.numbers[i1][i2]-1]);
- pDC->DrawText(t,1,&posK,DT_CALCRECT);
- pDC->DrawText(t,1,&posK,0);
- }
- }
- }
- CSudokuView::CSudokuView()
- {
- _NewGame = new CRect(600,10,800,50);
- _Help = new CRect(600,60,800,100);
- _Solve = new CRect(600,110,800,150);
- _About = new CRect(600,200,800,240);
- editOpened = false;
- colors[0] = RGB(0,0,0);
- colors[1] = RGB(0,0,205);
- colors[2] = RGB(0,100,0);
- colors[3] = RGB(255,255,0);
- colors[4] = RGB(255,0,0);
- colors[5] = RGB(160,32,240);
- colors[6] = RGB(0,0,255);
- colors[7] = RGB(0,255,0);
- colors[8] = RGB(139,35,35);
- }
- newGame.Create(_T("New game"), WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
- _NewGame, this, NEWGAME_BN);
- help.Create(_T("Help"), WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
- _Help, this, HELP_BN);
- solve.Create(_T("Solve"), WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
- _Solve, this, SOLVE_BN);
- about.Create(_T("About"), WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
- _About, this, ABOUT_BN);
- BEGIN_MESSAGE_MAP(CSudokuView, CView)
- ON_BN_CLICKED(NEWGAME_BN,onNewGame)
- ON_BN_CLICKED(HELP_BN,onHelp)
- ON_BN_CLICKED(SOLVE_BN,onSolve)
- ON_BN_CLICKED(ABOUT_BN,onAbout)
- ON_WM_LBUTTONDOWN()
- END_MESSAGE_MAP()
- CSudokuDoc* pDoc = GetDocument();
- difList list1;
- list1.DoModal();
- switch(list1.selected)
- {
- case 0: pDoc->game.difficulty = 30;
- break;
- case 1: pDoc->game.difficulty = 40;
- break;
- case 2: pDoc->game.difficulty = 50;
- break;
- case 3: pDoc->game.difficulty = 64;
- break;
- default:pDoc->game.difficulty = 30;
- break;
- }
- pDoc->game.generate();
- Invalidate();
- BEGIN_MESSAGE_MAP(difList, CDialog)
- END_MESSAGE_MAP()
- BOOL difList::OnInitDialog()
- {
- CDialog::OnInitDialog();
- SetWindowText(L"Chose difficulty");
- this->SetWindowPos(&CWnd::wndTop, 0, 0, 200,200,SWP_NOMOVE);
- difBox.Create(WS_CHILD|WS_VISIBLE|LBS_STANDARD|WS_HSCROLL, CRect(0,0,200,100), this, 99);
- difBox.AddString(L"1. Easy");
- difBox.AddString(L"2. Medium");
- difBox.AddString(L"3. Hard");
- difBox.AddString(L"4. Allmost unsolvable");
- difBox.SetSel(0,true);
- GetDlgItem(IDOK)->MoveWindow(60,140,80,30);
- return TRUE; // return TRUE unless you set the focus to a control
- }
- void difList::OnOK()
- {
- // TODO: добавьте специализированный код или вызов базового класса
- selected = difBox.GetCurSel();
- CDialog::OnOK();
- }
- if(!GetDocument()->game.win())
- {
- MessageBox(_T("A number is added to board"), _T("Help"),MB_ICONQUESTION| MB_OK);
- GetDocument()->game.help();
- Invalidate();
- }
- CRect rcClient;
- GetClientRect(&rcClient);
- CSudokuDoc* pDoc = GetDocument();
- int row = 9*point.y/rcClient.Height();
- int column = (9*point.x)/(rcClient.Width()*0.75);
- if(!pDoc->game.numbers[row][column])
- {
- enterNumber addNum;
- addNum.DoModal();
- if(addNum.numb&&pDoc->game.checkNumberValidity(addNum.numb,row,column))
- pDoc->game.numbers[row][column]=addNum.numb;
- else
- MessageBox(L"Incorrect number!!!!!!!!",(LPCTSTR)0,MB_ICONERROR | MB_OK);
- if(pDoc->game.win())
- MessageBox(L"You win!!!!!!!!",(LPCTSTR)0, MB_OK);
- Invalidate();
- }
Add new comment
- 87 views