繁体   English   中英

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

[英]Programming Technique: How to create a simple card game

当我学习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 J Q K A)
  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(n-i)+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算法

红宝石1.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”来了解更多信息并获得更高的满意度。

从简单开始,只考虑:

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

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

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

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

Macek的答案很好,就建立一个套牌而言。

您还询问了其他实体。

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

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

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

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

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

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

......还在想......我可能会添加更多......

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

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

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

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

让你入门非常简单:

class CardGame
  DECK = %w[A 2 3 4 5 6 7 8 9 T J Q K].product(%w[c d h s]).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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM