惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Help Net Security
博客园 - Franky
GbyAI
GbyAI
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
爱范儿
爱范儿
IT之家
IT之家
酷 壳 – CoolShell
酷 壳 – CoolShell
aimingoo的专栏
aimingoo的专栏
博客园_首页
MongoDB | Blog
MongoDB | Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Recent Announcements
Recent Announcements
Scott Helme
Scott Helme
有赞技术团队
有赞技术团队
M
MIT News - Artificial intelligence
C
CERT Recently Published Vulnerability Notes
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Jina AI
Jina AI
F
Fortinet All Blogs
N
Netflix TechBlog - Medium
L
LangChain Blog
L
LINUX DO - 最新话题
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
H
Hacker News: Front Page
MyScale Blog
MyScale Blog
P
Palo Alto Networks Blog
G
Google Developers Blog
Google DeepMind News
Google DeepMind News
AI
AI
T
Troy Hunt's Blog
Microsoft Azure Blog
Microsoft Azure Blog
阮一峰的网络日志
阮一峰的网络日志
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Vercel News
Vercel News
Microsoft Security Blog
Microsoft Security Blog
罗磊的独立博客
S
Secure Thoughts
大猫的无限游戏
大猫的无限游戏
博客园 - 叶小钗
人人都是产品经理
人人都是产品经理
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
Apple Machine Learning Research
Apple Machine Learning Research
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 三生石上(FineUI控件)
S
Security @ Cisco Blogs
Cloudbric
Cloudbric
E
Exploit-DB.com RSS Feed
Attack and Defense Labs
Attack and Defense Labs

博客园 - 枫

2015半年记 再写一帖~就《离开上海》一文再说明 再见,上海~非主流码农在上海的9年心路历程 Mac下配置mysql-python 《Rework》摘录读后随感 Event Sourcing与大数据 无题(1) 在Mac上安装Hadoop [原创]一步一步用C#编写三国杀(三):设计流程 [原创]一步一步用C#编写三国杀(一):规则和需求描述 走进单元测试(3):消灭HttpContext的依赖,兼谈单元测试的设计辅助性 走进单元测试(2):必须要自动化 我也想对广大程序员说一些话 走进单元测试(1):为什么难以广泛应用? 高级语言发展之回归人类思维——听老赵的Session有感 缺乏自信怎么办? 梦话对象之三:三要素的差异与统一 梦话对象之二:事件之无限扩展 梦话对象之一:逃不开的生死问题
[原创]一步一步用C#编写三国杀(二):牌堆的设计
· 2010-07-29 · via 博客园 - 枫

原创文章,转载请保留作者署名!

前一节说到了一些基础性的定义。这一节开始将进入流程的分析。

首先,在游戏的场景建立之后,你就必须有一个牌堆。对于目前的需求来说,只要有手牌的牌堆即可;尽管后面可能还要有身份牌堆和武将牌堆,但目前只考虑手牌,即游戏牌。于是有以下定义:

    /// <summary>
    
/// 定义牌堆的基本类型。
    
/// </summary>
    
/// <typeparam name="T">参数类型。</typeparam>
    public abstract class CardHeap<T> : Collection<T>
    {

    }

 定义为抽象的,是我希望能提供一些通用的方法以简化其他牌堆的设计。

对于牌堆来说,其一个重要的功能就是能够压出牌以供使用,因此定义如下:

压牌

        /// <summary>
        
/// 从牌堆中压出指定数量的牌,这些牌将会从牌堆中移除。
        
/// </summary>
        
/// <param name="number">要压出的牌的数量。</param>
        
/// <returns>所压出的牌的数组。</returns>
        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<T> 继承,其内部有个IList<T>类型的Items属性,因此编写一个扩展方法,对IList<T>类型的数据进行类似洗牌的操作:

洗牌扩展

    /// <summary>
    
/// 定义对List的扩展方法。
    
/// </summary>
    public static class ListExtension
    {
        
/// <summary>
        
/// 将IList中的元素进行洗牌操作。
        
/// </summary>
        
/// <typeparam name="T">类型参数。</typeparam>
        
/// <param name="list">所要洗牌的List。</param>
        public static void Shuffle<T>(this IList<T> 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<T> usedItems = new List<T>();private void ReCreate()
        {
            
// 将usedItems的牌还原到Items中,并进行洗牌,然后清空usedItems
            ((List<T>) Items).AddRange(usedItems);
            usedItems.Clear();
            Items.Shuffle();            
        }

既然多了usedItems,那么上面定义的Pop就需要有个重载,以指定牌堆是否可以进行重生,所以重构上面的Pop方法,改为重载:

重载Pop

        /// <summary>
        
/// 从牌堆中压出指定数量的牌,这些牌将会从牌堆中移除。如果牌堆的牌数量不够,则只返回牌堆中剩余的牌。
        
/// </summary>
        
/// <param name="number">要压出的牌的数量。</param>
        
/// <returns>所压出的牌的数组。</returns>
        public T[] Pop(int number)
        {
            
return Pop(number, false);
        }
/// <summary>
        
/// 从牌堆中压出指定数量的牌,这些牌将会从牌堆中移除。
        
/// </summary>
        
/// <param name="number">要压出的牌的数量。</param>
        
/// <param name="recreateHeap">在压出牌数量不够的时候是否重新创建牌堆。</param>
        
/// <returns>所压出的牌的数组。</returns>
        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中分离,即除了基本牌的杀、闪、桃之外,锦囊和装备、武将都是由扩展包来提供。因此定义了个扩展包的接口:

扩展包接口

    /// <summary>
    
/// 定义扩展包所必须实现的接口。
    
/// </summary>
    public interface IPackage
    {
        
/// <summary>
        
/// 扩展包中的游戏牌。
        
/// </summary>
        GameCard[] GameCards { get; }
    }

好,牌堆的设计就说到这里,后面就定义实际的基本牌,并将进入实际流程循环。