首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >JavaScript BlackJack原型

JavaScript BlackJack原型
EN

Code Review用户
提问于 2019-03-02 07:16:23
回答 2查看 255关注 0票数 6

在BlackJack中组织JavaScript的最佳方法是什么,也许从空白开始?

具体领域:

  • 更新UI
  • Hand类中加入经销商的手以减少重复
  • 可能减少嵌套的IFs
  • 合并分裂

我并不担心这个原型的投注金额/支出/芯片数量,但我当然会补充这一点。

代码:

代码语言:javascript
复制
    BlackJack Early Prototype
    
      .flex-row{
        display: flex;
        flex-flow: row wrap;
        justify-content: space-around;
      }
    
  
  
    deal
    
      
      
      

      
      

      
      

      
    


  
    const suits = [
      {
        digit: 'H',
        word: 'hearts'
      },
      {
        digit: 'C',
        word: 'clubs'
      },
      {
        digit: 'D',
        word: 'diamonds'
      },
      {
        digit: 'S',
        word: 'spades'
      }
    ]
    const cardsWithoutSuits = [
      {
        numeric: 11,
        word: 'ace',
        digit: 'A'
      },
      {
        numeric: 2,
        word: 'two',
      },
      {
        numeric: 3,
        word: 'three',
      },
      {
        numeric: 4,
        word: 'four',
      },
      {
        numeric: 5,
        word: 'five',
      },
      {
        numeric: 6,
        word: 'six',
      },
      {
        numeric: 7,
        word: 'seven',
      },
      {
        numeric: 8,
        word: 'eight',
      },
      {
        numeric: 9,
        word: 'nine',
      },
      {
        numeric: 10,
        word: 'ten',
      },
      {
        numeric: 10,
        word: 'jack',
        digit: 'J'
      },
      {
        numeric: 10,
        word: 'queen',
        digit: 'Q'
      },
      {
        numeric: 10,
        word: 'king',
        digit: 'K'
      }
    ]

    class Hand{
      constructor(bet){
        // assigning the bet value
        this.bet = bet;
        // default values, no cards dealt yet
        this.cards = [];
        this.value = 0;
        this.blackjack = false;
        this.soft = false;
        this.bust = false;
        this.winner = false;
        this.aceQuantity = 0;
        this.canHit = false;
        this.canStay = false;
        this.canSplit = false;
        this.canDouble = false;
        this.finished = false;
        this.textResult = '';
        this.payout = 0;
      };
      evaluate(){
        this.aceQuantity = this.cards.filter(x => x.word === 'ace').length;
        this.value = this.cards.filter(x => x.word !== 'ace').reduce((total, x) => +total + x.numeric, 0);
        this.soft = false;
        for (var i = 0; i < this.aceQuantity; i++) {
          if (this.value + 11 > 21) this.value += 1;
          else {
            this.value +=11;
            if (this.value !== 21) this.soft = true;
          }
        }

        if (this.cards.length === 2) {
          this.canDouble = true;
          this.canSplit = this.cards[0].word === this.cards[1].word;
        } else {
          this.canSplit = false;
          this.canDouble = false;
        }

        if (this.value > 21){
          this.bust = true;
          this.finished = true;
          this.canHit = false;
          this.canStay = false;
        }

        if(this.value === 21){
          this.finished = true;
          this.canHit = false;
          if (this.cards.length === 2) {
            this.blackjack = true;
          }
        }

        if (this.bust) {
          this.textResult = `Busted!`;
          this.payout = 0;
        } else if (this.blackjack) {
          this.textResult = `BlackJack!`;
        } else if (game.dealerFinished) {
          if (this.finished) {
            if (game.dealerValue > 21) {
                            this.textResult = `Winner!`;
            } else if (this.value > game.dealerValue) {
              this.textResult = `Winner!`;
            } else if (this.value === game.dealerValue) {
              this.textResult = `Push`;
            } else if (this.value < game.dealerValue) {
              this.textResult = `Loser`;
            }
          }
        } else {
          this.textResult = `Standing on ${this.value}`;
        }
      }
    }

    function createDeck(decks = 1){
      let deck = [];
      for (let i = 0; i < decks; i++) {
        suits.forEach( x => {
          cardsWithoutSuits.forEach( y => {
            deck.push({
              numeric: y.numeric,
              word: y.word,
              suit: x.word,
              phrase: `${y.word} of ${x.word}`,
              abbr: `${y.hasOwnProperty('digit') ? y.digit : y.numeric}${x.digit}`
            })
          })
        })
      }
      return deck;
    }

    function shuffle(array){
      let array2 = [];
      while (array.length){
        let index = Math.floor(Math.random() * array.length);
        let card = array.splice(index, 1);
        array2.push(card[0]);
      }
      return array2;
    }

    let game = {
      state: 'start',
      deck: [],
      dealerCards: [],
      dealerFinished: false,
      dealerValue: 0,
      hands: [],
      shuffle: function(){
        this.deck = shuffle(createDeck(4));
      },
      deal: function(){
        this.hands.forEach(x => {
          x.cards.push(this.deck.shift());
          x.evaluate();
        });
        this.dealerCards.push(this.deck.shift());
        this.hands.forEach(x => {
          x.cards.push(this.deck.shift());
          x.evaluate();
        });
        this.dealerCards.push(this.deck.shift());
      },
      tempCreateTestHands: function(){
        this.hands.push(new Hand(5));
        this.hands.push(new Hand(10));
        this.hands.push(new Hand(25));
      },
      dealCard: function(hand){
        if (hand === -1) {
          this.dealerCards.push(this.deck.shift());
          updateUI();
        } else {
          this.hands[hand].cards.push(this.deck.shift());
          this.hands[hand].evaluate();
        }
      },
      tempStart: function(){
        game.dealerFinished = false;
        game.hands = [];
        game.dealerCards = [];
        if (game.deck.length < 30) game.shuffle();
        game.tempCreateTestHands();
        game.deal();
        updateUI();
      },
      dealerTurn: function(){
        let aceQuantity = this.dealerCards.filter(x => x.word === 'ace').length;
        let value = this.dealerCards.filter(x => x.word !== 'ace').reduce((total, x) => +total + x.numeric, 0);
        for (var i = 0; i < aceQuantity; i++) {
          if (value + 11 > 21) value += 1;
          else {
            value +=11;
          }
        }
        this.dealerValue = value;
        if (value < 17) {
          this.dealCard(-1);
        } else {
          this.dealerFinished = true;
          game.hands.forEach(x => x.evaluate());
          updateUI();
        }
      }

    }


game.tempStart();




function updateUI(){
  const playersUI = [document.getElementById('hand1'), document.getElementById('hand2'), document.getElementById('hand3')];
  const dealer = document.getElementById('dealer');

  const handsRemaining = game.hands.filter(x => !x.finished).length;
  if (handsRemaining) dealer.innerHTML = `<h2>Dealer</h2><p>Card Hidden</p><p>${game.dealerCards[1].phrase}</p><p>Showing ${game.dealerCards[1].numeric}</p>`;
  else dealer.innerHTML = `<h2>Dealer</h2>` + game.dealerCards.map(x => `<p>${x.phrase}</p>`).join('') + `<p>Total: ${game.dealerValue}</p>`;

  if (!handsRemaining && !game.dealerFinished) {
    game.dealerTurn();
  }

  for (var i = 0; i < game.hands.length; i++) {
    let buttons = '';
    if (game.hands[i].finished){
      buttons += `<div>`;
      if (game.hands[i].busted) buttons +=`BUSTED`;
      else {
        buttons += game.hands[i].textResult;
      }

      buttons += `</div>`
    } else {


      buttons += `<div>
        <button class='hit' onclick="buttonHandler(${i}, 'hit')">HIT</button>
        <button class='stay' onclick="buttonHandler(${i}, 'stay')">STAY</button>
      `;
      if (game.hands[i].canDouble) buttons +=`<button class='double' onclick="buttonHandler(${i}, 'double')">DOUBLE</button>`;
      if (game.hands[i].canSplit) buttons +=`<button class='split' onclick="buttonHandler(${i}, 'split')">SPLIT</button>`;
      buttons += `</div>`;
    }
    playersUI[i].innerHTML = `<h2>Hand ${i + 1}</h2>` + game.hands[i].cards.map(x => `<p>${x.phrase}</p>`).join('') + `<p>Total: ${game.hands[i].soft ? 'Soft' : ''} ${game.hands[i].value}</p>${buttons}`;
  }
}




function buttonHandler(playerHand, action){
  switch (action) {
    case 'stay':
      game.hands[playerHand].finished = true;
      break;
    case 'hit':
      game.dealCard(playerHand);
      break;
    case 'double':
      game.dealCard(playerHand);
      game.hands[playerHand].finished = true;
      game.hands[playerHand].bet = game.hands[playerHand].bet * 2;
      break;
    case 'split':
      console.log(`split function not setup yet...`);
      break;
    default:
      console.log(`error, cannot find ${action} in the switch statement.`);
  }

  updateUI();
}


document.getElementById("deal").onclick = function() {
  // alert("hello");
  game.tempStart();
 }
EN

回答 2

Code Review用户

发布于 2019-07-31 17:52:05

您可以看到我建议的改进这里。作为前一个人,我也偏向于一种更实用的风格。这就是我所做的:

  • 正如前面的答案所指出的那样,代码的格式很差。我运行了更漂亮的标准配置
  • 我删除了一些不必要的评论。很明显,this.bet = bet正在分配投注值。
代码语言:javascript
复制
constructor(bet) {
    // assigning the bet value
    this.bet = bet;
    // default values, no cards dealt yet
    this.cards = [];
    ...
}
  • 有一个*=运算符可以像foo *= 2一样使用,而不是foo = 2 * foo
  • 如果您使用模板文字,那么也使用+操作符连接是没有意义的(注意,模板文字允许多行)。
  • 这有点固执己见,但我认为你应该总是用大括号来表示if-语句。
  • 如果您想知道是否有满足条件的元素(布尔值),请使用some,如果您想知道它们是多少(一个数字),则使用filter().length。在这种情况下,我们希望布尔值表示是否存在任何元素,并且我们不关心这些元素之外有多少元素。使用正确的一个可以提高可读性。
代码语言:javascript
复制
const handsRemaining = game.hands.filter(x => !x.finished).length;

代码语言:javascript
复制
const isHandsRemaining = game.hands.some(x => !x.finished);
  • 为了更好的可读性,您可以使用字符串连在一起的if-else块来模板具有三元结构的文本。
代码语言:javascript
复制
buttons += ``;
if (busted) {
  buttons += `BUSTED`;
} else {
  buttons += textResult;
}
buttons += ``;

代码语言:javascript
复制
buttons += `${busted ? 'BUSTED' : textResult}`;
  • 在适用的情况下,在点符号上使用破坏性方法(略带透明度)
  • 指定哪些玩家存在于一个地方(我刚刚添加了一个const players,但这通常来自服务器),而不是让它分散在标记和代码中的不同位置。
  • 喜欢Array.map而不是Array.forEach
  • 喜欢const而不是let

另外,shuffle应该以您使用的洗牌算法命名,或者有一个注释来解释它是什么算法。

还有更多的改进需要做,但至少这是一个开始:)

编辑:打印

票数 3
EN

Code Review用户

发布于 2019-03-03 12:05:56

注意:我倾向于一种更实用的风格

好:

  • 带有普通旧数据的套装和卡片清单
  • update函数是声明性的。非常好
  • 事件处理程序主要委托给其他函数(但您还可以做更多)

坏:

  • 检查您的代码格式
  • 直接在状态中存储短语和其他与ui相关的东西是不好的。它增加了噪声,它应该在ui逻辑中完成。
  • 比赛的结果应该是某种狂热。用户界面应该决定如何显示枚举。
  • 手课上有太多的状态。您可以删除几乎整个类,并用对普通旧数据进行操作的util函数替换。这些实用函数可以任意操作,包括经销商的
  • 当它计算出谁是赢家时,游戏应该用手来探测信息,而不是反过来。似乎奇怪的是,这只手告诉游戏它已经赢了。如果您还使用了一个单独的函数,您可以使用早期返回来避免嵌套If
  • Update ui应该以必需的状态作为参数。不依赖全局,而是更好地依赖某种状态对象,而不是整个游戏对象。
  • 一个典型的游戏循环由一个更新函数组成: 1.读取事件;2.更新游戏状态;3.更新ui。我建议您也这样做,而不是从按钮处理程序中临时更新它。通过这样做,您将不需要记住从这么多地方更新ui。

通常,在可能的情况下使用更纯的函数来避免跟踪所有这些状态。将状态存储在普通的旧对象中。只有存储所需的状态,ui才能从状态中计算其结果。

一些启示:

代码语言:javascript
复制
state = intitialState

updateGame(event){
    state = handleEvent(state, event)
    state = updateState(state)
    updateUi(state)
}

onButton(event){
    updateGame(event)
}
票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/214591

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档