[英]Programming Technique: How to create a simple card game
当我学习Ruby语言时,我越来越接近实际的编程。 我在想创造一个简单的纸牌游戏。 我的问题不是面向Ruby,但我确实想知道如何用真正的OOP方法解决这个问题。 在我的纸牌游戏中,我希望有四个玩家,使用标准牌组,52张牌,没有笑话/通配符。 在游戏中,我不会将ace用作双卡,它始终是最高卡。
所以,我想知道的编程问题如下:
如何对卡片组进行分类/随机化? 有四种类型,每种类型有13个值。 最终只能有唯一值,因此选择随机值可能会生成重复值。
我怎样才能实现简单的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算法 。
/*
* 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;
}
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.