[游戏学习22] MFC 井字棋 双人对战
>_<:太多啦,感觉用英语说的太慢啦,没想到一年做的东西竟然这么多.....接下来要加速啦!
>_<:注意这里必须用MFC和前面的Win32不一样啦!
>_<:这也是第一次出现MFC游戏,其框架和逻辑的写法和Win32有很大的区别,建议先看一下MFC的基础再理解代码:
>_<:TicTac.h
- #define EX 1 //该点左鼠标
- #define OH 2 //该点右鼠标
- class CMyApp : public CWinApp
- {
- public:
- virtual BOOL InitInstance ();
- };
- class CMainWindow : public CWnd //不是继承CFrameWnd 因此需要在CMainWindow()自己定义窗口类了
- {
- protected:
- static const CRect m_rcSquares[]; // Grid coordinates
- int m_nGameGrid[]; // 9个格子的状态是否被下0没下;1左下了;2右下了
- int m_nNextChar; // 下一个鼠标状态左or右 (EX or OH)
- bool ptab[][]; //玩家的获胜的状态表
- bool ctab[][]; //电脑的获胜的状态表
- int win[][]; //每种状态表里的棋子数
- int GetRectID (CPoint point);
- void DrawBoard (CDC* pDC);
- void DrawX (CDC* pDC, int nPos);
- void DrawO (CDC* pDC, int nPos);
- void CpDraw(CDC* pDC);
- void InitGame();
- void out();
- void ResetGame ();
- bool CheckForGameOver ();
- int IsWinner ();
- BOOL IsDraw ();
- public:
- CMainWindow ();
- protected:
- virtual void PostNcDestroy ();//在程序终止之前销毁CMainWindow对象
- afx_msg void OnPaint ();
- afx_msg void OnLButtonDown (UINT nFlags, CPoint point);
- afx_msg void OnLButtonDblClk (UINT nFlags, CPoint point);
- afx_msg void OnRButtonDown (UINT nFlags, CPoint point);
- DECLARE_MESSAGE_MAP ()
- };
>_<:TicTac.cpp
- #include <afxwin.h>
- #include "TicTac.h"
- #include <fstream>
- #include <iostream>
- #include<iomanip>
- using namespace std;
- CMyApp myApp;
- /*ofstream Cout("out.txt");
- void CMainWindow::out(){
- Cout<<"ptab[][]=:\n";
- for(int i=0;i<9;i++){
- for(int j=0;j<8;j++)
- Cout<<setw(3)<<ptab[i][j]<<' ';
- Cout<<'\n';
- }
- Cout<<"ctab[][]=:\n";
- for(int i=0;i<9;i++){
- for(int j=0;j<8;j++)
- Cout<<setw(3)<<ctab[i][j]<<' ';
- Cout<<'\n';
- }
- Cout<<"win[][]=:\n";
- for(int i=0;i<2;i++){
- for(int j=0;j<8;j++)
- Cout<<setw(3)<<win[i][j]<<' ';
- Cout<<'\n';
- }
- }*/
- /////////////////////////////////////////////////////////////////////////
- // CMyApp member functions
- BOOL CMyApp::InitInstance ()
- {
- m_pMainWnd = new CMainWindow;
- m_pMainWnd->ShowWindow (m_nCmdShow);
- m_pMainWnd->UpdateWindow ();
- return TRUE;
- }
- /////////////////////////////////////////////////////////////////////////
- // CMainWindow message map and member functions
- BEGIN_MESSAGE_MAP (CMainWindow, CWnd)
- ON_WM_PAINT ()
- ON_WM_LBUTTONDOWN ()
- ON_WM_LBUTTONDBLCLK ()
- ON_WM_RBUTTONDOWN ()
- END_MESSAGE_MAP ()
- //9个矩形区域用来判定鼠标是否点进某一区域
- const CRect CMainWindow::m_rcSquares[] = {
- CRect ( , , , ),
- CRect (, , , ),
- CRect (, , , ),
- CRect ( , , , ),
- CRect (, , , ),
- CRect (, , , ),
- CRect ( , , , ),
- CRect (, , , ),
- CRect (, , , )
- };
- CMainWindow::CMainWindow ()
- {
- //初始化游戏
- InitGame();
- //注册一个 WNDCLASS 窗口类.
- CString strWndClass = AfxRegisterWndClass (
- CS_DBLCLKS, // Class style(有双击时间发生的窗口类型)
- AfxGetApp ()->LoadStandardCursor (IDC_ARROW), // Class cursor(加载一个系统光标,也可自己定义)
- (HBRUSH) (COLOR_3DFACE + ), // Background brush(每次::BeginPaint时用它清空客户区);COLOR_3DFACE+1是指定窗口具有与按钮对话框一致的背景色和其他一些3D属性;默认为灰亮色
- AfxGetApp ()->LoadStandardIcon (IDI_WINLOGO) // Class icon(加载系统图标,也可自己定义)
- );
- //调用CWnd::CreateEx()创建主窗口
- //第一个参数表示0个或是多个WS_EX标志组合;2:AfxRegisterWndClass()返回的WNDCLASS名称;
- //3、标题;4、窗口样式
- CreateEx (, strWndClass, _T ("井字棋"),
- WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX, //WS_THICKFRAME窗口可调大小属性(这里不用)
- CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, //初始位置和大小,这里用CW_USEDEFAULT让Windows拾取窗口和大小
- NULL, NULL);
- //处理窗口位置和尺寸
- CRect rect (, , , ); //理想客户区窗口矩形形状
- CalcWindowRect (&rect); //根据分辨率、菜单...计算窗口矩形大小(必须在窗口创建后调用)
- SetWindowPos (NULL, , , rect.Width (), rect.Height (),
- SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW);
- }
- //在程序结束之前销毁创建的CMainWindow对象
- void CMainWindow::PostNcDestroy ()
- {
- delete this;
- }
- //OnPaint()响应每次重绘棋盘
- void CMainWindow::OnPaint ()
- {
- CPaintDC dc (this);
- DrawBoard (&dc);
- }
- //单击鼠标左键响应
- void CMainWindow::OnLButtonDown (UINT nFlags, CPoint point)
- {
- CClientDC dc (this);
- //如果不该左键响应(即不该左键下,返回)
- if (m_nNextChar != EX){
- return ;
- }
- //获得点击矩形区域编号
- //如果没有点中或者已经被下棋了,返回
- int nPos = GetRectID (point);
- if ((nPos == -) || (m_nGameGrid[nPos] != ))
- return;
- //标记已下并改变下一个点击状态
- m_nGameGrid[nPos] = EX;
- m_nNextChar = OH;
- //画上图并判断游戏是否结束
- DrawX (&dc, nPos);
- if(CheckForGameOver ())return;
- //后续改变胜利表和各人、机各胜利组合的棋子数
- for(int i=;i<;i++){
- if(ptab[nPos][i]){
- win[][i]++;
- ctab[nPos][i]=false;
- win[][i]=;
- }
- }
- //电脑下棋
- CpDraw(&dc);
- if(CheckForGameOver ())return;
- }
- //单击鼠标右键响应(同左键)
- void CMainWindow::OnRButtonDown (UINT nFlags, CPoint point)
- {
- if (m_nNextChar != OH)
- return;
- int nPos = GetRectID (point);
- if ((nPos == -) || (m_nGameGrid[nPos] != ))
- return;
- m_nGameGrid[nPos] = OH;
- m_nNextChar = EX;
- CClientDC dc (this);
- DrawO (&dc, nPos);
- CheckForGameOver ();
- }
- //左键双击边框重新开始
- //dc.GetPixel (Point point)获取当前光标下像素颜色判断与黑色匹配
- void CMainWindow::OnLButtonDblClk (UINT nFlags, CPoint point)
- {
- CClientDC dc (this);
- if (dc.GetPixel (point) == RGB (, , ))
- ResetGame ();
- }
- //判定鼠标是否点进矩形某一区域,点进返回区域编号,没有返回-1
- //此处用了一个rect.PtInRect(Point point)函数帮助判定
- int CMainWindow::GetRectID (CPoint point)
- {
- for (int i=; i<; i++) {
- if (m_rcSquares[i].PtInRect (point))
- return i;
- }
- return -;
- }
- //画上棋盘并画上圈和叉
- void CMainWindow::DrawBoard (CDC* pDC)
- {
- //画上棋盘
- CPen pen (PS_SOLID, , RGB (, , ));
- CPen* pOldPen = pDC->SelectObject (&pen);
- pDC->MoveTo (, );
- pDC->LineTo (, );
- pDC->MoveTo (, );
- pDC->LineTo (, );
- pDC->MoveTo (, );
- pDC->LineTo (, );
- pDC->MoveTo (, );
- pDC->LineTo (, );
- //画上叉和圈
- for (int i=; i<; i++) {
- if (m_nGameGrid[i] == EX)
- DrawX (pDC, i);
- else if (m_nGameGrid[i] == OH)
- DrawO (pDC, i);
- }
- pDC->SelectObject (pOldPen);
- }
- //画叉函数
- void CMainWindow::DrawX (CDC* pDC, int nPos)
- {
- CPen pen (PS_SOLID, , RGB (, , ));//宽为16像素的红笔
- CPen* pOldPen = pDC->SelectObject (&pen);
- CRect rect = m_rcSquares[nPos];
- rect.DeflateRect (, );//把矩形每个方向都缩进16个像素作为线条边框
- pDC->MoveTo (rect.left, rect.top);
- pDC->LineTo (rect.right, rect.bottom);
- pDC->MoveTo (rect.left, rect.bottom);
- pDC->LineTo (rect.right, rect.top);
- pDC->SelectObject (pOldPen);
- }
- //画圈函数
- void CMainWindow::DrawO (CDC* pDC, int nPos)
- {
- CPen pen (PS_SOLID, , RGB (, , ));//宽为16像素的红笔
- CPen* pOldPen = pDC->SelectObject (&pen);
- pDC->SelectStockObject (NULL_BRUSH); //空画刷是为了防止画出的圆内部出现白色遮住背景
- CRect rect = m_rcSquares[nPos];
- rect.DeflateRect (, );//把矩形每个方向都缩进16个像素作为圆的边框
- pDC->Ellipse (rect);
- pDC->SelectObject (pOldPen);
- }
- //电脑画图
- void CMainWindow::CpDraw(CDC* pDC)
- {
- int grades[][];
- int m,i,max=;
- int u;
- for(m=;m<;m++)
- {
- grades[][m]=;
- grades[][m]=;
- if(m_nGameGrid[m]==)
- {
- for(i=;i<;i++)
- {
- //计算玩家在空棋格上的获胜分数
- if(ptab[m][i] && win[][i]!=)
- {
- switch(win[][i])
- {
- case :
- grades[][m]+=;
- break;
- case :
- grades[][m]+=;
- break;
- case :
- grades[][m]+=;
- break;
- }
- }
- //计算计算机在空格上的获胜分数
- if(ctab[m][i] && win[][i]!=)
- {
- switch(win[][i])
- {
- case :
- grades[][m]+=;
- break;
- case :
- grades[][m]+=;
- break;
- case :
- grades[][m]+=;
- break;
- }
- }
- }
- if(max==)u=m;
- if(grades[][m]>max){
- max=grades[][m];
- u=m;
- }
- else if(grades[][m]==max){
- if(grades[][m]>grades[][u])u=m;
- }
- if(grades[][m]>max){
- max=grades[][m];
- u=m;
- }
- else if(grades[][m]==max){
- if(grades[][m]>grades[][u])u=m;
- }
- }
- }
- //标记已下并改变下一个点击状态
- m_nGameGrid[u]=OH;
- m_nNextChar = EX;
- //画上图
- DrawO(pDC,u);
- //后续改变胜利表和各人、机各胜利组合的棋子数
- for(i=;i<;i++){
- if(ctab[u][i]){
- win[][i]++;
- ptab[u][i]=false;
- win[][i]=;
- }
- }
- }
- //响应胜利结束的函数
- bool CMainWindow::CheckForGameOver ()
- {
- int nWinner;
- //通过调用IsWinner ()函数获取谁获胜;并用MessageBox输出胜利消息;响应OK后重开一局
- //==Message(CString,_T(标题),类型)
- if (nWinner = IsWinner ()) {
- CString string = (nWinner == EX) ?
- _T ("X wins!") : _T ("O wins!");
- MessageBox (string, _T ("Game Over"), MB_ICONEXCLAMATION | MB_OK);
- ResetGame ();
- return ;
- }
- //通过IsDraw ()函数判断是否平局
- else if (IsDraw ()) {
- MessageBox (_T ("It's a draw!"), _T ("Game Over"),
- MB_ICONEXCLAMATION | MB_OK);
- ResetGame ();
- return ;
- }
- return ;
- }
- //判断输赢EX左胜;OH右胜;0没有胜
- int CMainWindow::IsWinner ()
- {
- //用静态数组存储获胜组合
- static int nPattern[][] = {
- , , ,
- , , ,
- , , ,
- , , ,
- , , ,
- , , ,
- , , ,
- , ,
- };
- for (int i=; i<; i++) {
- if ((m_nGameGrid[nPattern[i][]] == EX) &&
- (m_nGameGrid[nPattern[i][]] == EX) &&
- (m_nGameGrid[nPattern[i][]] == EX))
- return EX;
- if ((m_nGameGrid[nPattern[i][]] == OH) &&
- (m_nGameGrid[nPattern[i][]] == OH) &&
- (m_nGameGrid[nPattern[i][]] == OH))
- return OH;
- }
- return ;
- }
- //判断是否平局函数
- BOOL CMainWindow::IsDraw ()
- {
- for (int i=; i<; i++) {
- if (m_nGameGrid[i] == )
- return FALSE;
- }
- return TRUE;
- }
- //初始化游戏
- void CMainWindow::InitGame()
- {
- int i,k;
- int count=;
- //设定玩家与计算机在各个获胜组合中的棋子数
- for(i=;i<;i++)
- {
- win[][i]=;
- win[][i]=;
- }
- //初始化棋盘状态
- ::ZeroMemory (m_nGameGrid,*sizeof(int));
- memset(ctab,,sizeof(ctab));
- memset(ptab,,sizeof(ptab));
- //设定水平方向的获胜组合
- for(i=;i<=;i+=)
- {
- for(k=;k<;k++)//3个棋子1个获胜组合
- {
- ptab[i+k][count]=true;
- ctab[i+k][count]=true;
- }
- count++;
- }
- //设定垂直方向的获胜组合
- for(k=;k<;k++)
- {
- for(i=;i<=;i+=)//3个棋子1个获胜组合
- {
- ptab[i+k][count]=true;
- ctab[i+k][count]=true;
- }
- count++;
- }
- //设定对角线方向上的获胜组合
- for(i=;i<=;i+=){
- ptab[i][count]=true;
- ctab[i][count]=true;
- }count++;
- for(i=;i<=;i+=){
- ptab[i][count]=true;
- ctab[i][count]=true;
- }
- srand(unsigned(time(NULL)));
- m_nNextChar = EX;//玩家先走
- }
- //重新开始初始化
- void CMainWindow::ResetGame ()
- {
- InitGame();
- Invalidate (); //使控件的整个图面无效并导致重绘控件
- }
[游戏学习22] MFC 井字棋 双人对战的更多相关文章
- 井字棋游戏升级版 - TopTicTacToe项目 简介
一.游戏简介 井字棋是一款世界闻名的游戏,不用我说,你一定知道它的游戏规则. 这款游戏简单易学,玩起来很有意思,不过已经证明出这款游戏如果两个玩家都足够聪明的话, 是很容易无法分出胜负的,即我们得到的 ...
- 强化学习实战 | 表格型Q-Learning玩井字棋(四)游戏时间
在 强化学习实战 | 表格型Q-Learning玩井字棋(三)优化,优化 中,我们经过优化和训练,得到了一个还不错的Q表格,这一节我们将用pygame实现一个有人机对战,机机对战和作弊功能的井字棋游戏 ...
- 强化学习实战 | 自定义Gym环境之井字棋
在文章 强化学习实战 | 自定义Gym环境 中 ,我们了解了一个简单的环境应该如何定义,并使用 print 简单地呈现了环境.在本文中,我们将学习自定义一个稍微复杂一点的环境--井字棋.回想一下井字棋 ...
- python 游戏(井字棋)
1. 游戏思路和流程图 实现功能,现实生活中的井字棋玩法 游戏流程图 2. 使用模块和游戏提示 import random def game_info(): print('欢迎来到井字棋游戏') pr ...
- C++井字棋游戏,DOS界面版
据说有一个能保证不败的算法.明天看看先再写个PVC版的. 正题.今天无聊写了个井字棋游戏,顺便逐渐让自己习惯良好的代码风格,放上来给新手学习学习. jzq2.cpp /* N字棋游戏PVP版,DOS版 ...
- 井字棋小游戏(C语言)
最近沉迷于<NetHack>.<DCSS>等字符游戏,对其很感兴趣,于是用C语言写了个字符界面的井字棋小游戏.欢迎大家指教. 编写时遇到了一些问题,我原先准备用循环,直到读取到 ...
- 强化学习实战 | 表格型Q-Learning玩井字棋(一)
在 强化学习实战 | 自定义Gym环境之井子棋 中,我们构建了一个井字棋环境,并进行了测试.接下来我们可以使用各种强化学习方法训练agent出棋,其中比较简单的是Q学习,Q即Q(S, a),是状态动作 ...
- 强化学习实战 | 表格型Q-Learning玩井字棋(二)
在 强化学习实战 | 表格型Q-Learning玩井字棋(一)中,我们构建了以Game() 和 Agent() 类为基础的框架,本篇我们要让agent不断对弈,维护Q表格,提升棋力.那么我们先来盘算一 ...
- [CareerCup] 17.2 Tic Tac Toe 井字棋游戏
17.2 Design an algorithm to figure out if someone has won a game oftic-tac-toe. 这道题让我们判断玩家是否能赢井字棋游戏, ...
随机推荐
- 无法连接到已配置的开发web服务器
http://jingyan.baidu.com/article/29697b91099847ab20de3c8b.html 这是防火墙造成的,将防火墙关闭即可
- Java设计模式——装饰者模式
JAVA 设计模式 装饰者模式 用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式 ...
- 实现MFC菜单画笔画圆,并且打钩
这个是用最简单的方法,移动客户区,圆会不见,所以下一篇我还要改进. 首先新建一个MFC单文件,在资源那里的菜单下,建立画笔,可以弹出红画笔,蓝画笔和绿画笔,,给出ID_RED,ID_BLUE,ID_G ...
- Echarts tooltip文字没有左对齐
tooltip : { trigger: 'axis', axisPointer : { // 坐标轴指示器,坐标轴触发有效 type : 'shadow' // 默认为直线,可选为:'line' | ...
- 【python3】collections系列介绍
文章来源:http://www.jb51.net/article/48771.htm (http://www.cnblogs.com/wushank/p/5122786.html) 修改人:天马流行拳 ...
- Fedora20安装fcitx输入法
Fedora20安装fcitx输入法 Fedora20默认安装的是ibus输入法,总有一些原因让我们选择fcitx输入法: ibus出词顺序有bug 在输入人名的时候,有些名字输入两三次后还是不会出现 ...
- stm32 MDK5软件仿真之查看io口输出
软件MDK5 stm32的pack 打开MDK,添加工程 一.首先找到Project的Options选项,里面的Debug选为Use Simulator,也就是选择软件仿真. 然后再Logic ...
- C#版SQLHelper.cs类
using System; using System.Data; using System.Xml; using System.Data.SqlClient; using System.Collect ...
- Scrum会议10.19
Scrum会议 组名称:好好学习 项目名称:记账本 参会成员:林莉(Master)胡丽娜 宫丽君 汪东涵 时间:2016.10.19 已完成内容: 1.完成新项目的查找,查找学姐的代码和项目. 2.理 ...
- python py生成为pyc文件
生成单个pyc文件 python就是个好东西,它提供了内置的类库来实现把py文件编译为pyc文件,这个模块就是 py_compile 模块. 使用方法非常简单,如下所示,直接在idle中,就可以把一个 ...