这道题我刚看到时,曾尝试用手工来破解,每次都以为找到了农民的必胜策略时,最后都发现其实农民跑不掉。
由于手工破解无法穷尽所有可能性,所以这道题究竟农民有没有妙手跑掉呢,只能通过代码来帮助我们运算了。
本文将简要讲述怎么通过代码来求解此类问题,在最后会公布残局的最后结果,并开源代码以供大家吐槽。
代码的核心思想是minimax。
minimax可以拆解为两部分,mini和max,分别是最小和最大的意思。
直观的理解是什么呢?就有点像A、B两个人下棋。
A现在可以在N个点走棋,假设A在某个点走棋了,使得A的这一步的盘面评估分数最高;但是轮到B下的时候,就一定会朝着让A最不利的方向走,使得A的下一步必然按照B设定的轨迹来,而没法达到A在第一步时估算到这一步的最高盘面评分。
在牌局中是一样的,如果农民的一手牌,让地主无论如何应对都不能赢的话,那么可以说农民有必胜策略;否则,农民必输。
我们可以用一个函数hand_out来模拟一个人的出牌过程。
在现实生活中,一个人想要出牌的话,必然需要知道自己手上的所有牌:me_pokers,也需要知道上一手的出的牌:last_hand。
如果我们要用这个函数来模拟两个人的出牌,则还需要知道对手当前的所有牌:enemy_pokers。
这个函数的返回值,是轮到我me_pokers出牌时,是否能够必赢牌。
如果能赢则返回真,否则返回假。
假设轮到我出牌时,如果我手上的牌都出完了,那么我将立刻知道我赢了;反之如果对手的牌都出完了,而我没有,则我失败了。
因为现在轮到我出牌,所以我首先需要知道我现在能出的所有手牌组合。
注意:这个组合中,包括过牌(即不出牌)的策略。
关键点来了,在出完我的牌或选择过牌后,我们需要用一个递归调用来模拟对手下一步的行为。
如果对手的下一次出牌不能获胜的话,则我这一次的出牌必胜;否则,对于我的每一个出牌选择,对手都能获胜的话,则我必败。
首先,我们要用数字来表示牌的大小,这里我们用3表示3,11来表示J,12表示Q,依次类推……
其次,我们需要求出一个手牌的所有出牌组合,这里需要get_all_hands函数,具体实现比较繁琐但是很简单,就不在此赘述。
然后,我们还需要一个牌力判断函数canb2_beatb1b1,b2),这个函数用于比较两组手牌的牌力,看是b2可以击b1。
唯一需要注意的一点,在斗地主的规则中,除了炸弹外,其他所有牌力均等,只有牌型一样时才能去比较。
最后,我们需要一个模拟出牌函数make_hand(pokers, hand),用于求出在手牌为pokers的情况下打出一手牌hand后,剩下的手牌,实现也非常简单,只需简单的移除掉那些打出的牌即可。
澳门司警拘捕一名内地「性感赌客」,怀疑有人因钟情新口岸某赌场一间钟表珠宝店内价值79.5万元的名贵手表,遂提前在内地「私人订制」同款式作备用,于4月下旬假扮顾客试戴名表,再用「偷龙转凤」方式偷取手表逃逸,店铺直到盘点时才发现失窃报司警求助。警方透过「天眼」系统锁定疑人身份及去向,知悉她犯案后续留在澳门博彩旅游数日才离境。及至上周六(13日)于机场再入境时落网,并在其随身物品中起回赃物。
澳门一名男荷官串谋两同诈骗自家赌场,涉公务上之侵占,当场被捕的两疑人分别是姓倪荷官及姓施男子,职业为销售员,分别30和23岁,均为澳门人。姓施男子于5至七7月期间已多次合作犯案,另一在逃者则在6月作案逃逸,暂未落网,赌场暂计损失16.5万元,司警正跟进犯罪行为会否不止相关数目。
由于一副牌的可能手牌巨大,导致递归的分支数巨大。
所以时间开销非常大,为阶乘级O(N!),根据斯特林公式,大约为O(N^N)。
即对我方手牌和敌方手牌和上一轮手牌的描述(str(me_pokers)+str(enemy_pokers)+str(last_hand))为键,将求出的结果存进缓存字典中。
下一次遇到相同的局面时,即可直接从缓存字典中取出,而无需再次重复计算。
时间复杂度优化为指数级O(C^N)。
代码运算出来的结果是,农民没有必胜策略。
换言之,只要地主会玩,农民不可能赢。
阶级固化已经如斯了么……网上棋牌游戏