三国杀C++语言编写.docx
《三国杀C++语言编写.docx》由会员分享,可在线阅读,更多相关《三国杀C++语言编写.docx(16页珍藏版)》请在冰豆网上搜索。
三国杀C++语言编写
软考软件工程师教程之用C#编写三国杀
(1)
来源:
考试百科更新时间:
2011-1-249:
18:
17【点击进入论坛】
【摘要】本文有考试百科为你提供,本文主要介绍软考软件工程师教程之用C#编写三国杀,最近做翻译做的头疼,疼过之余,想想之前公司内组织的三国杀开发兴趣小组在三国杀开发问题上几乎又停滞了。
于是又翻…
本文有考试百科为你提供,本文主要介绍软考软件工程师教程之用C#编写三国杀,最近做翻译做的头疼,疼过之余,想想之前公司内组织的三国杀开发兴趣小组在三国杀开发问题上几乎又停滞了。
于是又翻出来搞了搞,这次却搞的有点像模像样了,特地把思路和方法都共享出来,一起学习,下面是本文主要内容:
最近做翻译做的头疼,疼过之余,想想之前公司内组织的三国杀开发兴趣小组在三国杀开发问题上几乎又停滞了。
于是又翻出来搞了搞,这次却搞的有点像模像样了,特地把思路和方法都共享出来,一起学习。
今天要说的是实现的第一步。
为了尽量简化需求,我们最初要实现的东西也挺简单,就是能支持两个玩家自动对战。
具体的描述如下:
在一个场景中设置两个玩家,游戏的牌堆中只有三种基本牌:
杀、闪、桃。
其中,只有杀和桃是属于主动性游戏牌,即可以在自己行动回合将其打出;闪是被动性游戏牌,只有当别人对自己出杀的时候才可以出闪,否则扣除一点体力,而使用桃则为自己增加一点体力。
同样,为了简化实现,只要有能使用的牌,就必须使用;而且,如果你的手牌数量大于体力值,则需要弃牌直到手牌数与体力值相等。
在这种情况下,只要有一方体力为0,游戏结束!
对需求进行整理,不难发现有以下需求待实现:
1、游戏结束的条件:
只要有玩家死亡即宣告结束。
2、牌堆的实现,注意牌堆中拿出的牌需要从牌堆中移除,以及洗牌的功能。
3、杀、闪、挑的逻辑实现。
4、从手牌中计算是否还有可用的牌。
5、玩家轮询行动的实现。
在设计中,我的思路是这样的:
一局游戏实际都在一个场景中进行,直到游戏结束。
因此首先有场景的定义:
///
/// 表示游戏场景。
///
public class Scene
{
private readonly Player[] players;}
用C#编写三国杀
(二):
牌堆的设计
其中场景中定义了游戏玩家,因为场景中的游戏玩家在场景生成之后就固定了,所以使用了readonly。
同样,游戏实际是个循环,结束的条件就是有玩家死亡。
因此,先定义玩家死亡的条件:
private bool IsGameEnds()
{
return players.Where(p => p.IsDead).Count() > 0; // 如果选择到IsDead的Player,游戏结束
}
这样,由于其中引用到了Player类,因此,建立玩家类,来对其按照需求进行设计。
代码如下:
Player
///
/// 表示游戏中的玩家。
///
public class Player
{
private readonly List handCards = new List(20);
public Player(string name, byte maxHp)
{
this.Name = name;
this.Hp = maxHp;
this.MaxHp = maxHp;
}
///
/// 表示该玩家的名称。
///
public string Name { get; private set; }
///
/// 表示该玩家是否已经死亡。
///
public bool IsDead
{
get { return Hp == 0; }
}
///
/// 表示该玩家所选武将的当前HP.
///
public byte Hp { get; private set; }
///
/// 表示该玩家所选武将的最大HP.
///
public byte MaxHp { get; private set; }
}
其中的handCards表示玩家手中可以拿的手牌数量,这里设定上限为20张。
那么同样要开始定义游戏卡牌,代码如下:
GameCard
///
/// 表示游戏中用作游戏的卡牌。
///
public abstract class GameCard
{
///
/// 表示牌的花色。
///
public CardMark Mark { get; protected set; }
///
/// 表示牌的大小。
///
public CardValue Value { get; protected set; }
///
/// 表示牌的名称。
///
public string Name { get; protected set; }
///
/// 对目标玩家使用卡牌。
///
/// 使用卡牌的源对象。
/// 使用卡牌的目标对象。
public abstract void Use(Player source, Player target);
}
直到现在,所需要的定义已经基本完成。
这其中的代码有很多不规范的地方,之所以展现出来,也是为了表现自己的思维过程,在后面会有很多对目前代码的重构。
在这样定义完了之后,下一步将开始仔细分析流程,按照流程先写出最初能运行的版本。
(未完待续...)
软考软件工程师教程之用C#编写三国杀
(2)
来源:
考试百科更新时间:
2011-1-249:
27:
15【点击进入论坛】
【摘要】本文有考试百科为你提供,本文主要介绍软考软件工程师教程之用C#编写三国杀,最近做翻译做的头疼,疼过之余,想想之前公司内组织的三国杀开发兴趣小组在三国杀开发问题上几乎又停滞了。
于是又翻…
本文有考试百科为你提供,本文主要介绍软考软件工程师教程之用C#编写三国杀,最近做翻译做的头疼,疼过之余,想想之前公司内组织的三国杀开发兴趣小组在三国杀开发问题上几乎又停滞了。
于是又翻出来搞了搞,这次却搞的有点像模像样了,特地把思路和方法都共享出来,一起学习,下面是本文主要内容:
前一节说到了一些基础性的定义。
这一节开始将进入流程的分析。
首先,在游戏的场景建立之后,你就必须有一个牌堆。
对于目前的需求来说,只要有手牌的牌堆即可;尽管后面可能还要有身份牌堆和武将牌堆,但目前只考虑手牌,即游戏牌。
于是有以下定义:
///
/// 定义牌堆的基本类型。
///
/// 参数类型。
public abstract class CardHeap :
Collection
{
}
定义为抽象的,是我希望能提供一些通用的方法以简化其他牌堆的设计。
对于牌堆来说,其一个重要的功能就是能够压出牌以供使用,因此定义如下:
压牌
///
/// 从牌堆中压出指定数量的牌,这些牌将会从牌堆中移除。
///
/// 要压出的牌的数量。
/// 所压出的牌的数组。
public T[] Pop(int number)
{
if (number <= 0)
return new T[0];
if (Items.Count < number)
number = Items.Count;
T[] newT = new T[number];
for (int i = 0; i < number; i++)
{
newT[i] = Items.First();
Items.RemoveAt(0);
}
return newT;
}
牌堆定义之后就需要定义洗牌的操作。
由于我定义了从Collection继承,其内部有个IList类型的Items属性,因此编写一个扩展方法,对IList类型的数据进行类似洗牌的操作:
洗牌扩展
///
/// 定义对List的扩展方法。
///
public static class ListExtension
{
///
/// 将IList中的元素进行洗牌操作。
///
/// 类型参数。
/// 所要洗牌的List。
public static void Shuffle(this IList list)
{
Random random = new Random();
int count = list.Count;
for (int i = 0; i < count; i++)
{
int currentIndex = random.Next(0, count - i);
T tempCard = list[currentIndex];
list[currentIndex] = list[count - 1 - i];
list[count - 1 - i] = tempCard;
}
}
}
很明显,只要游戏没有结束,牌堆中拿出使用过的废牌总要回收进行循环利用,所以废牌要保存起来,并让牌堆支持其中的Items的重生。
因此CardHeap中便多了如下定义:
牌堆重生
private readonly IList usedItems = new List();
private void ReCreate()
{
// 将usedItems的牌还原到Items中,并进行洗牌,然后清空usedItems
((List) Items).AddRange(usedItems);
usedItems.Clear();
Items.Shuffle();
}
既然多了usedItems,那么上面定义的Pop就需要有个重载,以指定牌堆是否可以进行重生,所以重构上面的Pop方法,改为重载:
重载Pop
///
/// 从牌堆中压出指定数量的牌,这些牌将会从牌堆中移除。
如果牌堆的牌数量不够,则只返回牌堆中剩余的牌。
///
/// 要压出的牌的数量。
/// 所压出的牌的数组。
public T[] Pop(int number)
{
return Pop(number, false);
}
///
/// 从牌堆中压出指定数量的牌,这些牌将会从牌堆中移除。
///
/// 要压出的牌的数量。
/// 在压出牌数量不够的时候是否重新创建牌堆。
/// 所压出的牌的数组。
public T[] Pop(int number, bool recreateHeap)
{
if (number <= 0)
return new T[0];
if (Items.Count < number && !
recreateHeap)
number = Items.Count;
T[] newT = new T[number];
for (int i = 0; i < number; i++)
{
if (recreateHeap && Items.Count == 0)
{
ReCreate();
}
newT[i] = Items.First();
Items.RemoveAt(0);
usedItems.Add(newT[i]);
}
return newT;
}
这样,牌堆的定义就算基本完成了。
但在此基础上考虑考虑扩展包的问题。
实际上扩展包主要是牌的扩展,而且在设计初期,就已经考虑将标准版的牌从这个Core中分离,即除了基本牌的杀、闪、桃之外,锦囊和装备、武将都是由扩展包来提供。
因此定义了个扩展包的接口:
扩展包接口
///
/// 定义扩展包所必须实现的接口。
///
public interface IPackage
{
///
/// 扩展包中的游戏牌。
///
GameCard[] GameCards { get; }
}
好,牌堆的设计就说到这里,后面就定义实际的基本牌,并将进入实际流程循环。
软考软件工程师教程之用C#编写三国杀(3)
来源:
考试百科更新时间:
2011-1-249:
34:
18【点击进入论坛】
【摘要】本文有考试百科为你提供,本文主要介绍软考软件工程师教程之用C#编写三国杀,最近做翻译做的头疼,疼过之余,想想之前公司内组织的三国杀开发兴趣小组在三国杀开发问题上几乎又停滞了。
于是又翻…
本文有考试百科为你提供,本文主要介绍软考软件工程师教程之用C#编写三国杀,最近做翻译做的头疼,疼过之余,想想之前公司内组织的三国杀开发兴趣小组在三国杀开发问题上几乎又停滞了。
于是又翻出来搞了搞,这次却搞的有点像模像样了,特地把思路和方法都共享出来,一起学习,下面是本文主要内容:
前面已经说了牌堆的设计,那么现在就正式进入流程,满足我们在
(1)中所说的需求。
由于在
(2)中已经说了要维护扩展,因此对于之前定义的Scene,则需要定义一个所选择的扩展包,代码如下:
扩展包 privatereadonlyIPackage[]selectedPackages;
///
///初始化新的类的实例。
///
///所要加载的包。
publicScene(IEnumerablepackages)
{
players[currentToken].HasToken=true;
selectedPackages=packages.ToArray();
}
由于玩家是轮动的,因此设定一个令牌,只有持有令牌的玩家才能行动。
privateintcurrentToken;
那么定义一个Start方法,开始游戏循环。
首先要根据选择的扩展包生成游戏牌堆。
对于游戏牌堆,由于我们之前已经定义好了牌堆基类,因此实现就比较简单了,将扩展包中的游戏牌载入并洗牌即可。
游戏牌堆 ///
///表示游戏牌牌堆。
///
publicsealedclassGameCardHeap:
CardHeap
{
///
///初始化新的类的实例。
///
///所要加载的扩展包。
publicGameCardHeap(IEnumerablepackages)
{
foreach(varpackageinpackages)
{
((List)Items).AddRange(package.GameCards);
}
Items.Shuffle();
}
}
回头看我们上面的Start方法,在牌堆创建后,就进入了流程循环,还是直接来代码吧,注释都写的蛮清楚的
流程循环 ///
///开始游戏。
///
publicvoidStart()
{
PlayercurrentPlayer;
GameCardHeapheap=newGameCardHeap(selectedPackages); //创建牌堆
while(true)
{
currentPlayer=players[currentToken]; //设置当前有令牌的玩家
//摸牌阶段
GameCard[]newCards=heap.Pop(2,true);
currentPlayer.Draw(newCards);
//出牌阶段
while(currentPlayer.HasPlayableCard)
{
//NOTE:
为了简单,先实现只杀下家,并只使用杀、闪、桃
intnextToken=currentToken==players.Length-1?
0:
currentToken+1;
currentPlayer.Play(players[nextToken],currentPlayer.FirstPlayableCard);
if(IsGameEnds())
gotolabel;
}
//弃牌阶段
//NOTE:
为了简单,先实现只弃从头开始的牌到当前体力值
intdisCardCount=currentPlayer.HandCards.Length-currentPlayer.Hp;
if(disCardCount>0)
{
GameCard[]removeCards=currentPlayer.HandCards.Take(disCardCount).ToArray();
currentPlay