原創文章,轉載請保留作者署名!
前一節說到了一些基礎性的定義。這一節開始將進入流程的分析。
首先,在遊戲的情境建立之後,你就必須有一個牌堆。對於目前的需求來說,只要有手牌的牌堆即可;儘管後面可能還要有身份牌堆和武將牌堆,但目前只考慮手牌,即遊戲牌。於是有以下定義:
/// <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; }
}
好,牌堆的設計就說到這裡,後面就定義實際的基本牌,並將進入實際流程迴圈。