【问题标题】:Better way than "switch" in javascript比javascript中的“switch”更好的方法
【发布时间】:2017-09-16 22:24:55
【问题描述】:

我对 javascript 很陌生,但遇到了一点小问题。

作为我的第一个“项目”,我希望我的程序给我一张随机扑克牌。没什么了不起的,但我正在尝试找出一种优雅的方式来做到这一点。 到目前为止,我唯一的想法是给一个 1 到 52 之间的随机数一张特定的卡片,但必须有更好的方法。

这是我当前的代码:

function newCard() {
var card_id = document.getElementById("card_id");
var c1 = Math.floor(Math.random() * 52) + 1;
switch(c1) {
    case 1:
        c1 = "ace of spades";
        break;
    case 2:
        c1 = "2 of spades";
        break;
    case 3:
        c1 = "3 of spades";
        break;  


    // ...I think you get the idea here


         } 

card_id.innerHTML = c1;
}

你对我有什么提示吗,如何让它更快/更好?

【问题讨论】:

标签: javascript


【解决方案1】:

function newCard() {
  // Pick from 0 to 51, not 1 to 52
  var cardId = Math.floor(Math.random() * 52);
  
  var ranks = ["ace", "2", "3", "4", "5", "6", "7", "8", "9", "ten", "jack", "queen", "king"];
  var suits = ["spades", "hearts", "diamonds", "clubs"];
 
  // % is the modulo operator, so 0 => 0, 1 => 1, ... 12 => 12, 13 => 0, 14 => 1, ...
  // Math.floor(cardId / 13) gets the suit (0, 1, 2, 3)
  return ranks[cardId % 13] + " of " + suits[Math.floor(cardId / 13)];
}

for (var i = 0; i < 10; i++) {
  console.log(newCard());
}

更新

因为这引发了一些关于从非无限套牌(不是重复牌)发牌的讨论,所以这里有一个典型的解决方案,它产生一副完整的牌,洗牌,然后发牌。 cardIdToEnglish 基本上就是上面的解决方案。 shuffle 是 Fisher-Yates shuffle。请注意,从套牌的end 使用pop 进行交易,这可能看起来很奇怪,但请注意这无关紧要。 :-)

function cardIdToEnglish(id) {
    var ranks = ["ace", "2", "3", "4", "5", "6", "7", "8", "9", "ten", "jack", "queen", "king"];
    var suits = ["clubs", "diamonds", "hearts", "spades"];

    return ranks[id % 13] + " of " + suits[Math.floor(id / 13)];
}

function shuffle(arr) {
    // Fisher-Yates shuffle
    var n = arr.length;

    for (var i = 0; i < arr.length - 2; i++) {
        var j = Math.floor(Math.random() * (n - i)) + i;

        // Swap a[i] and a[j]
        var temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

function newDeck() {
    var deck = [];
    for (var i = 0; i < 52; i++) {
        deck.push(i);
    }

    shuffle(deck);
    return deck;
}

var deck = newDeck();
for (var i = 0; i < 52; i++) {
    var card = deck.pop();
    console.log(cardIdToEnglish(card));
}

【讨论】:

  • 非常感谢!这真是个好方法:D
  • 虽然我喜欢这个 - 我想知道是否应该有一种机制来防止选择同一张选定的牌 - 由于相同的随机数,随后的两次抽签选择同一张牌并非不可能。因此,选择方块 2 一方面应该从随后的抽签中删除该选项 - 这就像创建一组选定的卡片对象(数字和花色)并在选择之后但在显示选择之前检查它一样简单。然后如果卡片存在于数组中 - 重新绘制随机数等。
  • 对于使用非无限牌组的游戏,典型的解决方案是先将牌组洗牌,然后从洗好的牌组中抽牌。
  • @gavgrif 检查下面的答案。
  • 谢谢。因此,如果我做对了,使用此代码,在第一张牌被“抽出”之前,甲板被设置在预洗牌位置,然后为您提供第二张、第三张、第四张等按顺序排列的牌。这样对吗?
【解决方案2】:

我将创建一个包含所有卡片的数组作为花色和等级的对象(使用嵌套的 forEach 循环以方便 - 迭代花色和等级数组),然后使用随机数生成卡片 ID。这将给出该卡的等级和花色。然后我从牌堆中取出卡片 - 以防止它被发两次并减少可用卡片的数量)。这样做意味着选择的卡是随机的并且不重复。我还添加了一个按钮,可以重置所有内容并允许进一步处理 10 张牌。

var deck = [];

function setCards(){
  var suits = ["Spades", "Hearts", "Diamonds", "Clubs"];
  var ranks = ["Ace", "2", "3", "4", "5", "6", "7", "8", "9", "Ten", "Jack", "Queen", "King"];
  
  
  //creates an array of card objects that have the rank and suit as properties
 //eg: deck = [ {suit: "Spades", rank: "Ace"}, {suit: "Spades", rank: "2"},...etc] 
  suits.forEach(function(suit){
     ranks.forEach(function(rank){
      var card = {};
      card.suit = suit;
      card.rank = rank;
      deck.push(card);
     });
      
    });
}

function newCard(count) {
  //decreases the count of the deck since some cards have already been dealt
  var remainingCards = 52 - count;
  var index = Math.floor(Math.random() * remainingCards);
  var card= deck[index];
  deck.splice(index,1); //removes the selected card from the deck
  return card;
 }

function dealCards() {
  document.getElementById('hand').innerHTML = '<dt>My Hand </dt>';
  setCards();
  
  for (var i = 0; i < 10; i++) {
  var card = newCard(i);
  document.getElementById('hand').innerHTML += '<dd>'+ card.rank +  ' of '  + card.suit + '</dd>';
  }
}
<button type="button" onclick="dealCards()">Deal New Hand</button>
<hr/>


<dt id = "hand"></dt>

【讨论】:

  • 您好,也感谢您的代码 :)。你能解释一下代码如何准确地记住取出的特定卡片吗?我真的看不透这个
  • deck.splice(index,1); 从deck 中删除卡。因此,代码不是记住哪些卡片已被移除,而是记住哪些卡片仍然存在。
  • @gavgrif 为什么不使用deck.length 而不是传入count?
  • 嗨 Robin - 选择的卡片从牌组中移除(使用 .splice() 使其不再可用),并且因为选择的索引被传递给 newCard() 函数,所以卡片的数量(甲板数组的长度)减少了索引号 - 例如 - newCard(3) 将允许随机数成为初始长度 - 3 os hte 数组的长度始终与甲板阵列的大小。
  • @smarx - 这是因为在每次迭代中的 deck.length 会强制浏览器在每次迭代中计算数组的长度 - 这不如使用上述方法给它一个明确的长度那样高效.在所有情况下 - 最好不要强制动态确定数组的长度 - 必须检查数组的每个项目以查看它是否是最后一个 - 在这里说长度要好得多。在正常情况下——你会计算一次并缓存结果——但在这种情况下,减少长度会禁止这种方法。
【解决方案3】:

这是一个想法:

var deck = ['ace of spades', 'two of spades', 'three of spades', '...']; 
// and so on, all 52 cards
var rand = Math.floor(Math.random() * 52);

console.log(deck[rand]); // will print a random card from the deck

如果您想继续“游戏”,也可以从牌组中取出抽出的牌。

deck.splice(rand, 1);

在此之后,您需要为较小的牌组重新生成随机数:

rand = Math.floor(Math.random() * deck.length); 

【讨论】:

    【解决方案4】:

    这是一个有趣的问题。这就是我的想法……数字卡从 2 到 9。如果我们继续使用整数作为卡值,则杰克、皇后、国王和王牌可以分别表示为 10、11、12 和 13 .

    但是,当然,我们有四个以四种花色形式存在的值。快速谷歌search 显示,在英语国家,花色的相对价值可以从最低到最高排列为梅花、方块、红心和黑桃。因此,我们可以说 2 - 13 是梅花,14 - 25 方块,26 - 37 红心,28 - 49 黑桃。按照这个公式,你的随机数应该在 2 到 49 之间。

    因此,考虑到这一点,您可以编写一个函数来进行一些数学运算,以根据生成的数字确定确切的卡片。显然,您不能两次发同一张牌,因此一旦生成了一个数字,就应该将其存储在发牌数组中。每次生成一个数字时,您都需要检查该数字是否在发牌数组中。如果是,您将需要生成另一个数字。我有一个设置和检查这个数组的方法的想法。如果您认为我的想法值得追求,请告诉我,我会将其添加到此答案中。

    【讨论】:

    • 嘿,如果不是很努力的话,我很乐意看到这个!
    • 看看我对我的解决方案所做的编辑,它显示了一种更典型的处理一副纸牌的方式。 (先洗牌。)“抽一张牌,如果是重复牌,再抽一张”是低效的……随着牌库的耗尽,抽一张新牌需要的时间越来越长,理论上,不能保证你永远成功!最好跟踪剩余的可用卡并始终从该池中提取。 Fisher-Yates 洗牌基本上就是这样做的。
    • 不错! @Robin,我会选择 smarx 的答案。我要投赞成票。
    猜你喜欢
    • 2020-11-10
    • 1970-01-01
    • 1970-01-01
    • 2022-08-05
    • 1970-01-01
    • 2017-06-26
    • 1970-01-01
    • 2017-04-03
    • 1970-01-01
    相关资源
    最近更新 更多