如何在多个阵列上使用Fisher-Yates Shuffle?

时间:2013-12-18 03:05:16

标签: javascript 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算法的这种随机函数:

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;
}

问题是我的编程(和/或逻辑)技能太差,无法找到我如何在我的场景中应用这个算法,处理多个数组。

这是我的问题的良好开端,还是我应该研究一下更好的方法?

4 个答案:

答案 0 :(得分:1)

我认为你的问题是你正在构建你的套牌的方式。这对于顺序的逻辑访问来说很好。你想要西装混合以及西装卡。使用包含字符串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 = [ [], [], [], [] ];

你应该好好去(未经测试)

答案 1 :(得分:1)

每张卡的适用性和价值都很重要,因此您需要将它们保持在一起。卡片可能更好地表示为{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]});
    }
}

然后使用你的shuffle来洗牌,slice()将它分成13只4手。

您概述的方法(每个套装都有一个单独的数组)可以完全奏效。但我觉得这是将数据表示映射到所表示事物的干净利落的情况之一,这使得您想要对数据执行的每个操作都更容易。

答案 2 :(得分:1)

首先,您需要一种方法来区分每张卡片。假设所有卡的编号从0到51如下:

  1. 红心从0到12编号。
  2. 黑桃的编号从13到25。
  3. 钻石编号为26至38。
  4. 分会编号从39到51。
  5. 在每件套装中,卡片的编号如下:

    1. Ace到10的编号从0到9。
    2. 杰克是10岁。
    3. 女王是11岁。
    4. King是12岁。
    5. 请注意,这些数字仅用于识别卡片。它们不代表任何卡的价值。因此,它们可用于所有纸牌游戏。鉴于一个数字,你可以找到它的诉讼和排名如下:

      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));
      }
      

      就是这样。

答案 3 :(得分:0)

你不能在这里使用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]);
    }
}

然而,将单张卡片作为具有西装和价值的物品代表可能会更容易处理 - 然后您也可以在将牌组切成四个部分之前使用随机方法。