当我学习Ruby语言时,我越来越接近实际的编程。我在想创造一个简单的纸牌游戏。我的问题不是面向Ruby,但我确实想知道如何用真正的OOP方法解决这个问题。在我的纸牌游戏中,我希望有四个玩家,使用标准牌组,52张牌,没有笑话/通配符。在游戏中,我不会将ace用作双卡,它始终是最高卡。
所以,我想知道的编程问题如下:
如何对牌组进行分类/随机化?有四种类型,每种类型有13个值。最终只能有唯一值,因此选择随机值可能会生成重复值。
如何实现简单的AI?由于有大量的纸牌游戏,有人会已经想出这部分,所以参考会很棒。
我是一个真正的Ruby nuby,我的目标是学习解决问题,所以伪代码会很棒,只是为了理解如何以编程方式解决问题。如果不清楚,我为我的语法和写作风格道歉,因为它不是我的母语。
此外,指向解释此类挑战的网站的指针将是一个很好的资源!
感谢您的意见,答案和反馈!
答案 0 :(得分:15)
使用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
答案 1 :(得分:5)
我没有在评论中填写这一切,而是将其添加为可能会发现它有用的人的注释。 Ruby 1.9的原生Array#shuffle!
和Array#shuffle
确实使用了Knuth-Fisher-Yates shuffle algorithm。
/*
* 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;
}
答案 2 :(得分:3)
您将通过自己编写“AI”来了解更多信息并获得更高的满意度。
从简单开始,只考虑:
一旦你有了这个工作,你就可以制定更复杂的策略:
然后如果你想让真的复杂,你可以开始研究opponent modeling
答案 3 :(得分:2)
对于建立一个套牌,马斯克的答案是好的。
您还询问了其他实体。
你可能想要四个“玩家”。每个玩家可能是人或受机器控制的。
要实现人类播放器,您需要与屏幕/鼠标/键盘连接;为了实现机器控制的玩家,你有一只手,你可以看到桌子上的一些中央牌(所有玩家都需要知道一张中央牌桌,可以放在牌桌上的牌)。
从那里开始,逻辑基于你正在玩什么游戏。
一旦你的“玩家”(AI)转向(例如,在你的玩家对象上调用takeTurn
方法),它应该检查它的牌并做出正确的决定 - 从堆栈中取出牌桌子或将牌从手放到桌子上。 (该表几乎肯定至少有两个玩家可以访问的堆栈 - “Draw”和“Discard”。)
当一个人类玩家调用他的takeTurn
方法时,它应该与屏幕交互 - 更新玩家的手,允许他绘制和丢弃。
当每个玩家完成转弯时,它应该返回。它不能直接调用下一个玩家(否则你会开始建立一个堆栈),所以你需要某种形式的转弯控制,可以按顺序调用玩家。这种集中控制还可以防止玩家彼此了解,他们不应该真正需要(最好的OO设计策略之一就是每个对象应该尽可能少地了解其他对象)。
......还在想......我可以添加更多...
答案 4 :(得分:1)
我不确定你想要构建什么类型的纸牌游戏,但是构建这种AI的最常见方式是生成一个可能的选项树。我不认为有这样的库可以做到这一点,但红宝石可以很容易地做树。
目标是拥有一个根节点,它是当前时间,然后每个子节点都是可能的动作。然后,每个可能的动作的孩子是下一个可能的动作。从那里你可以建立一个包含所有可能结果的树。剩下的就是选择你喜欢的结果。
如果您没有所有信息(即无法看到您的对手牌),您可以模拟它。通过模拟我的意思是猜测。所有模拟/猜测的平均值将使您清楚地了解哪些树枝“可能是最好的”。
如果你可以做好所有你正在做的事情(这是一个非常好的练习),那里有数以百计的人工智能文章,谷歌将成为你的朋友。我描述的方法唯一的问题是它可能非常慢,但有许多聪明的技术来加速它,如换位表,alpha-beta修剪等......我不建议你查看它。
答案 5 :(得分:0)
让你开始的事情非常简单:
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