如何在多个数组上使用Fisher Yates Shuffle

How can i use Fisher-Yates Shuffle on multiple arrays?

本文关键字:Fisher Yates Shuffle 数组      更新时间:2023-09-26

我正在尝试生成一个桥牌交易,四个玩家,每个玩家得到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,如下所示:

  1. 红心大战的编号从0到12
  2. 黑桃的编号从13到25
  3. 钻石的编号从26到38
  4. 俱乐部的编号从39到51

每一套卡片的编号如下:

  1. Ace到10的编号从0到9
  2. 杰克10岁
  3. 女王11岁
  4. 金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手。

你概述的方法(每件衣服都有一个单独的数组)完全可行。但我觉得这是其中一种情况,使数据表示清晰地映射到所表示的事物,可以使您想要对数据执行的几乎每一个操作都更容易。

我认为您的问题在于构建甲板的方式。这对于顺序逻辑访问来说很好。你想把西装和纸牌混在一起。最好使用包含字符串2HQD等或简单对象{'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]);
    }
}

然而,将一张牌表示为一个有适合度和价值的对象可能会更容易处理——然后,在将牌组分为四部分之前,您也可以使用洗牌方法。