[游戏学习22] MFC 井字棋 双人对战

>_<:太多啦,感觉用英语说的太慢啦,没想到一年做的东西竟然这么多.....接下来要加速啦!

>_<:注意这里必须用MFC和前面的Win32不一样啦!

>_<:这也是第一次出现MFC游戏,其框架和逻辑的写法和Win32有很大的区别,建议先看一下MFC的基础再理解代码:

>_<:TicTac.h


 1 #define EX 1            //该点左鼠标
2 #define OH 2 //该点右鼠标
3
4 class CMyApp : public CWinApp
5 {
6 public:
7 virtual BOOL InitInstance ();
8 };
9
10 class CMainWindow : public CWnd //不是继承CFrameWnd 因此需要在CMainWindow()自己定义窗口类了
11 {
12 protected:
13 static const CRect m_rcSquares[9]; // Grid coordinates
14 int m_nGameGrid[9]; // 9个格子的状态是否被下0没下;1左下了;2右下了
15 int m_nNextChar; // 下一个鼠标状态左or右 (EX or OH)
16 bool ptab[9][8]; //玩家的获胜的状态表
17 bool ctab[9][8]; //电脑的获胜的状态表
18 int win[2][8]; //每种状态表里的棋子数
19
20 int GetRectID (CPoint point);
21 void DrawBoard (CDC* pDC);
22 void DrawX (CDC* pDC, int nPos);
23 void DrawO (CDC* pDC, int nPos);
24 void CpDraw(CDC* pDC);
25 void InitGame();
26 void out();
27 void ResetGame ();
28 bool CheckForGameOver ();
29 int IsWinner ();
30 BOOL IsDraw ();
31
32 public:
33 CMainWindow ();
34
35 protected:
36 virtual void PostNcDestroy ();//在程序终止之前销毁CMainWindow对象
37
38 afx_msg void OnPaint ();
39 afx_msg void OnLButtonDown (UINT nFlags, CPoint point);
40 afx_msg void OnLButtonDblClk (UINT nFlags, CPoint point);
41 afx_msg void OnRButtonDown (UINT nFlags, CPoint point);
42
43 DECLARE_MESSAGE_MAP ()
44 };

>_<:TicTac.cpp


  1 #include <afxwin.h>
2 #include "TicTac.h"
3 #include <fstream>
4 #include <iostream>
5 #include<iomanip>
6 using namespace std;
7 CMyApp myApp;
8 /*ofstream Cout("out.txt");
9 void CMainWindow::out(){
10 Cout<<"ptab[][]=:\n";
11 for(int i=0;i<9;i++){
12 for(int j=0;j<8;j++)
13 Cout<<setw(3)<<ptab[i][j]<<‘ ‘;
14 Cout<<‘\n‘;
15 }
16 Cout<<"ctab[][]=:\n";
17 for(int i=0;i<9;i++){
18 for(int j=0;j<8;j++)
19 Cout<<setw(3)<<ctab[i][j]<<‘ ‘;
20 Cout<<‘\n‘;
21 }
22 Cout<<"win[][]=:\n";
23 for(int i=0;i<2;i++){
24 for(int j=0;j<8;j++)
25 Cout<<setw(3)<<win[i][j]<<‘ ‘;
26 Cout<<‘\n‘;
27 }
28 }*/
29 /////////////////////////////////////////////////////////////////////////
30 // CMyApp member functions
31
32 BOOL CMyApp::InitInstance ()
33 {
34 m_pMainWnd = new CMainWindow;
35 m_pMainWnd->ShowWindow (m_nCmdShow);
36 m_pMainWnd->UpdateWindow ();
37 return TRUE;
38 }
39
40 /////////////////////////////////////////////////////////////////////////
41 // CMainWindow message map and member functions
42
43 BEGIN_MESSAGE_MAP (CMainWindow, CWnd)
44 ON_WM_PAINT ()
45 ON_WM_LBUTTONDOWN ()
46 ON_WM_LBUTTONDBLCLK ()
47 ON_WM_RBUTTONDOWN ()
48 END_MESSAGE_MAP ()
49
50 //9个矩形区域用来判定鼠标是否点进某一区域
51 const CRect CMainWindow::m_rcSquares[9] = {
52 CRect ( 16, 16, 112, 112),
53 CRect (128, 16, 224, 112),
54 CRect (240, 16, 336, 112),
55 CRect ( 16, 128, 112, 224),
56 CRect (128, 128, 224, 224),
57 CRect (240, 128, 336, 224),
58 CRect ( 16, 240, 112, 336),
59 CRect (128, 240, 224, 336),
60 CRect (240, 240, 336, 336)
61 };
62
63 CMainWindow::CMainWindow ()
64 {
65 //初始化游戏
66 InitGame();
67
68
69
70 //注册一个 WNDCLASS 窗口类.
71 CString strWndClass = AfxRegisterWndClass (
72 CS_DBLCLKS, // Class style(有双击时间发生的窗口类型)
73 AfxGetApp ()->LoadStandardCursor (IDC_ARROW), // Class cursor(加载一个系统光标,也可自己定义)
74 (HBRUSH) (COLOR_3DFACE + 1), // Background brush(每次::BeginPaint时用它清空客户区);COLOR_3DFACE+1是指定窗口具有与按钮对话框一致的背景色和其他一些3D属性;默认为灰亮色
75 AfxGetApp ()->LoadStandardIcon (IDI_WINLOGO) // Class icon(加载系统图标,也可自己定义)
76 );
77
78 //调用CWnd::CreateEx()创建主窗口
79 //第一个参数表示0个或是多个WS_EX标志组合;2:AfxRegisterWndClass()返回的WNDCLASS名称;
80 //3、标题;4、窗口样式
81 CreateEx (0, strWndClass, _T ("井字棋"),
82 WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX, //WS_THICKFRAME窗口可调大小属性(这里不用)
83 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, //初始位置和大小,这里用CW_USEDEFAULT让Windows拾取窗口和大小
84 NULL, NULL);
85
86 //处理窗口位置和尺寸
87 CRect rect (0, 0, 352, 352); //理想客户区窗口矩形形状
88 CalcWindowRect (&rect); //根据分辨率、菜单...计算窗口矩形大小(必须在窗口创建后调用)
89
90 SetWindowPos (NULL, 0, 0, rect.Width (), rect.Height (),
91 SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW);
92 }
93
94 //在程序结束之前销毁创建的CMainWindow对象
95 void CMainWindow::PostNcDestroy ()
96 {
97 delete this;
98 }
99
100 //OnPaint()响应每次重绘棋盘
101 void CMainWindow::OnPaint ()
102 {
103 CPaintDC dc (this);
104 DrawBoard (&dc);
105 }
106
107
108 //单击鼠标左键响应
109 void CMainWindow::OnLButtonDown (UINT nFlags, CPoint point)
110 {
111 CClientDC dc (this);
112
113 //如果不该左键响应(即不该左键下,返回)
114 if (m_nNextChar != EX){
115 return ;
116 }
117
118 //获得点击矩形区域编号
119 //如果没有点中或者已经被下棋了,返回
120 int nPos = GetRectID (point);
121 if ((nPos == -1) || (m_nGameGrid[nPos] != 0))
122 return;
123
124 //标记已下并改变下一个点击状态
125 m_nGameGrid[nPos] = EX;
126 m_nNextChar = OH;
127
128 //画上图并判断游戏是否结束
129 DrawX (&dc, nPos);
130 if(CheckForGameOver ())return;
131
132 //后续改变胜利表和各人、机各胜利组合的棋子数
133 for(int i=0;i<8;i++){
134 if(ptab[nPos][i]){
135 win[0][i]++;
136 ctab[nPos][i]=false;
137 win[1][i]=5;
138 }
139 }
140
141 //电脑下棋
142 CpDraw(&dc);
143 if(CheckForGameOver ())return;
144 }
145
146 //单击鼠标右键响应(同左键)
147 void CMainWindow::OnRButtonDown (UINT nFlags, CPoint point)
148 {
149 if (m_nNextChar != OH)
150 return;
151
152 int nPos = GetRectID (point);
153 if ((nPos == -1) || (m_nGameGrid[nPos] != 0))
154 return;
155
156 m_nGameGrid[nPos] = OH;
157 m_nNextChar = EX;
158
159 CClientDC dc (this);
160 DrawO (&dc, nPos);
161 CheckForGameOver ();
162 }
163
164 //左键双击边框重新开始
165 //dc.GetPixel (Point point)获取当前光标下像素颜色判断与黑色匹配
166 void CMainWindow::OnLButtonDblClk (UINT nFlags, CPoint point)
167 {
168 CClientDC dc (this);
169 if (dc.GetPixel (point) == RGB (0, 0, 0))
170 ResetGame ();
171 }
172
173 //判定鼠标是否点进矩形某一区域,点进返回区域编号,没有返回-1
174 //此处用了一个rect.PtInRect(Point point)函数帮助判定
175 int CMainWindow::GetRectID (CPoint point)
176 {
177 for (int i=0; i<9; i++) {
178 if (m_rcSquares[i].PtInRect (point))
179 return i;
180 }
181 return -1;
182 }
183
184 //画上棋盘并画上圈和叉
185 void CMainWindow::DrawBoard (CDC* pDC)
186 {
187 //画上棋盘
188 CPen pen (PS_SOLID, 16, RGB (0, 0, 0));
189 CPen* pOldPen = pDC->SelectObject (&pen);
190
191 pDC->MoveTo (120, 16);
192 pDC->LineTo (120, 336);
193
194 pDC->MoveTo (232, 16);
195 pDC->LineTo (232, 336);
196
197 pDC->MoveTo (16, 120);
198 pDC->LineTo (336, 120);
199
200 pDC->MoveTo (16, 232);
201 pDC->LineTo (336, 232);
202
203 //画上叉和圈
204 for (int i=0; i<9; i++) {
205 if (m_nGameGrid[i] == EX)
206 DrawX (pDC, i);
207 else if (m_nGameGrid[i] == OH)
208 DrawO (pDC, i);
209 }
210 pDC->SelectObject (pOldPen);
211 }
212
213 //画叉函数
214 void CMainWindow::DrawX (CDC* pDC, int nPos)
215 {
216 CPen pen (PS_SOLID, 16, RGB (255, 0, 0));//宽为16像素的红笔
217 CPen* pOldPen = pDC->SelectObject (&pen);
218
219 CRect rect = m_rcSquares[nPos];
220 rect.DeflateRect (16, 16);//把矩形每个方向都缩进16个像素作为线条边框
221 pDC->MoveTo (rect.left, rect.top);
222 pDC->LineTo (rect.right, rect.bottom);
223 pDC->MoveTo (rect.left, rect.bottom);
224 pDC->LineTo (rect.right, rect.top);
225
226 pDC->SelectObject (pOldPen);
227 }
228
229 //画圈函数
230 void CMainWindow::DrawO (CDC* pDC, int nPos)
231 {
232 CPen pen (PS_SOLID, 16, RGB (0, 0, 255));//宽为16像素的红笔
233 CPen* pOldPen = pDC->SelectObject (&pen);
234 pDC->SelectStockObject (NULL_BRUSH); //空画刷是为了防止画出的圆内部出现白色遮住背景
235
236 CRect rect = m_rcSquares[nPos];
237 rect.DeflateRect (16, 16);//把矩形每个方向都缩进16个像素作为圆的边框
238 pDC->Ellipse (rect);
239
240 pDC->SelectObject (pOldPen);
241 }
242
243 //电脑画图
244 void CMainWindow::CpDraw(CDC* pDC)
245 {
246 int grades[2][9];
247 int m,i,max=0;
248 int u;
249
250 for(m=0;m<9;m++)
251 {
252 grades[0][m]=0;
253 grades[1][m]=0;
254
255 if(m_nGameGrid[m]==0)
256 {
257 for(i=0;i<8;i++)
258 {
259 //计算玩家在空棋格上的获胜分数
260 if(ptab[m][i] && win[0][i]!=5)
261 {
262 switch(win[0][i])
263 {
264 case 0:
265 grades[0][m]+=1;
266 break;
267 case 1:
268 grades[0][m]+=2000;
269 break;
270 case 2:
271 grades[0][m]+=10000;
272 break;
273 }
274 }
275
276 //计算计算机在空格上的获胜分数
277 if(ctab[m][i] && win[1][i]!=5)
278 {
279 switch(win[1][i])
280 {
281 case 0:
282 grades[1][m]+=1;
283 break;
284 case 1:
285 grades[1][m]+=2001;
286 break;
287 case 2:
288 grades[1][m]+=10001;
289 break;
290 }
291 }
292 }
293
294 if(max==0)u=m;
295
296 if(grades[0][m]>max){
297 max=grades[0][m];
298 u=m;
299 }
300 else if(grades[0][m]==max){
301 if(grades[1][m]>grades[1][u])u=m;
302 }
303
304 if(grades[1][m]>max){
305 max=grades[1][m];
306 u=m;
307 }
308 else if(grades[1][m]==max){
309 if(grades[0][m]>grades[0][u])u=m;
310 }
311 }
312 }
313
314 //标记已下并改变下一个点击状态
315 m_nGameGrid[u]=OH;
316 m_nNextChar = EX;
317
318 //画上图
319 DrawO(pDC,u);
320
321 //后续改变胜利表和各人、机各胜利组合的棋子数
322 for(i=0;i<8;i++){
323 if(ctab[u][i]){
324 win[1][i]++;
325 ptab[u][i]=false;
326 win[0][i]=5;
327 }
328 }
329 }
330
331 //响应胜利结束的函数
332 bool CMainWindow::CheckForGameOver ()
333 {
334 int nWinner;
335
336 //通过调用IsWinner ()函数获取谁获胜;并用MessageBox输出胜利消息;响应OK后重开一局
337 //==Message(CString,_T(标题),类型)
338 if (nWinner = IsWinner ()) {
339 CString string = (nWinner == EX) ?
340 _T ("X wins!") : _T ("O wins!");
341 MessageBox (string, _T ("Game Over"), MB_ICONEXCLAMATION | MB_OK);
342 ResetGame ();
343 return 1;
344 }
345
346 //通过IsDraw ()函数判断是否平局
347 else if (IsDraw ()) {
348 MessageBox (_T ("It‘s a draw!"), _T ("Game Over"),
349 MB_ICONEXCLAMATION | MB_OK);
350 ResetGame ();
351 return 1;
352 }
353 return 0;
354 }
355
356 //判断输赢EX左胜;OH右胜;0没有胜
357 int CMainWindow::IsWinner ()
358 {
359 //用静态数组存储获胜组合
360 static int nPattern[8][3] = {
361 0, 1, 2,
362 3, 4, 5,
363 6, 7, 8,
364 0, 3, 6,
365 1, 4, 7,
366 2, 5, 8,
367 0, 4, 8,
368 2, 4, 6
369 };
370
371 for (int i=0; i<8; i++) {
372 if ((m_nGameGrid[nPattern[i][0]] == EX) &&
373 (m_nGameGrid[nPattern[i][1]] == EX) &&
374 (m_nGameGrid[nPattern[i][2]] == EX))
375 return EX;
376
377 if ((m_nGameGrid[nPattern[i][0]] == OH) &&
378 (m_nGameGrid[nPattern[i][1]] == OH) &&
379 (m_nGameGrid[nPattern[i][2]] == OH))
380 return OH;
381 }
382 return 0;
383 }
384
385 //判断是否平局函数
386 BOOL CMainWindow::IsDraw ()
387 {
388 for (int i=0; i<9; i++) {
389 if (m_nGameGrid[i] == 0)
390 return FALSE;
391 }
392 return TRUE;
393 }
394
395 //初始化游戏
396 void CMainWindow::InitGame()
397 {
398
399 int i,k;
400 int count=0;
401
402 //设定玩家与计算机在各个获胜组合中的棋子数
403 for(i=0;i<8;i++)
404 {
405 win[0][i]=0;
406 win[1][i]=0;
407 }
408
409 //初始化棋盘状态
410 ::ZeroMemory (m_nGameGrid,9*sizeof(int));
411 memset(ctab,0,sizeof(ctab));
412 memset(ptab,0,sizeof(ptab));
413 //设定水平方向的获胜组合
414 for(i=0;i<=6;i+=3)
415 {
416 for(k=0;k<3;k++)//3个棋子1个获胜组合
417 {
418 ptab[i+k][count]=true;
419 ctab[i+k][count]=true;
420 }
421 count++;
422 }
423
424 //设定垂直方向的获胜组合
425 for(k=0;k<3;k++)
426 {
427 for(i=0;i<=6;i+=3)//3个棋子1个获胜组合
428 {
429 ptab[i+k][count]=true;
430 ctab[i+k][count]=true;
431 }
432 count++;
433 }
434
435
436 //设定对角线方向上的获胜组合
437 for(i=2;i<=6;i+=2){
438 ptab[i][count]=true;
439 ctab[i][count]=true;
440 }count++;
441 for(i=0;i<=8;i+=4){
442 ptab[i][count]=true;
443 ctab[i][count]=true;
444 }
445
446
447 srand(unsigned(time(NULL)));
448
449 m_nNextChar = EX;//玩家先走
450 }
451 //重新开始初始化
452 void CMainWindow::ResetGame ()
453 {
454 InitGame();
455 Invalidate (); //使控件的整个图面无效并导致重绘控件
456 }

[游戏学习22] MFC 井字棋 双人对战,布布扣,bubuko.com

时间: 2024-12-25 18:31:14

[游戏学习22] MFC 井字棋 双人对战的相关文章

井字棋游戏升级版 - TopTicTacToe项目 简介

一.游戏简介 井字棋是一款世界闻名的游戏,不用我说,你一定知道它的游戏规则. 这款游戏简单易学,玩起来很有意思,不过已经证明出这款游戏如果两个玩家都足够聪明的话, 是很容易无法分出胜负的,即我们得到的结果是平局. 我们的项目,就是井字棋游戏的升级版!游戏有九个小棋盘,每个棋盘构成了整体布局的一部分,要想获得游戏的胜利,你要把握整个局势才行! 二.亮点 创新 传统的井字棋只有九个格,玩法简单,但是变化也相当有限.初玩者很容易被这新颖的游戏吸引住,但是玩了一段时间后,很容易摸出规律,很轻松达到不败的

人工智能博弈树算法做的井字棋游戏

不会输,超碉!井字棋这个游戏真是太无聊啦! 算法大概就是,有一个给状况进行估价的函数,深搜每种状况,假设每个人都按对自己最有利的方式走(假设玩家也是不傻),最后让电脑走出最有利的一步. 代码: 1 //#pragma comment(linker, "/STACK:102400000,102400000") 2 #include<cstdio> 3 #include<cmath> 4 #include<iostream> 5 #include<

C++井字棋游戏,DOS界面版

据说有一个能保证不败的算法.明天看看先再写个PVC版的. 正题.今天无聊写了个井字棋游戏,顺便逐渐让自己习惯良好的代码风格,放上来给新手学习学习. jzq2.cpp /* N字棋游戏PVP版,DOS版 本棋盘可扩充,仅仅需调整检測条件就可以,其它接口不需改变. 非人机对战型.PVP类型; @author:天下无双 @date:2014-5-25 @version:1.0 */ #include <iostream> #include <string> #define INVALID

『HTML5实现人工智能』小游戏《井字棋》发布,据说IQ上200才能赢【算法&amp;代码讲解+资源打包下载】

一,什么是TicTacToe(井字棋) 本游戏为在下用lufylegend开发的第二款小游戏.此游戏是大家想必大家小时候都玩过,因为玩它很简单,只需要一张草稿纸和一只笔就能开始游戏,所以广受儿童欢迎.可能我说了半天,对它名字不熟悉的朋友也不懂我在说神马.那没关系,我就引用Wiki(维基百科)的介绍作为大家对它名字的认识,顺便也勾起我们儿时的回忆: 井字棋,大陆.台湾又称为井字游戏.圈圈叉叉:另外也有打井游戏.OX棋的称呼,香港多称井字过三关.过三关,是种纸笔游戏.两个玩家,一个打圈(O),一个打

[CareerCup] 17.2 Tic Tac Toe 井字棋游戏

17.2 Design an algorithm to figure out if someone has won a game oftic-tac-toe. 这道题让我们判断玩家是否能赢井字棋游戏,有下面几点需要考虑: 1. 判断是否能赢hasWon函数是调用一次还是多次,如果是多次,我们可能为了优化而需要加入一些预处理. 2. 井字棋游戏通常是3x3的大小,我们是否想要实现NxN的大小? 3. 我们需要在代码紧凑,执行速度和代码清晰之间做出选择. #include <iostream> #

Pascal小游戏 井字棋

一个很经典的井字棋游戏 Pascal源码Chaobs奉上 注意:1.有的FP版本不支持汉语,将会出现乱码.2.别想赢电脑了,平手不错了. 井字过三关: program TicTacToe; uses crt; var a:Array [1..3] of Array [1..3] of char; b:Array [1..3] of Array [1..3] of integer; i,n,g,e,p:integer; t:text; c:char; o:integer; r:integer; s

LeetCode 5275. 找出井字棋的获胜者 Find Winner on a Tic Tac Toe Game

地址 https://www.acwing.com/solution/LeetCode/content/6670/ 题目描述A 和 B 在一个 3 x 3 的网格上玩井字棋. 井字棋游戏的规则如下: 玩家轮流将棋子放在空方格 (” “) 上.第一个玩家 A 总是用 “X” 作为棋子,而第二个玩家 B 总是用 “O” 作为棋子.“X” 和 “O” 只能放在空方格中,而不能放在已经被占用的方格上.只要有 3 个相同的(非空)棋子排成一条直线(行.列.对角线)时,游戏结束.如果所有方块都放满棋子(不为

[游戏学习26] MFC 时间函数 画图形

>_<:这里第一次介绍MFC的时间函数,功能和Win32里的计时器类似. >_<:这里还介绍了MFC的图形绘制函数,和Win32有一点区别 >_<:ABC.h 1 #define EX 1 //该点左鼠标 2 #define OH 2 //该点右鼠标 3 4 class CMyApp : public CWinApp 5 { 6 public: 7 virtual BOOL InitInstance (); 8 }; 9 10 class CMainWindow : p

[游戏学习25] MFC 橡皮筋画线效果

>_<:这是给出窗口内外不同情况的处理展示的例子. >_<:MouseCap.h 1 #include<afxwin.h> 2 class CMyApp :public CWinApp 3 { 4 public: 5 virtual BOOL InitInstance(); 6 }; 7 class CMainWindow:public CFrameWnd 8 { 9 protected: 10 BOOL m_bTracking; //标志:鼠标按下为真,否则为假 11