[游戏学习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. 这道题让我们判断玩家是否能赢井字棋游戏, ...
随机推荐
- ASP.NET MVC应用程序执行过程分析
ASP.NET MVC应用程序执行过程分析 2009-08-14 17:57 朱先忠 朱先忠的博客 字号:T | T ASP.NET MVC框架提供了支持Visual Studio的工程模板.本文 ...
- IE调试网页之四:F12 开发人员工具控制台错误消息 (Windows)(转载)
F12 工具控制台可以报告在运行时期间发生的错误和信息消息.本文将介绍错误消息,并提供有关如何修正这些错误的建议. 简介 F12 工具控制台错误消息提供了一些代码(例如,SEC7111 或 HTML1 ...
- 前端---HTML
HTML基础 本章内容: 简介 HTML定义 标签定义和属性 HTML5基本结构 HTML5字符集 <head>标签 <title> <base/> <lin ...
- WinForm窗体PropertyGrid控件的使用
使用过 Microsoft Visual Basic 或 Microsoft Visual Studio .NET的朋友,一定使用过属性浏览器来浏览.查看或编辑一个或多个对象的属性..NET 框架 P ...
- React Developer Tools 安装小提示
1,在google市场里边,安装React Developer Tools之后,发现是开启的,但是按下F12后,并没有发现react选项 2,后来通过查资料,发现必须是运行react项目的时候,才出现 ...
- Daily Scrum 12.9
今日完成任务: 修复了提交回答,自动消除换行符,导致文本显示混乱的BUG.解决个人信息修改界面中,问题显示顺序不对的BUG.基本完成数据库接口webservice工作.但引入的异常还未修复. 遇到困难 ...
- PoEdu - C++阶段班- Lesson02_C to C++
1 原生bool类型 c++里面的bool类型才是真正原生的true和faul,比如常见的大写的"BOOL",它就不是原生的. 原生的与非原生的bool,它们的区别: 详细说下原 ...
- 用c语言写的简单计算器
这是自己在学习C语言,凭借自己的兴趣,将课本的知识运用后整理的关于C语言计算器的代码.计算器实现的功能有:加.减.乘.除.求余.功能简单,但对于初学者的我来说能把它写出来,排除每个错误依旧是个难题.前 ...
- 黑马程序员_Java基础:十进制转换其他进制
------- android培训.java培训.期待与您交流! ---------- 平时使用中,进制转换只要使用Integer这个包装类中的方法即可完成. 但其实我们也能用自己的方法去实现,这有助 ...
- 天气预报数据API
http://www.weather.com.cn/data/cityinfo/101010100.html//过期: http://api.36wu.com/Weather/GetMoreWeath ...