跳棋游戏设计与开发

摘  要

网络休闲游戏可以包含任何游戏类型的游戏性要素,通常休闲游戏的游戏规则相对简单,无需特别的技巧,也不需要较长的时间就能完成游戏,并且经常出现在不同的平台上,包括家用游戏机、掌上游戏机和个人电脑上等等,因此休闲类游戏发展迅速,尤其是棋类游戏,譬如广泛流行的围棋,象棋,跳棋就深受广大用户的喜爱。

本文就是在这种背景下开发的一款基于 VC++6.0网络跳棋游戏。主要是使用C++语言完成网络跳棋中的图形界面和网络对弈的设计以及计算机的智能行走,能够实现跳棋游戏中棋盘、棋子的绘制,鼠标执子和落子,下棋规则的定义,输赢的判断,网络传输命令的定义、发送、和接收等功能。

通过该款游戏,用户可以单机进行人机器对奕,也可以进行多人(最少2,最多六人)网络对奕,从而满足休闲的需求。

关键词:跳棋;图形界面;网络传输;人工智能

ABSTRACT

Network casual games could contain any type of game play elements, rules of casual games are usually relatively simple , without special skills, or taking more time to complete the game. Casual games often appear on different platforms, including home video game consoles, handheld game consoles and personal computers and so on. Casual games have developed rapidly , especially chess games, go chess, chess, checkers which are broadly popular have been loved by the majority of users.

  This article contains a Checkers game network which based on VC + +6.0. It mainly use C + + language to complete the graphical network interface ,network playing design and the intelligent walking of the computer checkers game ,can achieve the poltting of chessboard and pieces , hdlding and putting the pieces of using the mouse, the definition of the rules of chess, the judgment of winning or losing ,the definition of network transmission order, sending, and receiving functions.

  By the game , the user can play with  machine to people by stand-alone, and can aslo  play with more persons (at least two persons , maximum of six persons) , than it can meet the demand for leisure.

Key words: Checkers; Graphical interface; Network transmission; Artificial intelligence

目  录

摘  要

Abstract

1 引言…………………………………………………………………………………...1

1.1 题目背景..…………………………………………………………………………….1

1.2 落子规则及胜负判断………………………………………………………………...1

1.3 系统设计目标…………………………………………………….…………………..2

1.4 开发环境及运行环境………………………………………………………………...2

   1.4.1 开发环境……………………………………………………………………….2

    1.4.2 运行环境………………………………………………………………………2

2 需求分析……………………………………………………………………3

2.1 可行性分析…………………………………………………………………………....3

2.2 系统的功能……………………………………………………………………………3

3 系统设计……………………………………………………………………6

3.1 系统的整体设计………………………………………………………………………6

3.1.1 系统设计方案………………………………………………………………….6

3.1.2 游戏界面的概要设计………………………………………………………….6

3.1.3 单机人与人游戏的概要设计………………………………………………….7

3.1.4 人机对弈的概要设计………………………………………………………….8

3.1.5 网络对弈的概要设计………………………………………………………….8

3.2 游戏界面的详细设计………………………………………………………………...9

3.2.1 棋盘的数据结构……………………………………………………………….9

3.2.2 棋子的数据结构……………………………………………………………...10

3.2.3 游戏界面的具体设计………………………………………………………...11

3.3 单机人与人游戏详细设计……………………………………..………………..14

3.3.1 鼠标左键操作设计……………………………………………………….......16

3.3.1 鼠标右键操作设计……………………………………………………….......17

3.3.3 人与人对弈的流程…………………………………………………………...17

3.4 人机对弈的详细设计……….………………………………………………………18

3.5 网络对弈的详细设计…………………………………………………………….....21

4 系统实现…………………………………………………………………..24

4.1 游戏界面的实现…………………………………………………………………….24

4.2 单机人与人游戏的实现………………………………………………….…………31

4.3 人机对弈的实现…………………………………………………………..…………35

4.4 网络对弈的实现…………………………………………………………………….37

5 结论与展望………………………………………………………….……41

5.1 总结………………………………………………………….………………………41

5.2 展望………………….………………………………………..……………………..41

参考文献……………………………………………………………………………42

致谢…………………………………………………………………………………43

  • 引言
    1. 题目背景

随计算机科学与技术的发展,电脑游戏的开发占据当今计算机领域的一大块,特别是棋牌类游戏,因其容易上手历史比较悠久在联众、QQ等大型游戏网站都受到不用年龄段的玩家喜爱。至今计算机程序员们对棋牌类游戏的设计有深有浅,特别是人机对弈这一块更是现在计算机界需积极开发的领域。本次设计的内容是网络跳棋,跳棋在我国是一项老少皆宜、流传广泛的益智型棋类游戏。由于其规则简单,一玩就懂一辈子都不会忘,所以几乎每个人从小到大都下过跳棋。而目前程序员们对尤其象棋开发的比较深,而跳棋就相对较浅。虽然跳棋游戏的开发和设计也取得了一定得成果,但是当前网络上流传的跳棋游戏功能并不尽善尽美,还有很多方面需要在游戏版本中得到改进,其中最主要的问题就是人机对战和网络对战不能够一起实现,基于玩家对网络游戏的要求越来越高,本人所要设计的跳棋系统将取长补短,根据不同玩家的实际需求,完善游戏中画面、游戏设置等主要问题,在网络游戏流行趋势稳步提升的网络化时代,本次设计对现存的网络跳棋游戏具有实际指导意义。综上所述的一系列要素考虑到C++语言的优越性,所以我准备基于C++语言开发一个既能够人机对战,又能够进行网络对战的网络跳棋游戏。

    1. 落子规则及胜负判断

要开发跳棋电脑游戏必需先了解跳棋的具体落子规则以及胜负判断。跳棋的落子规则和胜负判断如下:

游戏人数:2,3,4,6人;

你的营区:起初你 10 颗棋子的放置位置,是一个正三角形;

你的目标:你的 10 颗棋子要离乡别井,连走带跳奔向对家,完全占领正对面的营区;

入营:棋子进入目标营区;

走子:只能一步走,棋子可六个方向走动,每步只能达到相邻的空位上;

跳子:可以连续跳;

一子跳棋规则:隔一个棋子可以跳;

空跳棋规则:像跷跷板一样,一个棋子在中间,两旁有相等的空位就可以跳到对称的位置上。

胜负判断:当某一方的10颗棋子先全部跳到对方的营区里则该方获胜。

1.3系统设计目标

本次游戏系统设计目标如下:

1. 实现跳棋棋盘的布局 ;

2.根据跳棋规则实现玩家棋子的行走;

3.运用一定的AI知识实现计算机棋子的智能行走;

4. 具有双人在线网络对弈功能;

5.具有友好的界面。

1.4开发环境及运行环境

1.4.1开发环境

  1. Intel® Pentium® 4 2.0GHz,512M内存,80G硬盘
  2. Microsoft® Windows™ XP Professional
  3. Microsoft® Visual C++ 6.0
  4. Microsoft® Developer Network for Visual Studio.NET 2003
  5. Visual Assist X 10.1.1301.0

1.4.2运行环境

  1. Intel® Pentium® 2及以上处理器,32M以上内存,4G以上硬盘
  2. Microsoft® Windows™ XP操作系统
  3. 800*600或以上的屏幕分辨率

  • 需求分析

2.1 可行性分析

本系统是是C++来编写的,界面是用其MFC来进行开发的的。使用 MFC 的最大优点是它为你做了所有最难做的事。你所调用的很多成员函数完成了你自己可能很难完成的工作。从这点上讲,MFC 极大地加快了你的程序开发速度。由于MFC编程方法充分利用了面向对象技术的优点,它使得我们编程时极少需要关心对象方法的实现细节,同时类库中的各种对象的强大功能足以完成我们程序中的绝大部分所需功能,这使得应用程序中所需要编写的代码大为减少,有力地保证了程序的良好的可调试性。最后要指出的是MFC类库在提供的对象的各种属性和方法都是经过谨慎的编写和严格的测试,可靠性很高,这就保证了使用MFC类库不会影响程序的可靠性和正确性。网络对弈是由Socket编程来实现的,虽然对这快比较陌生,但有指导老师的指导和这方面的参考资料,因此可以基本实现,只是水平有限,功能可能被限制,不能做到“我能想到的,就能做到的”的程度,虽然不能尽善尽美,重点是为以后的对以后网络游戏设计打下了良好的基础。

2.2 系统的功能

游戏系统功能主要有以下几个:

1) 生成友好的跳棋游戏界面;

2) 实现具有1至6人可以玩跳棋游戏;

3) 人机对弈功能;

4) 双人网络对弈功能。

下面来看下系统功能的具体用例图:

实现在一台电脑上完成的人与人单机游戏,可以是2,4,6人一起玩不等,可以实现玩家的执子,跳子,落子以及输嬴的判断等一系列操作,具体用例图如下:

图2-1单机人与人游戏用例图

人机对弈即能运用人工智能的知识实现计算机智能行走,判断输嬴功能具体用例图如下:

图2-2人机对弈游戏用例图

网络对弈即能建立主机客户机的连接,网络命令的传输,实现进行双人网络对战功能,具体用例图如下:

图2-3双人网络对弈游戏用例图

  • 系统设计

3.1 系统的整体设计

3.1.1系统设计方案

跳棋程序设计主要分为游戏界面、玩家下棋、人机对弈以及网络对战等方面的设计。通过Visual C++ 6.0建立一个基于对话框的应用程序,将这个绘制了棋盘和棋子的对话框作为游戏的平台。通过鼠标左键和右键的单击或移动操作实现拾子、移子、执子、落子。对于网络对战按先前定义好形式的字符串,作为网络传输和将要接收和执行的命令。因本款游戏分为单机和网络游戏两种游戏模式,那就要涉及到网络连接方面。在VC++中,可以抛开复杂的网络协议进行网络开发,除了进行使用Windows Socket API进行开发之外,还可以使用MFC中的CasyncSocket和CSocket进行开发,本次程序开发使用的是Csocket。下图为整个游戏交互图:

图3-1 游戏交互图

3.1.2游戏界面的概要设计

要生成跳棋游戏的初始化界面首先要明白棋盘和棋子的基本设计思想。跳棋的棋盘从表面上看是一个六星棋盘,棋盘中可落子的位置均匀地分布在这个棋盘的内部和六个角上。这不像军棋、象棋或是围棋棋盘那样方方正正,可方便地用一个数组结构来表示可以落子的点。因此,跳棋棋盘的设计有一定的难度。这里利用一个动态数组来保存可走位置表示这里是否可以落子。当然,这里提到的可落子的位置是在程序中是可落子位置的逻辑位置,而不是落子位置的物理位置。确定了所有可落子的位置后,利用绘图函数,将这个棋盘绘制出来。

有了棋盘,没有可以操作的棋子,也不能进行正常的对弈。起初设计时,试想了以一个圆来表示棋子,填充以不同的颜色来区分不同游戏者的棋子。当需要实现棋子的移动时,则可以把棋子移动的原始位置用棋盘背景填充,而在棋子移到的目的位置绘画一个同样大小和同样颜色的圆。为了实现程序的美观性,这里采用了位图来表示棋子,通过对位图的操作来实现对棋子的操作。

再对游戏界面进行一系列初始化,使之生成友好的游戏界面。

3.1.3单机人与人游戏的概要设计

单机人与人游戏的设计主要是在游戏界面生成的基础上实现鼠标操作响应的设计。鼠标对棋子的操作主要是贴图与消息响应的运用。先要来看一下Windows应用程序、操作系统和计算机硬件之间的关系(如图3-2)。

   

图3-2 应用程序与操作系统和硬件系统的关系图

由上图可得当用户在某个程序活动时点击了鼠标,对事件作出反应的过程就是消息响应,所以鼠标对棋子和按钮操作主要是消息响应。

在本次设计中鼠标左键添加的消息响应主要是对执子和按钮响应,右键添加的消息响应主要是对落子的响应。方法是先对每个棋格或按钮等确定在它棋盘上的坐标,当鼠标光标移动到某一按钮(这里以按钮为例)的坐标范围内点击则成功,不在其坐标范围内则失败。对于棋子操作还需进行执棋和落子后的棋盘的重新绘制;

3.1.3人机对弈的概要设计

人机的对弈的实现主要是对计算机下子先做好一些事先设置,比如哪个棋子计算机可以进行操作,哪些位置计算机可以落子,能落到的所有位置进行比较,然后得出所有可下的位置中哪个位置是最优化的,进行好最优化的下子后要对当前所下的棋子进行原位置和现位置的更新。然后设置计算机下完后能自动判断输嬴的功能,这样就可以实现人机对弈了。这里主要的难点就是对计算机的最优化下子判断,第四章中将详细分析最优化下子方法。

3.1.5网络对弈概要设计

随着因特网的迅速流行,网络游戏对战在游戏开发中显得越来越重要,一台计算机供两个人玩始终是一件不爽的事情,为了实现网络对战先来看下面向连接协议的Socket编程。因为连接协议提供了一系列数据纠错功能,可以保证在网络上传输的数据及时、正确地到达。Socket的使用是基于CS系统的,客户机和服务器的Socket使用不太一样,分别有如下几个步骤,如图:

图3-3 网络对弈CS结构流程图

3.2游戏界面的详细设计

3.2.1棋盘的数据结构

本程序用的棋盘为六星跳棋棋盘,因为此棋盘为一个不规则的图形所以设计有点复杂,我的设计思路如下:

此棋盘共有121个棋格,把它分为7个区,其中中间为0区,上为1区,逆时针至右上角分别为2到6区。

定义一个棋格的属性:

根据棋盘用坐标量出棋格的中心坐标;

棋格的所属区域,即上面提到的7个区;

棋格离六方端点有几格,用一个6维数组来记录次棋格离6个方向的格数,这样电脑在下子时可作下子的优先判断;

棋格上棋子的颜色,因有六方所以棋子有六种颜色,但这里还要在考虑当棋格上无子的情况,因此棋格上棋子的颜色用数字0到6表示,其中0为无子;

当前棋格属于第几个棋格,这里以从上到下,从左到右来计算,分别为第1到121个棋格;

再定义一个6维指针数组,用它来指向当前棋格的6个方向的属性,6个方向以右上方为第0维至右方第5维,如果某方向无棋格则指向NULL,这主要是在1到6区里的棋格以及0区的七种棋格,它们的某方会无棋格;

下图为棋盘上的分区和维:

图3-4 棋盘的分区与维

3.2.2棋子的数据结构

棋子的数据结构相对与棋盘较简单,我的思路如下:

当前棋子所属的区域,初始化时主要是属于棋盘上的1到6区中的一个,行走时可到0区;

棋子的颜色,六方棋子六种颜色,分别用1到6表示;

棋子的优先级,可以根据棋盘中各个棋格距离六方的格数来判断当前棋子的优先级;

当前棋子指向第几个棋格;

当前棋子的六个方向上(即上面所述的六个维)分别为第几个棋格,若某方无棋格则指向NULL;

棋子的可走路径,可走路径用数组集合动态保存;

棋子的状态,棋子状态可分为选择该棋子与否,即UP或则DOWN;

这样棋盘和棋子的数据结构定义好了

3.2.3游戏界面的具体设计

因为本次设计是一个游戏,所以应该在界面上下点功夫,早就不是在DOS时代了,应该不会有人喜欢玩一个界面很难看的游戏。同时截面要简洁,功能也不必太多,但是基本功能却一定要做好,让用户玩个痛快。下图是界面:

图3-5 棋盘界面图

首先定义跳子棋各个棋子的属性类TzqDate,它的类图如下:

图3-6 TzqDate类图

nArea表示当前棋子所属的区域,即0到6区,0为中间区域,上为1区,逆时针至右上角分别为2到6区;

nPRI[6]为当前棋子各个方向上的优先级,上0,1到5逆时针至右上方; nColor表示当前棋子的颜色,1到6表六方棋子;

pTzq[6]表示当前棋子的各个方向是第几棋格, [0-5]逆时针,右上主第一方向为[0],若某方无棋格则指向NULL;

n_x,n_y为当前棋子所在棋格中心位置横坐标和纵坐标;

nQz表示当前棋子所在棋格为第几棋格。

接下来定义玩家10颗棋子的可走位置,类图如下:

图3-7 Cplace类图

pTzq表示棋子实例;

nPlaceArray这是个动态数组,表示当前棋子可走位置,可以动态的保存当前棋子的可走位置;

nTestUD表示棋子的状态,nTsetUD=0,则棋子状态为Down,nTsetUD不为0,则棋子状态为Up。

定义好这些后,就可以设计本次设计的核心类CTzq,它的类图如下:

        

图3-8 CTzq类图

PlayerAc当为网络对战时,该变量为0表示先走,1为后走;

nPlayType[6]表示六方玩家的状态,具体为当等于0时表示此方无玩家,不用初始化此方,当等于1时为玩家为人,当等于2时表示此方为计算机下子;

qzc,qz两个变量是六方棋子属性aPlace[6][10]的二维下标全局变量;

aTzqFull[122]数组为棋子可放的全部棋格 [1-121],[0]为空;

aPlace[6][10]表示六方玩家棋子的属性;

pPlace指针指向当前操作棋子;m_cwnd指针表示当前视指针;

nPlaySyst表示当前下子为第几方玩家;

BeginGame表示是否开始下子,true表示开始,flase表示没开始

定义好这些类后来看一下游戏界面生成的游戏模式对话框,本款游戏设计单机和网络对战两部分,而且涉及的玩家最多为6人,这里定一个游戏对话框类CnewGame,它主要是用于给用户进行选择。CnewGame中定义了六个玩家变量和一个游戏类型变量GameType,用来与对话框中RADIO进行数据交换。游戏界面生成还与涉及下面要介绍的类CTtqDlg中的成员函数OnPaint(),NewGame(),这里主要对单机的具体说明下网络的后面具体介绍,下面来看下具体游戏初始化流程图:

                    

图3-9游戏界面初始化流程图

3.3单机人与人游戏详细设计

人与人对弈是棋类游戏设计的最基本实现,当设计好游戏界面模块和游戏模式对话框后,就是本次设计的一个重点即单机人与人游戏设计中的鼠标操作响应的设计,主要分为左键操作设计和右键操作设计,这里要定义一个继CTzq后又一个重要类CTtqDlg,它的类图如下:

图3-10 CTtqDlg类图

   该类还中定义了CTzq的对象Tzq来表示整个游戏中所有棋子数据,Cplace的对象tempplace用来表示临时棋子,下面要讲到的网络控制类NetControl的指针 m_pControl;

Nqztempud用来表示当前临时棋子的状态,UP或则DOWN。

首先来看类CTtqDlg中两个较重要的两个成员函数CTtqDlg::OnPaint以及

CTtqDlg::WindowProc。

CTtqDlg::OnPaint:该函数的作用为绘制窗口,它涉及到对程序中所有贴图的加载以及棋子的绘制;

CTtqDlg::WindowProc:该函数是一个应用程序定义的函数。它处理发送给窗口的消息,WNDPROC类型定义了一个指向该回调函数的指针。它处理消息的具体流程图如下如下:

                       

图3-11 CTtqDlg::WindowProc消息响应图

接下来下看下鼠标左键操作和右键操作消息响应的流程图:

图3-12鼠标消息响应图

3.3.1鼠标左键操作设计

左键操作设计主要涉及CTtqDlg::OnLButtonDown和CTtqDlg::OnLButtonUp这两个函数。

先看比较重要的CTtqDlg::OnLButtonDown函数,一开始要对棋盘图上新游戏按钮进行绘制,然而它的设计主要是为判断是否点击棋子,若点击退出程序按钮即效果和上面的按下键Ese一样就跳出对话框,当点击棋子时,具体设计思路如下:

当为网络双人对战时,本方走完后点击对方玩家棋子无效。单机时若当前没确定要跳到某一可走棋格或则现在临时所选的棋子就是当前操作棋子(即没重新选择棋子)则可以重新选择。可从当前玩家的10颗棋子中任意选中某一颗,把它看作当前操作棋子同时即临时棋子,修改其状态为Up状态以及重绘当前操作棋子,当选中某一颗棋子后想重新选择也可以直接左键点击其他一颗,如果点击的是空棋格则返回重新选择。选好某一颗棋子后就可以修改其临时状态即this->nqztempud=1,这样你就可进行跳了。具体实现步骤如下:

当你选中某一颗棋子要跳到某一所选棋格,先定义一个BOOL型变量TempYn赋初值为FALSE,然后返回所选棋格为第几棋格赋给整型变量tempQZ,判断所选棋格是否为空,若不为空重新移动鼠标,若所选为空则判断当前棋子的六个方向的可走位置中是否有这个棋格,若有则TempYn==TRUE跳出for循环。如果要落到该棋格上的话就把该棋格的属性赋给当前操作棋子,当前棋格上的棋子颜色则就为该玩家方为第几玩家加1即Tzq.nPlaySyst+1。

CTtqDlg::OnLButtonUp:该函数主要是为了响应新游戏的确定,当玩家玩开始玩以及中途不想在本局中继续玩则随时可以点击新游戏按钮重新开始。                

3.3.2鼠标右键操作设计

鼠标右键操作为落子确定,成员函数为CTtqDlg::OnRButtonDown,它主要判断当前指到的棋格是否可走,这里主要思路如下:

若当前所指棋格不在可走位置中则点击无效,若鼠标指在除自己方区域或对方区域或中间区域外的其他四方玩家区域无效。若点击在有效区域的可走位置时则把当前跳到的棋格和原位置重新绘制,更改两棋格上棋子颜色,然后把当前棋格的全部属性赋给当前棋子。最后还要判断该棋子跳到后是否该方的全部棋子都已在对方的区域,若全部跳到则胜利。

3.3.3人与人对弈的流程

人与人对弈的基本设计只要能完成执子、落子操作就是上面鼠标操作中所提到左键操作设计和右键操作设计就能实现,它与人机对弈最大不同在于人机对弈中计算机会智能的行走,它会判断当前可走路径走哪一条是最优的,人与人对弈中都是要玩家自己思考,还有不同就是在于执子和落子操作需要人完成。所以到这步为止简单的跳棋游戏已经实现。下面以双人对弈为例看下具体对战流程:

图3-13单机人与人对战流程图

但这还不够完善,接下来这一节将要设计本次设计的一大难点人机对弈。

3.4人机对弈的详细设计

棋类游戏中人机对弈我想大家都不陌生,计算机能战胜世界顶级大师并不是计算机有多少聪明,这都人事先设计好了,鉴于人机对弈(即到了人工智能)是计算机领域的前沿技术,深不可测,所以我这里只设计了一步探索,下面来分析下人机对弈的主要算法。

要设计人机对弈的算法先先来看下上面所提到的CTzq::SetTF_nPRI(),该函数作用是计算出没个棋格距离自己方端点棋格和对方端点棋格的距离(这里的距离即包括端点棋格以及当前棋格在内它们之间一共有多少棋格)。弄清此函数的作用后基本的思想就有了,即当前棋子的可走路径中那一条路径是能最大幅度的让该棋子向对方领域跳,接下来看具体怎么实现。

首先当前某一方经过上述的CTzq::ChessFull()实现后就可以得到该方的每个棋子当前的可走位置,把可走位置全部保存到动态数组中。当轮到该方走时,计算机对当前的10颗棋子逐一判断,如何实现判断方法如下:

先定义一个整型变量zzr初始化赋值为-10(算法我们规定往对方领域跳为正,往自己方领域跳为负),这是因为当前该方的10颗棋子若某棋子已在对方领域若跳回自己方需要的最小幅度就是距离10个棋格,所以初始化为-10。然后在可走位置动态数组中取出一个可走位置,把它所在的棋格是第几棋的值格赋给整型变量x,即x=aPlace[nPlaySyst][zze].nPlaceArray[asd1]。再用当前棋子所在的棋格优先级减去可走位置所在的棋格的优先级就可以得出该棋子距端点的距离与该棋子可走位置距端点距离差,即this->aTzqFull[x].nPRI[nPlaySyst]减去aPlace[nPlaySyst][zze].pTzq->nPRI[nPlaySyst],得出的值与zzr比较,分两种情况如下:

若该值大于zzr,判断第x个棋格是否在对方区域或则在中间区域即0或则在自己区域,若其中之一成立就把当前棋子为该放第几颗棋子值赋给整型变量ttt,把可走位置为第几可走位置值赋给整型变量tt3以及暂时保存该棋格到tt2,把上面所得的距离差赋给zzr。就这样比较所有可走位置中最优的。

若该值等于zzr,则产生一个随机数crand,crand大于0则更新下,和上面操作相同,crand小于等于0则不变。上面主要判断操作好之后就是把当前棋子现在跳到的棋格颜色赋为该棋颜色,原位置颜色则变为无色,然后对棋格的重新绘制。

以上是计算机智能行走的设计思想,当然最后还要加上胜负判断,具体的实现方法和CTzq::renovate基本一样。

下图为人机对弈中计算机下子具体流程:

图3-14计算机下子流程图

3.5网络对弈的详细设计

在第二章中介绍了网络对弈基本设计思路,新建一个Csocket,但是为了更好地控制网络,这里新建一个类对它进行管理,此类为NetControl。具体定义如下:

图3-15 NetControl类图

其中m_Port为端口,m_Server为服务器名称,m_pList服务器为客户请求建立的Socket队列。其中还设置了CNet的指针m_pSocket和m_pListening表示请求连接和监听的标志。还设置了CTtqDlg 的指针m_pView。m_Ready[2]用来表示用户准备好与否,m_Isserver表示该方是否是主机。

上面的定义中要包含Net.h头文件,所以这里还使用了另一个类Cnet,其实这里所谓的Cnet就是Csocket,不同的是CNet被修改了,更适合此程序设计,它是从Csocket派生的。它在Csocket原有的基础上加入了对virtual void OnReceive(int nErrorCode),virtual void OnAccept(int nErrorCode)的从写,同时加入了NetControl的指针*m_Nc,并可以用NetControl的指针初始化Cnet。这样就可以直接把消息传入了NetControl进行具体响应,这在设计上方便了不少,不用再在Csocket里纠缠。在分析NetControl前,先建立主机设置和客户机设置对话框, OnSet()函数用来对主机设置。

这里要为主机对话框Edit Box设置一个CString类型变量m_Port,用它来记录游戏端口,修改它的默认值为100,并为OK按钮添加消息响应。

成员函数OnOpen()用来对客户机的设置,这里要为客户机的两个Edit Box设置分别为m_ip和m_Port,修改其默认值为127.0.0.1和100,并为OK按钮添加消息响应。现在来看下整个NetControl的消息流程:

图3-16消息流程图

现在来看对战双方是发送什么消息和怎么处理消息的,这里是用SendMsg(CNet *pSend,char *pMsg)函数来发送消息的,在具体程序实现中对SendMsg(CNet *pSend,char *pMsg)函数进行再次封装即SendAction(int x,int y,int action)函数,,使它更适合这个游戏,这样会更简单、更清晰。其中这里的x表示对方当前走的为第几颗棋子,y表示对方当前棋子跳到第几棋格,action表示是初始化消息还是走棋消息,1为初始化消息,4为走棋消息。当接受消息后用FetMsg(CNet *pRequest)函数来处理,先是对受到消息进行判断是初始化消息还是走棋,这里初始化消息和走棋消息分别用2005和2008做为标示, 并且在主机方设为先走客户机方设为后走,现在来看下网络对战的主客机连接和消息发送图,如下:

   

图3-17 网络对战应用程序图

到这里为止网络跳棋的基本设计已经完成,接下来一章来看一下系统具体实现以及运行结果。

  • 系统实现

4.1游戏界面的实现

第三章中已经详细的分析了游戏的截面初始化的设计过程,下面来主要看以下设计过程主要成员函数的具体实现。

设计游戏界面涉及到的几个初始化函数SetTF_nxy(),SetTF_nArea(),SetTF_nColor(),SetTF_nPRI(),SetTF_nQz(),SetTF_pTzq(),SetPlace()它们分别为初始化aTzqFull数组的 n_x和n_y,(n_x,n_y)为棋格中心坐标,nArea,nColor,nPRI,nQz,pTzq。CTzq::NewGame()初始化网络对战时主机的游戏界面。主要看下SetTF_nxy(),SetTF_nPRI(),SetTF_pTzq()的实现方法:

CTzq::SetTF_nxy():下面是该函数代码设计:

CTzq::SetTF_nxy()

{

int temp1[18]={1,2,4,7,11,24,36,47,57,66,76,87,99,112,116,119,

121,122};//前后两数之差为当前行的棋格数

int temp2[18]={0,220,204,188,172,28,44,60,76,92,76,60,44,28,

172,188,204,220};//横坐标变换

int tab,tae=1;

int tyt=30,txt;

for(int tj=1;tj<18;tj++)//tj为一共17行棋格

{

tab=tae;

tae=temp1[tj];

txt=temp2[tj];

for(int tj1=tab;tj1<tae;tj1++)

{

this->aTzqFull[tj1].n_x=txt;

this->aTzqFull[tj1].n_y=tyt;

txt=txt+32;//左右两棋格中心位子横坐标之差为32

}

tyt=(int)(tj*27.5)+30;//上下两行棋格中心位子纵坐标之差为tj*27.5+30

}

}

CTzq::SetTF_nPRI():此函数对计算机下子做判断极其重要,其设计思想为分别计算出当前棋格中棋子与对方端点棋子之间共多少个棋格以及与自己方端点棋格之间共有多少棋格(这里说明每行中棋格只能算一次),为考虑算法的效率,我们可以计算出某一方数据后即可计算出对方此数据,具体实现如下:

CTzq::SetTF_nPRI()

{

TzqDate *pTzqDate;

TzqDate *pTzqTemp;

int ntemp[6]={1,11,99,121,111,23};//六方棋子顶点棋子属于第几棋子

for(int i=0;i<3;i++)

{

pTzqDate=&aTzqFull[ntemp[i]];

for(int ni=1;ni<14;ni++)

{

pTzqTemp=pTzqDate;

pTzqTemp->nPRI[i]=ni;

pTzqTemp->nPRI[i+3]=18-ni;//当前棋格中棋子与对方端点棋子之间共18-ni个棋格

while(pTzqTemp->pTzq[i+2])//同行上左边的棋子

{

pTzqTemp=pTzqTemp->pTzq[i+2];

pTzqTemp->nPRI[i]=ni;

pTzqTemp->nPRI[i+3]=18-ni;

}

pTzqTemp=pTzqDate;

pTzqTemp->nPRI[i]=ni;

pTzqTemp->nPRI[i+3]=18-ni;

while(pTzqTemp->pTzq[(i+5)%6])//同行上右边的棋子

{

pTzqTemp=pTzqTemp->pTzq[(i+5)%6];

pTzqTemp->nPRI[i]=ni;

pTzqTemp->nPRI[i+3]=18-ni;

}

pTzqDate=pTzqDate->pTzq[i+3];

}

pTzqDate=&aTzqFull[ntemp[i+3]];//上面对方的棋子优先级

对方棋子的优先级实现方法同上。

CTzq::SetTF_pTzq():该函数初始化aTzqFull数组 pTzq,最重要的就是计算出每个棋格的周边六个方向分别是第几个棋格,可以用一个temp[6][121]数组来记录六方,这里以每个棋格的右上方为例如下:

0,                     

1,0,                   

2,3,0,                  

4,5,6,0,

0,0,0,0,7,8,9,10,0,0,0,0,0,

12,13,14,15,16,17,18,19,20,21,22,23,

25,26,27,28,29,30,31,32,33,34,35,

37,38,39,40,41,42,43,44,45,46,

48,49,50,51,52,53,54,55,56,

57,58,59,60,61,62,63,64,65,0,

66,67,68,69,70,71,72,73,74,75,0,

76,77,78,79,80,81,82,83,84,85,86,0,

87,88,89,90,91,92,93,94,95,96,97,98,0,

104,105,106,107,

113,114,115,

117,118,

120

定义好某一个方向后其他五个方向就比较简单了;

接下来比较重要的是CTzq::Chess,CTzq::ChessOdd,CTzq::Addpa三个函数,把它们放在一起,因它们之间有相互调用成一个循环递归,CTzq::Chess是加当前棋子的六个方向到aPlace,它调用了CTzq::ChessOdd。CTzq::ChessOdd,它的实现如下:

void CTzq::ChessOdd(TzqDate *pTF, int i1)

 {

int it,ht,ii;

TzqDate *pb;

pb=pTF;

ii=0;

it=0;

ht=0;

//有空格ii++,遇到棋子it=1;当it=1时进入else,当空格时ii--,直到最后可以跳到的位置

while(pb->pTzq[i1])

{

pb=pb->pTzq[i1];

if(it==0)

{

if(pb->nColor==0)//0为无棋,即为空格

ii++;

else

it=1;

}

else

{

if(ii==0)

{

if(pb->nColor==0)

{

Addpa(pb);//加上一个棋子

return ;

}

else

return ;

}

else

{

if(pb->nColor==0)

ii--;

else

return ;

}

}

}

}

它调用了CTzq::Addpa。CTzq::Addpa,它调用了CTzq::Chess,求出所有可以走的步。这里的实现主要是CTzq::ChessOdd设计有点难度,但只要参考跳棋的行走规则就比较容易理解;

ChessFull()表示把所有可下的地方加到aPlace,这里需要定义一个整形链表yjj动态的保存可下棋子的位置,这里要提到一点是先把当前棋子的颜色设为空表示当前棋子位置也是可下棋子位置之一,即玩家执棋后又可以落到自己的位置;

CTzq::StepPath函数的实现与上面重点讲到的CTzq::ChessOdd函数基本一样,ChessOdd里是当该棋格可下则加到可走路径动态队列中,StepPath是当前棋子跳到的地方是否是可下棋格,可下返回true,不可下返回false;

GameStep()为对下下一步棋子的玩家选择,BsetSide(TzqDate *dp)为添加临近六个方向可下的棋格到aPlace,qzsetel(CPoint point)为返回所选棋格,renovate(TzqDate *t1,TzqDate * t2)为网络下子后更新视图;

有了上面的一系列重要的函数实现后,下面我们来看下2,4,6人游戏的界面初始化图:

图4-1二人游戏图

图4-2四人游戏图

图4-3六人游戏图

4.2单机人与人游戏的实现

在上面的详细设计中已经对鼠标左右键进行了具体思路的设计,下面主要是左键消息响应中的OnLButtonDown(UINT nFlags, CPoint point)实现核心代码:

if(Tzq.nPlayType[Tzq.nPlaySyst]==1)//棋子图片边长为18,nPlayType为1时,是人下棋

{

if(this->nqztempud==0||tempplace.pTzq==Tzq.pPlace->pTzq)

{

for(int i=0;i<10;i++)

{

rgn=CRect(

Tzq.aPlace[Tzq.nPlaySyst][i].pTzq->n_x-9,

Tzq.aPlace[Tzq.nPlaySyst][i].pTzq->n_y-9,

Tzq.aPlace[Tzq.nPlaySyst][i].pTzq->n_x+9,

Tzq.aPlace[Tzq.nPlaySyst][i].pTzq->n_y+9);

if(rgn.PtInRect(point))

{

if(Tzq.pPlace!=NULL){

Tzq.pPlace->nTestUD=0;//棋子状态为down    

this->InvalidateRect(//该函数向指定的窗体添加一个矩形,然后窗口客户区域的这一部分将被重新绘制

CRect(

Tzq.pPlace->pTzq->n_x-15,

Tzq.pPlace->pTzq->n_y-15,

Tzq.pPlace->pTzq->n_x+17,

Tzq.pPlace->pTzq->n_y+17),0);

}

Tzq.pPlace=&Tzq.aPlace[Tzq.nPlaySyst][i];

                 which=i;

this->tempplace.pTzq=Tzq.pPlace->pTzq;

TM=TRUE;

Tzq.pPlace->nTestUD=1;

this->InvalidateRect(

CRect(

Tzq.pPlace->pTzq->n_x-15,

Tzq.pPlace->pTzq->n_y-15,

Tzq.pPlace->pTzq->n_x+17,

Tzq.pPlace->pTzq->n_y+17),0);

}

}

if(Tzq.pPlace==NULL) return;

this->nqztempud=1;

}

if(this->nqztempud!=0)//选中临时棋子

{

BOOL TempYn=FALSE;

int tempQZ=Tzq.qzsetel(point);

if(Tzq.aTzqFull[tempQZ].nColor==0)

for(int it=0;it<6;it++)

{

TempYn=Tzq.StepPath(Tzq.pPlace,&Tzq.aTzqFull[tempQZ],it);

if(TempYn==TRUE)

break;

}

if(TempYn==TRUE) //刷新视

{

Tzq.pPlace->pTzq->nColor=0;

this->InvalidateRect(

CRect(Tzq.pPlace->pTzq->n_x-15,

Tzq.pPlace->pTzq->n_y-15,

Tzq.pPlace->pTzq->n_x+17,

Tzq.pPlace->pTzq->n_y+17),0);

Tzq.pPlace->pTzq=&Tzq.aTzqFull[tempQZ];

Tzq.pPlace->pTzq->nColor=Tzq.nPlaySyst+1;

this->InvalidateRect(

CRect(Tzq.pPlace->pTzq->n_x-15,

Tzq.pPlace->pTzq->n_y-15,

Tzq.pPlace->pTzq->n_x+17,

Tzq.pPlace->pTzq->n_y+17),0);

}

}

左键消息响应中的OnLButtonUp(UINT nFlags, CPoint point)函数和右键消息响应OnRButtonDown(UINT nFlags, CPoint point)可以参考人机对弈算法中的实现。下面来看下左键点击跳出的主要对话框,左键点击新游戏按钮跳出对话框如下:

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

等天晴i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值