loading...

Daily Challenge #198 - 21 Blackjack

thepracticaldev profile image dev.to staff ・1 min read

Implement a function that determines the score of a hand in the card game 21 Blackjack.

The function will receive an array filled with strings that represent each card in the hand. Your function should return the score of the hand as an integer.

Number cards count as their face value (2 through 10). Jack, Queen and King count as 10. An Ace can be counted as either 1 or 11.

Return the highest score of the cards that is less than or equal to 21. If there is no score less than or equal to 21 return the smallest score more than 21.

Examples

["A"] ==> 11
["5", "4", "3", "2", "A", "K"] ==> 25

Tests

["A", "J"]

["A", "10", "A"]

["5", "3", "7"]


This challenge comes from jodymgustafson on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Posted on by:

thepracticaldev profile

dev.to staff

@thepracticaldev

The hardworking team behind dev.to ❤️

Discussion

pic
Editor guide
 

In Python:

import sys

FACE_CARDS = ['J', 'Q', 'K']


def score_hand(hand):
    '''
    Calculate the total score of the hand.
    '''
    hand_score = 0

    for card in hand:
        if card in FACE_CARDS:
            hand_score += 10
            continue

        if card == 'A':
            hand_score += 1
            if hand_score < 12:
                hand_score += 10
            continue
        try:
            hand_score += int(card)
            continue
        except ValueError:
            print('Invalid card in hand: {}'.format(card))
        except TypeError:
            print('Card is an invalid type: {}'.format(type(card)))
    return hand_score


print(str(score_hand(['A'])))
print(str(score_hand(['5', '4', '3', '2', 'A', 'K'])))
print(str(score_hand(['5', '3', '7'])))
print(str(score_hand(['A', 'J'])))
print(str(score_hand(['A', '10', 'A'])))
 

Javascript

This solution doesn't consider the fact that an ace could be both a 1 and an 11 (2+ aces in a hand). But this solution does solve all of the tests


const blackJack = (cards) => {
  let total = [0, 0]
  cards.forEach((card) => {
    switch(card) {
      case "2":
      case "3":
      case "4":
      case "5":
      case "6":
      case "7":
      case "8":
      case "9":
      case "10":
        total[0] += parseInt(card);
        total[1] += parseInt(card);
        break;
      case "J":
      case "Q":
      case "K":
        total[0] += 10;
        total[1] += 10;
        break;
      case "A":
        total[0] += 1;
        total[1] += 11;
        break;
    }
  })

  return Math.max(...total) <= 21 ? Math.max(...total) : Math.min(...total)
}

Codepen

 

Elixir

Some recursion

defmodule Cards do
  def blackjack(list), do: _blackjack(0, 0, list)

  defp _blackjack(n, a, []), do: n + _get_as(n, a)
  defp _blackjack(n, a, [ head | tail ]) do
    cond do
      head == "A" -> _blackjack(n, a + 1, tail)
      Enum.member?([ "J", "Q", "K" ], head) -> _blackjack(n + 10, a, tail)
      true -> _blackjack(n + String.to_integer(head), a, tail)
    end
  end

  defp _get_as(_n, 0), do: 0
  defp _get_as(n, a) when n + a - 1 < 11, do: a + 10
  defp _get_as(_n, a), do: a
end
 

Clojure ✌🏿

(ns dailyChallenge.one-ninety-eight)

(defn scoreHand [hand]
  (let [WIN 21
        CARDS {:2 2, :3 3, :4 4, :5 5, 
               :6 6 :7 7, :8 8, :9 9, 
               [:J :Q :K] 10, :A [1 11]}] ;;this is why I ♥ Clojure
    (for [card hand]
         (apply + (CARDS card))...To be continued (need to save somewhere)))
 

In Ruby. Iterate over each card and total as you go. If it's a face card, count is as 10. If it's a number card, count is as what it is. If it's an ace and the current score is below 21 and counting an ace as a 11 wouldn't push it over 21, count is as 11. Otherwise, count is a 1.

def total(array)
  count = 0
  face_cards = ["J","Q","K"]

  array.each do |card|
    if face_cards.include?(card)
      count = count + 10
    elsif card == "A"
      count = count + 1
    else
      count = count + card.to_i
    end
  end

  if count < 21 && array.include?("A")
    if count + 10 <= 21
      count = count + 10
    end
  end

  count

end

The examples

total(["A"]) # ==> 11
total(["5", "4", "3", "2", "A", "K"]) # ==> 25
total(["A", "J"]) # ==> 21
total(["A", "10", "A"]) # ==> 12
total(["5", "3", "7"]) # ==> 21

A possible flaw is that you should be seeing cards one at a time, instead of as a whole then deciding on how to handle aces.

A .map or .inject method could clean this up in Ruby.

 

Isn't (["5", "3", "7"]) # ==> 21 wrong.

 

Python

var = input("Enter the string: ")
var = var.split(' ')

sum = 0;
a = 0
for i in var:
        if i == 'K' or i == 'J' or i == 'Q':
                sum += 10
        elif i == 'A':
                a += 1
        else:
                sum += int(i)

if sum + a*11 <= 21:
        sum += a*11
elif sum + a <= 21:
        sum += a
else:
        for i in range(a):
                if sum <= 21:
                        sum += 11
                else:
                        sum += a-i  
                        break

print(sum)
 

C++

int scoreHand(vector<string> cards){
    unordered_map<string,int> scoreCount; // holds the score of each card
    for(int i=2;i<=10;i++){
        scoreCount[to_string(i)] = i;
    }
    scoreCount["A"] = 1; // store the score of A as 1 (minimum)
    scoreCount["J"] = 10;
    scoreCount["K"] = 10;
    scoreCount["Q"] = 10;

    unordered_map<string, int> cardCount; // holds the number of times cards are apperaing
    int score = 0; // minimum score (i.e. score with A as 1)
    for(string s : cards){
        score += scoreCount[s];
        cardCount[s]++;
    }

    // if score is 21 return 21
    if(score == 21)
        return score;

    // if score < 21 try to maximize it
    // but keep it less than or equal to 21
    if(score < 21){
        for(int i=0;i<cardCount["A"];i++){
            if(score > 11){
                return score;
            }
            // add score of A to score
            // score = score - 1 + 11
            score += 10;
        }
    }

    // no need to write the case when score > 21
    // because we are always starting from minimum score possible
    return score;
}

Code can be optimized by caculating scoreCount seperately and then passing
it as an argument in the function.

 

Test cases --

cout << scoreHand({"A"}) << "\n";
cout << scoreHand({"5", "4", "3", "2", "A", "K"}) << "\n";
cout << scoreHand({"A", "J"}) << "\n";
cout << scoreHand({"A", "10", "A"}) << "\n";
cout << scoreHand({"5", "3", "7"}) << "\n";
cout << scoreHand({"5", "5", "A"}) << "\n";
cout << scoreHand({"A", "A", "A"}) << "\n";
cout << scoreHand({"A", "A"}) << "\n";
cout << scoreHand({"5", "4", "3", "10", "K"}) << "\n";

Output --

11
25
21
12
15
21
13
12
32
 

Elm

From what I understood about the game, the cards are revealed at each draw. I may have implemented a wrong version though.

module BlackJack exposing (score)

import List exposing (foldl)
import String exposing (toInt)
import Maybe exposing (withDefault)


cardToScore : String -> Int -> Int
cardToScore card accumulatedScore =
  case card of
    "A" ->
      if accumulatedScore + 11 > 21 then
        accumulatedScore + 1

      else
        accumulatedScore + 11

    otherCard ->
        accumulatedScore + ( otherCard |> toInt |> withDefault 10 )


score : List String -> Int
score cards =
  foldl cardToScore 0 cards

Tests

module BlackJackTest exposing (suite)

import Expect exposing (Expectation, equal)
import Test exposing (Test, describe, test)
import BlackJack exposing (score)


suite : Test
suite =
  describe "BlackJack.elm"
    [ describe "score"
      [ test "It should return 11" <| \_ ->
          equal 11 <| score [ "A" ]
      , test "It should return 25" <| \_ ->
          equal 25 <| score [ "5", "4", "3", "2", "A", "K" ]
      , test "It should return 21" <| \_ ->
          equal 21 <| score [ "A", "J" ]
      , test "It should return 22" <| \_ ->
          equal 22 <| score [ "A", "10", "A" ]
      , test "It should return 15" <| \_ ->
          equal 15 <| score [ "5", "3", "7" ]
      ]
    ]