不知道大家有没有玩过一个叫《拖板车》的扑克游戏(貌似暴露了年龄。。),本篇我们尝试让计算机来玩这个游戏,并且我们在确定双方的牌和顺序后就能立马知道谁将是赢家。在玩这个游戏前我们需要先了解下队列和栈。
队列简介
队列是一种特殊的线性结构,它只允许在队列的首部进行删除操作,这称作“出队”,而在队列的尾部进行插入操作,这称为“入队"。这和我们实际生活中排队买东西的场景一样,先买东西的肯定是队首的第一个,新来的人肯定是排队在最后面。我们称之为先进先出(FIFO)原则。
队列实现
队列的数据结构实现起来比较简单,用一个数组控制先进先出即可,.net平台已经内置了这种数据结构,我们一起看一下万能的微软是如何实现队列的。代码较多,我们看一下关键部分入队和出队的代码。
private T[] _array;
private int _head;
private int _tail;
private int _size;
private int _version;
//出队
public T Dequeue()
{
//判断队列中是否有元素,如果没有抛出空队列异常
if (_size == 0)
{
System.ThrowHelper.ThrowInvalidOperationException(System.ExceptionResource.InvalidOperation_EmptyQueue);
}
//取得队首元素,作为返回结果
T result = _array[_head];
//将队首元素重置为默认值
_array[_head] = default(T);
//_head指向队首下一个元素
_head = (_head + 1) % _array.Length;
//队列长度减一
_size--;
//版本更新
_version++;
return result;
}
//入队
public void Enqueue(T item)
{
//.net在初始化队列的时候会给数组一个初始长度,当数组数量达到一定条件会扩容,每次增加4的长度
if (_size == _array.Length)
{
int num = (int)((long)_array.Length * 200L / 100);
if (num < _array.Length + 4)
{
num = _array.Length + 4;
}
SetCapacity(num);
}
//将新元素加到数组末尾
_array[_tail] = item;
//_tail指向末尾后一个索引
_tail = (_tail + 1) % _array.Length;
//队列长度+1
_size++;
_version++;
}
栈简介
栈和队列类似,也是一种特殊的线性结构,同样在尾部进行插入操作,但是删除元素同样是在队尾,采用先进后出原则。假如我们往一个直径只有乒乓球大小的水杯分别放入2,3,1号乒乓球,那么我们想要拿出2号球就必须先分别拿出1号球和3号球,这里的水杯就是典型的栈结构。同样地我们看下微软关于栈的关键代码实现。
private T[] _array;
private int _size;
private int _version;
//出栈
public T Pop()
{
//判断栈中是否还有元素可以出栈,没有则抛出空栈异常
if (_size == 0)
{
System.ThrowHelper.ThrowInvalidOperationException(System.ExceptionResource.InvalidOperation_EmptyStack);
}
//版本+1
_version++;
//取的数组尾部元素作为返回结果,并且长度记录-1
T result = _array[--_size];
//将原来的尾部元素重置为默认值
_array[_size] = default(T);
return result;
}
//入栈
public void Push(T item)
{
//同队列相似,如果数组容量达到条件就扩容
if (_size == _array.Length)
{
T[] array = new T[(_array.Length == 0) ? 4 : (2 * _array.Length)];
Array.Copy(_array, 0, array, 0, _size);
_array = array;
}
//将新元素添加到数组尾部同时长度记录+1
_array[_size++] = item;
_version++;
}
拖板车游戏
游戏玩法
将一副扑克牌随机分为两份,小明和小红各一份。小明先将手中的第一张扑克牌放在桌上,然后小红也拿出手中的第一张牌放在小明刚打出的扑克牌上面,就像这样两人交替出牌。出牌时,如果某人打出的牌和桌上某张牌数字相同,即可将这两张牌及中间所夹的牌全部收走并放到自己手牌的末尾。当其中任意一人手中牌为空的时候,游戏结束,对手获胜。
我们分析下这个游戏过程,两个人的操作都是一样的,手里的牌相当于一个队列,出牌是出队,赢牌是入队。而桌上的牌相当于一个栈,出牌的时候是入栈,赢牌的时候是出栈。这样我们的实现思路就清晰了,下面我们用程序来模拟这个游戏。
编码实现
static void Main(string[] args)
{
var all = new List<string>();
//首先将52张牌放入集合
for (int i = 0; i < 4; i++) //每种牌四张,循环四次
{
for (int j = 2; j < 15; j++) //13种牌【2,3,4,5,6,7,8,9,10,J,Q,K,A】
{
if (j <= 10)
{
all.Add(j.ToString());
}
else
{
switch (j)
{
case 11:
all.Add("J");
break;
case 12:
all.Add("Q");
break;
case 13:
all.Add("K");
break;
case 14:
all.Add("A");
break;
default:
break;
}
}
}
}
//然后将54张牌随机分到小明和小红手里
//定义两个队列用来记录小明和小红的手牌
Queue<string> xiaoming = new Queue<string>();
Queue<string> xiaohong = new Queue<string>();
var strming = "小明手上的牌依次为:";
var strhong = "小红手上的牌依次为:";
for (int i = 0; i < 52; i++)
{
var random = new Random().Next(0, 51 - i);
if (i%2==0)
{
xiaoming.Enqueue(all[random]);
strming += all[random]+" ";
}
else
{
xiaohong.Enqueue(all[random]);
strhong += all[random] + " ";
}
all.RemoveAt(random);
}
Console.WriteLine(strming);
Console.WriteLine(strhong);
//开始游戏
//先定义一个栈来记录桌面上的牌
var stack = new Stack<string>();
while (xiaoming.Count>0&&xiaohong.Count>0)
{
var first = xiaoming.Dequeue(); //小明出牌
Console.WriteLine("小明出牌:"+first);
//判断桌面上是否有刚出的牌
if (stack.Contains(first))
{
//如果有那这两张牌及中间的牌都入队小明
xiaoming.Enqueue(first);
while (stack.First()!=first)
{
xiaoming.Enqueue(stack.Pop());
}
xiaoming.Enqueue(stack.Pop());
Console.Write("小明赢得一轮,桌上剩余牌为:");
foreach (var item in stack)
{
Console.Write(item + " ");
}
Console.WriteLine("");
}
else
{
//没有的话放到桌面上
stack.Push(first);
}
first = xiaohong.Dequeue(); //小红出牌
Console.WriteLine("小红出牌:" + first);
//判断桌面上是否有刚出的牌
if (stack.Contains(first))
{
//如果有那这两张牌及中间的牌都入队小红
xiaohong.Enqueue(first);
while (stack.First() != first)
{
xiaohong.Enqueue(stack.Pop());
}
xiaohong.Enqueue(stack.Pop());
Console.Write("小红赢得一轮,桌上剩余牌为:");
foreach (var item in stack)
{
Console.Write(item + " ");
}
Console.WriteLine("");
}
else
{
//没有的话放到桌面上
stack.Push(first);
}
}
if (xiaoming.Count==0)
{
Console.WriteLine("小红获胜!");
}
else
{
Console.WriteLine("小明获胜!");
}
Console.ReadKey();
}
来看一下执行结果,输出较多,只截了第一张和最后一张:
有趣的拖板车就到此为止啦~
欢迎关注我的算法系列博客:
算法(https://blog.csdn.net/leaderxin/article/category/9434286)