用Javascript和不一致的数组值混洗牌?

问题描述:

我正在JavaScript中构建一个小模块以充当一副卡片。我的第一种方法很有效,但很简单,所以我想创建一些模仿真实世界卡洗牌背后思想的洗牌方法。用Javascript和不一致的数组值混洗牌?

其中一些其他有用的功能,我创建了riffle,上手和削减功能,似乎都在那里工作,但当按顺序重复调用它们时,返回的包数量不一致,从反复运行它似乎是某种竞争条件,但似乎无法让我的头脑如何避免它。

相关的私有方法是:

riffle : function riffle() { 
     var top   = Pack.slice(0, 26); 
     var bottom = Pack.slice(26, 52); 
     Pack = []; 
     console.log('top is '+top.length+" and bottom is "+bottom.length); 
     var hand = 'right'; 
     var result = []; 
     var i = 52; 

     while (i > 0) { 
      var drop = Math.floor(Math.random()*3)+1; 
      var cards; 



      if (hand === 'right') { 
       if (drop >= top.length) { 
        cards = top; 
       } else { 
        cards = top.splice(0, drop); 
       } 
       hand = 'left'; 

      } else { 
       if (drop >= bottom.length) { 
        cards = bottom; 
       } else { 
        cards = bottom.splice(0, drop); 
       } 
       hand = 'right'; 
      } 

      result = result.concat(cards); 
      i -= drop; 

     } 
     Pack = result; 
     console.log(Pack.length+" after riffle"); 
     return this; 
    }, 

cut : function cut(fn) { 
     var top   = Pack.slice(0, 26); 
     var bottom = Pack.slice(26, 52); 
     Pack = []; 
     console.log(top); 
     Pack = bottom.concat(top); 
     console.log(Pack.length+" after cut"); 
     if (fn && typeof(fn) === 'function') { fn(); } 
     return this; 
    } 

后来我有一个叫洗牌特权的方法来调用它们:

shuffle : function shuffle(cb) { 
     State.cardsOut = []; 
     Internal.generatePack().cut().riffle().riffle() 
          .riffle().riffle().riffle(); 

     if (cb && typeof(cb) === 'function') { cb(); } 
    } 

注:我先说一个生成函数创建的arrray代表全套52张卡片的物品。当我在洗牌和裁员后的不同时间登录游戏包时,我得到的结果各不相同,我似乎无法弄清楚原因。

你可以看到什么i'km工作就在这里

https://gist.github.com/Pushplaybang/66bc7a1fa5d84eee2236

任何帮助将真棒。

+0

我明白动机,但真正的随机洗牌后面有[数学理论](http://eli.thegreenplace.net/2010/05/28/the-intuition-behind-fisher-yates-shuffling/) 。 – Pointy

+0

不是你的问题的答案,但当我看着你的要点,我看到一个奇怪的语法错误,我认为。 第104行:var drop z = Math.floor(Math.random()* 9)+1; –

+0

你用'drop'中的值减少'i',但'drop'可能比实际卡片拼接的数量要高。在你的drop> = top.length'(和bottom)块中,你应该将drop设置为top.length(或bottom.length)。或测试顶部和底部的长度,即'while(top.length && bottom.length)' –

drop变量存储了您应该从左手或右手轻松抽出的牌的数量。然而,有两种情况:

if (drop >= top.length) { 
    cards = top; 
} 

if (drop >= bottom.length) { 
    cards = bottom; 
} 

其中drop可以比其余的卡包这么多卡将从i减去比你的一半数量越多实际上是轻微的。你可以解决这个问题的:

if (drop >= top.length) { 
    drop = top.length; 
    cards = top; 
    top = []; 
} 

if (drop >= bottom.length) { 
    drop = top.length; 
    cards = bottom; 
    bottom = []; 
} 

(你需要清空阵列或者你可能最终加入同一卡的两倍)。

其他问题

  • 你的代码(2652),这些可以在类中的常量定义,并给出相应的名称(即PACK_SIZE = 52),这将意味着,如果你创建一个有魔力的数字子类代表不同数量的卡,那么它仍然可以工作。
  • hand有两个可能的值,可以表示为一个布尔值,但是可以将其赋值为字符串(也可以使用常量LEFT_HAND = true, RIGHT_HAND = !LEFT_HAND)。
  • Pack似乎是一个全局变量 - 我原以为它应该是该类的成员。
  • 您不需要命名函数,因为这只是污染全局命名空间:riffle : function riffle() {可以只是一个匿名函数riffle : function() {
  • 性能 - 您可以在每次迭代时创建额外的阵列,并且卡片会多次移动。这可能更有效率。

事情是这样的:

PACK_SIZE: 52, 
riffle : function() { 
    var index_of_cards_riffled_from_top = 0; 
    var index_of_cards_riffled_from_bottom = this.PACK_SIZE/2; 
    var riffled_cards = []; 
    while (index_of_cards_riffled_from_top < this.PACK_SIZE/2 
     || index_of_cards_riffled_from_bottom < this.PACK_SIZE) { 
    var num_cards_to_riffle_top = Math.min(this.PACK_SIZE/2 - index_of_cards_riffled_from_top, Math.floor(Math.random() * 3) + 1); 
    var num_cards_to_riffle_bottom = Math.min(this.PACK_SIZE - index_of_cards_riffled_from_bottom, Math.floor(Math.random() * 3) + 1); 
    while (num_cards_to_riffle_top > 0) { 
     riffled_cards.push(this.Pack[ index_of_cards_riffled_from_top++ ]); 
     num_cards_to_riffle_top--; 
    } 
    while (num_cards_to_riffle_bottom > 0) { 
     riffled_cards.push(this.Pack[ index_of_cards_riffled_from_bottom++ ]); 
     num_cards_to_riffle_bottom--; 
    } 
    } 
    this.Pack = riffled_cards; 
} 
+0

这是一个很好的答案,谢谢你,我完全按照你的建议努力做到这一点。我也开始为我的裁剪方法和裁减裁剪添加随机性。制作包装大小也是很重要的,因为我想要选择排除卡片或包括特定游戏的玩家。 – pushplaybang

+0

请注意,实际问题是当顶部和底部数组低于或等于丢弃量时,不会清空顶部和底部数组。但其他信息也很有见地。 – pushplaybang

而@MTO的回答没有解决我的问题,我想阐明我如何选择开始重构这一功能的一些情况。

riffle : function riffle() { 
     var cutPos = Math.floor(Math.random()*rv)+((cardCount-rv)/2); 
     var splitPack = { 
      left : Pack.splice(0, cutPos), 
      right : Pack.splice(0, Pack.length) 
     }; 

     var hand = 'right',result = [], i = 52, cards; 

     while(i > 0) { 
      drop = Math.floor(Math.random()*3)+1; 

      if (drop >= splitPack[ hand ].length) { 
       drop = splitPack[ hand ].length; 
      } 

      cards = splitPack[ hand ].splice(0, drop); 
      hand = (hand === 'left') ? 'right' : 'left'; 

      result = result.concat(cards); 
      cards = []; 
      i -= drop; 

     } 

     Pack = result; 
     console.log(Pack.length+" after riffle"); 
     return this; 
    }, 

几件事情:

  • ,似乎全球都算不上,因为这是所有创建了一个新的“甲板”对象的函数内包裹的元素,有些元素需要以民间,例如一旦交易开始后仍然在包中的卡片。
  • 尽管布尔值可以很好地用于手中,但我还是想稍微调整一下,并使用这些字符串来选择obj属性。
  • MTO所说的关于使用常量的说法绝对有效。
  • 现在每次拼接,我们将从数组中删除元素。
  • 我更喜欢这种方法,因为它只使用一个while循环。
  • 最后,这种类型的洗牌是为了模仿手洗牌,并且必须与其他手洗牌方法结合,理想情况下以重复的顺序,以产生一些有用的东西,如果你想要一致的随机和有效使用fischer的东西-yates算法。