编程技巧:如何创建一个简单的纸牌游戏

当我学习Ruby语言时,我越来越接近实际的编程。 我在想创造一个简单的纸牌游戏。 我的问题不是面向Ruby,但我确实想知道如何用真正的OOP方法解决这个问题。 在我的纸牌游戏中,我希望有四个玩家,使用标准牌组,52张牌,没有笑话/通配符。 在游戏中,我不会将ace用作双卡,它始终是最高卡。

所以,我想知道的编程问题如下:

  1. 我怎样才能对卡片组进行分类/随机化? 有四种类型,每种类型有13个值。 最终可能只有唯一值,因此选择随机值可能会生成重复值。

  2. 如何实现简单的AI? 由于有大量的纸牌游戏,有人会想出这部分,所以参考会很棒。

我是一个真正的Ruby nuby,我的目标是学习解决问题,所以伪代码会很棒,只是为了理解如何以编程方式解决问题。 如果不清楚,我为我的语法和写作风格道歉,因为它不是我的母语。

此外,指向解释此类挑战的网站的指针将是一个很好的资源!

感谢您的意见,解答和反馈!

一些让你入门的东西

您可以使用0到51之间的数字轻松确保唯一卡片。

Array#shuffle方法基于Knuth-Fisher-Yates shuffle算法。 http://en.wikipedia.org/wiki/Fisher-Yates_shuffle

 class Card RANKS = %w(2 3 4 5 6 7 8 9 10 JQKA) SUITS = %w(Spade Heart Club Diamond) attr_accessor :rank, :suit def initialize(id) self.rank = RANKS[id % 13] self.suit = SUITS[id % 4] end end class Deck attr_accessor :cards def initialize # shuffle array and init each Card self.cards = (0..51).to_a.shuffle.collect { |id| Card.new(id) } end end # people with Ruby 1.9 (or 1.8.7 with backports) can safely ignore this duck punch class Array # knuth-fisher-yates shuffle algorithm def shuffle! n = length for i in 0...n r = rand(ni)+i self[r], self[i] = self[i], self[r] end self end def shuffle dup.shuffle! end end 

测试

 d = Deck.new d.cards.each do |card| puts "#{card.rank} #{card.suit}" end 

产量

 6 Spade 5 Heart 2 Heart 8 Heart 8 Diamond 7 Club J Diamond 4 Club K Spade 5 Diamond J Heart 8 Spade 10 Club 4 Diamond 9 Heart 7 Diamond 3 Diamond K Diamond 7 Spade Q Diamond 9 Diamond 6 Heart A Heart 9 Club A Spade 5 Club J Club Q Spade 2 Club 2 Spade Q Heart A Diamond 10 Spade 10 Diamond Q Club 3 Club A Club K Club 6 Club 10 Heart 2 Diamond 3 Spade K Heart 5 Spade 9 Spade 7 Heart 4 Spade J Spade 3 Heart 4 Heart 8 Club 6 Diamond 

我没有在评论中填写这一切,而是将其添加为可能会发现它有用的人的注释。 Ruby 1.9的原生Array#shuffle!Array#shuffle实际上使用Knuth-Fisher-Yates shuffle算法 。

ruby1.9.1-P376 / array.c

 /* * call-seq: * array.shuffle! -> array * * Shuffles elements in _self_ in place. */ static VALUE rb_ary_shuffle_bang(VALUE ary) { long i = RARRAY_LEN(ary); rb_ary_modify(ary); while (i) { long j = rb_genrand_real()*i; VALUE tmp = RARRAY_PTR(ary)[--i]; RARRAY_PTR(ary)[i] = RARRAY_PTR(ary)[j]; RARRAY_PTR(ary)[j] = tmp; } return ary; } /* * call-seq: * array.shuffle -> an_array * * Returns a new array with elements of this array shuffled. * * a = [ 1, 2, 3 ] #=> [1, 2, 3] * a.shuffle #=> [2, 3, 1] */ static VALUE rb_ary_shuffle(VALUE ary) { ary = rb_ary_dup(ary); rb_ary_shuffle_bang(ary); return ary; } 

不要费心寻找AI包

您将通过自己编写“AI”来了解更多信息并获得更高的满意度。

从简单开始,只考虑:

  • 游戏状态 – 玩过或看过什么牌,所有牌手都能看到哪些牌
  • 策略 – 计算机玩家如何根据其当前手牌及其对游戏状态的了解做出响应

一旦你有了这个工作,你就可以制定更复杂的策略:

  • 推断 – 人类玩家可能根据她之前的行动持有哪些牌
  • 游戏树搜索 – 如何在可能发生的情况下最大化获胜机会

那么如果你想变得非常复杂,你可以开始研究对手的建模

对于设置甲板,马斯克的答案很好。

您还询问了其他实体。

你可能想要四个“玩家”。 每个玩家可能是人类或机器控制的。

要实现人类播放器,您需要与屏幕/鼠标/键盘连接; 为了实现机器控制的玩家,你有一只手,你可以在一张桌子上看到一些中央牌(所有玩家都需要知道一张中央牌桌,可以放在牌桌上的牌)。

从那里开始,逻辑基于你正在玩什么游戏。

一旦你的“玩家”(AI)转向(例如,你的玩家对象上调用了一个takeTurn方法),它应该检查它的牌并做出正确的决定 – 从桌面上的筹码中取出牌或从中放置牌把手放到桌子上。 (该表几乎肯定至少有两个玩家可以访问的堆栈 – “Draw”和“Discard”。)

当一个Human玩家调用了他的takeTurn方法时,它应该与屏幕交互 – 更新玩家的手,允许他绘制和丢弃。

当每个玩家完成转弯时,它应该返回。 它不能直接调用下一个玩家(否则你会开始建立一个堆栈),所以你需要某种forms的转弯控制,可以按顺序调用玩家。 这种集中控制还可以防止玩家彼此了解,他们不应该真正需要(最好的OO设计策略之一是每个对象应该尽可能少地了解其他对象)。

……还在想……我可以添加更多……

我不确定你想要建立什么样的纸牌游戏,但最常见的构建这种AI的方法是生成一个可能的选项树。 我不认为有这样的库可以做到这一点,但ruby可以很容易地做树。

目标是具有当前时间的根节点,然后每个子节点是可能的动作。 然后,每个可能的动作的孩子是下一个可能的动作。 从那里你可以建立一个可能结果的树。 剩下的就是选择你喜欢的结果。

如果您没有所有信息(即无法看到您的对手牌),您可以模拟它。 通过模拟,我的意思是猜测。 所有模拟/猜测的平均值将使您很好地了解哪些树枝“可能是最好的”。

如果你可以做的就是你在路上做得很好(这是一个非常好的练习),那里有数以百计的人工智能文章,谷歌将成为你的朋友。 我描述的方法唯一的问题是它可能非常慢,但有许多聪明的技术来加速它,如换位表,alpha-beta修剪等…我不建议你查找相当。

让你入门非常简单:

 class CardGame DECK = %w[A 2 3 4 5 6 7 8 9 TJQK].product(%w[cdhs]).map(&:join) def initialize(decks=1) @decks = decks end def shuffle @playing_deck = (DECK*@decks).shuffle end def deal(players=1, cards=5) shuffle @dealt = Array.new(players) { Array.new } @dealt.map { |hand| cards.times { hand << @playing_deck.pop } } end def display @dealt.each_with_index { |cards, i| puts "Player #{i+1}: #{cards.join(' | ')}" } puts "Cards used: #{@dealt.flatten.size}" puts "Cards remaining: #{@playing_deck.size}" end private :shuffle end game1 = CardGame.new game1.deal game1.display puts game1.deal(4) game1.display puts game2 = CardGame.new(2) game2.deal(6,10) game2.display