二十一点牌的总价值

Total value of cards in blackjack

我正在编写一个 21 点纸牌游戏,试图慢慢让自己回到 Swift。

我正在尝试获取玩家手牌的总价值。

我遇到的问题是,当我得到一个可以是 1 或 11 的 ace 时,我不确定在针对它进行 XCTest 时如何解决这个问题。

// Card model
struct Card {
    
    // Suit enumerated
    enum Suit : Character {
        case spades = "♠", hearts = "♡", diamonds = "♢", clubs = "♣"
    }
    
    // Rank enumerated
    enum Rank : Int {
        case two = 2, three, four, five, six, seven, eight, nine, ten
        case jack, queen, king, ace
                    
        struct Values {
            let first: Int, second : Int?
        }
        
        var values: Values {
            switch self {
            case .ace:
                return Values(first: 1, second: 11)
            case .jack, .queen, .king:
                return Values(first: 10, second: nil)
            default:
                return Values(first: self.rawValue, second: nil)
            }
        }
    }
    
    let rank: Rank, suit : Suit
    
    var value : Rank.Values {
        return self.rank.values
    }
}

等我来测试的时候,

// XCTest
func testSumOfCardsEquals21() {
    let card1 = Card(rank: .jack, suit: .diamonds) //10
    let card2 = Card(rank: .four, suit: .diamonds) //4
    let card3 = Card(rank: .three, suit: .diamonds) //3
    let card4 = Card(rank: .ace, suit: .diamonds) //1 or 11
    
    let hand = [card1, card2, card3, card4]
    
    var total = 0
    for card in hand {
        let values = card.value
        if (values.second == nil) {
            total += values.first
        } else {
            let secondValue = values.second!
            total += secondValue
        }
    }
    
    // expected 21
    XCTAssertEqual(total, 21) // this will fail and return 28
}

在这种情况下,您希望 A 低。

在您可能只有一张牌(A)的情况下,您希望它高。

我不知道如何解决这样的测试要求。

...

澄清一下,你如何做一个函数,测试计算手牌的总价值return当一张牌可以是两个价值时,预期的总数是21?

感谢您的帮助和时间。

那手牌是 18,而不是 21。

Values 类型可能没有用——这些卡片实际上没有原始值。

不先把所有的牌都累积起来,就不知道怎么计算得分了。

enum Rank: CaseIterable {
  case two, three, four, five, six, seven, eight, nine, ten
  case jack, queen, king
  case ace
}
struct Hand {
  init<Cards: Sequence>(cards: Cards) where Cards.Element == Card {
    let reduction = cards.reduce(into: (score: 0, aceCount: 0)) {
      switch .rank {
      case .jack, .queen, .king:
        [=11=].score += 10
      case .ace:
        [=11=].score += 1
        [=11=].aceCount += 1
      default:
        [=11=].score += try! .rank.caseIndex + 2
      }
    }

    var score = reduction.score
    reduction.aceCount.iterations
      .prefix { score <= 12 }
      .forEach { score += 9 }
    self.score = score
  }

  let score: Int
}
Hand(cards: [card1, card2, card3, card4]).score

public extension ExpressibleByIntegerLiteral where Self: Strideable, Stride: SignedInteger {
  /// *This many* iterations that produce no values.
  var iterations: LazyMapSequence<Range<Self>, Void> {
    (0..<self).lazy.map { _ in }
  }
}
public extension CaseIterable where Self: Equatable {
  /// The first match for this case in `allCases`.
  /// - Throws: `AllCasesError<Self>.noIndex`
  var caseIndex: AllCases.Index {
    get throws {
      do { return try Self.allCases.firstIndex(of: self).unwrapped }
      catch { throw AllCasesError.noIndex(self) }
    }
  }
}

public enum AllCasesError<Case: CaseIterable>: Error {
  /// No `AllCases.Index` corresponds to this case.
  case noIndex(Case)
}
public extension Optional {
  /// Represents that an `Optional` was `nil`.
  enum UnwrapError: Error {
    case `nil`
    case typeMismatch
  }

  /// [An alterative to overloading `??` to throw errors upon `nil`.](
  /// https://forums.swift.org/t/unwrap-or-throw-make-the-safe-choice-easier/14453/7)
  /// - Note: Useful for emulating `break`, with `map`, `forEach`, etc.
  /// - Throws: `UnwrapError` when `nil`.
  var unwrapped: Wrapped {
    get throws {
      switch self {
      case let wrapped?:
        return wrapped
      case nil:
        throw UnwrapError.nil
      }
    }
  }
}