如何在多个数组上使用Fisher Yates Shuffle
How can i use Fisher-Yates Shuffle on multiple arrays?
我正在尝试生成一个桥牌交易,四个玩家,每个玩家得到13张(随机)牌。
我想我应该先声明一些变量,例如套装、包含套装的套牌和玩家的手牌:
var deal = function() {
var spades = ['A', 'K', 'Q', 'J', 'T', 9, 8, 7, 6, 5, 4, 3, 2];
var hearts = ['A', 'K', 'Q', 'J', 'T', 9, 8, 7, 6, 5, 4, 3, 2];
var diamonds = ['A', 'K', 'Q', 'J', 'T', 9, 8, 7, 6, 5, 4, 3, 2];
var clubs = ['A', 'K', 'Q', 'J', 'T', 9, 8, 7, 6, 5, 4, 3, 2];
var deck = [spades, hearts, diamonds, clubs];
//Next to do: get 13 random cards dealt to each player
var northHand = [ [], [], [], [] ];
var eastHand = [ [], [], [], [] ];
var southHand = [ [], [], [], [] ];
var westHand = [ [], [], [], [] ];
}
然后我发现了这个基于fisher-yates算法的shuffle函数:
function shuffle(array) {
var m = array.length, t, i;
// While there remain elements to shuffle…
while (m) {
// Pick a remaining element…
i = Math.floor(Math.random() * m--);
// And swap it with the current element.
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
问题是,我的编程(和/或逻辑)技能太差,无法了解如何在我的场景中应用这种算法,处理多个数组。
这对我的问题是一个好的开始,还是有更好的方法我应该研究?
首先,您需要一种方法来区分每张卡。假设所有卡片的编号从0到51,如下所示:
- 红心大战的编号从0到12
- 黑桃的编号从13到25
- 钻石的编号从26到38
- 俱乐部的编号从39到51
每一套卡片的编号如下:
- Ace到10的编号从0到9
- 杰克10岁
- 女王11岁
- 金12岁
请注意,这些数字只是用来识别卡片的。它们不代表任何卡片的价值。因此,它们可以用于所有的纸牌游戏。给定一个数字,你可以找到它的套装和排名如下:
var suits = ["Hearts","Spades","Diamonds","Clubs"];
var ranks = ["Ace","2","3","4","5","6","7","8","9","10","Jack","Queen","King"];
function getSuite(card) {
return suits[Math.floor(card / 13)];
}
function getRank(card) {
return ranks[card % 13];
}
现在你的牌组只是一个从0到51的数字数组。因此,您可以使用Fischer-Yates来洗牌:
function deal() {
var deck = shuffle(range(0, 51));
var northHand = deck.slice(0, 13);
var eastHand = deck.slice(13, 26);
var southHand = deck.slice(26, 39);
var westHand = deck.slice(39, 52);
}
function range(from, to) {
if (from > to) return [];
else return [from].concat(range(from + 1, to));
}
仅此而已。
每张牌的花色和价值都很重要,所以你需要把它们放在一起。卡片最好用{suit: 'spades', value: 'A'}
表示。
我会沿着以下路线建造甲板:
var deck = [];
var suits = ['spade', 'heart', 'club', 'diamond'];
var values = ['A', 'K', 'Q', 'J', '10', '9', '8', '7', '6', '5', '4', '3', '2'];
for (var sIdx = 0; sIdx < suits.length; sIdx++) {
for (var vIdx = 0; vIdx < values.length; vIdx++) {
deck.push({suit: suits[sIdx], value: values[vIdx]});
}
}
然后用你的洗牌来洗牌,slice()
把它分成4手,每组13手。
你概述的方法(每件衣服都有一个单独的数组)完全可行。但我觉得这是其中一种情况,使数据表示清晰地映射到所表示的事物,可以使您想要对数据执行的几乎每一个操作都更容易。
我认为您的问题在于构建甲板的方式。这对于顺序逻辑访问来说很好。你想把西装和纸牌混在一起。最好使用包含字符串2H
、QD
等或简单对象{'Suit': 'Hearts', 'Value': 'Q'}
的52元素数组
一旦你有了一个单一的数组,使用你提供的代码,shuffle实现就变得微不足道了
一旦你洗牌,你可以给玩家1 0-12、2 13-25等牌,或者你可以正确地"处理"它们,这样玩家1就可以从0、4、6、12等位置获得牌…
不要将4个阵列命名为N/E/S/W,而是使用2d阵列,以便轻松访问它。
然后循环浏览卡片,类似于。。。
for(var i=0;i<52;i++) {
Pos = i % 4;
Hands[Pos].push(Deck[i]);
}
假设你的手被定义为
var Hands = [ [], [], [], [] ];
你应该很好去(未经测试)
您不能在这里使用Fisher Yates shuffle,因为您希望在数组之间随机移动值。这应该做到,并且仍然是随机的:
var hands = [northHand, eastHand, southHand, westHand];
var numberofcards = 4*13;
for (var i=0; i<4; i++) {
for (var j=0; j<13; j++) {
// draw card:
var card = Math.floor(Math.random() * numberofcards--);
// check which card it is:
for (var suit=4; suit-- && card > deck[suit].length; )
card -= deck[suit].length;
// remove from deck and assign to player
hands[i][suit].push(deck[suit].splice(card, 1)[0]);
}
}
然而,将一张牌表示为一个有适合度和价值的对象可能会更容易处理——然后,在将牌组分为四部分之前,您也可以使用洗牌方法。