블랙잭

블랙잭

학습 연습 문제

소개

case는 다른 언어에서 흔히 switch라고 부르는, if-else와 같은 제어 표현식의 한 형태예요. case를 사용하면 여러 if-else-if 문을 이어 붙일 수 있고, 코드가 더 읽기 쉬워지며, 강력한 구성도 만들 수 있어요.

case는 case 키워드 뒤에 선택적인 표현식을 붙여 정의해요. 그다음 각 경우마다 when 키워드를 사용하고, 그 뒤에 케이스 표현식과 비교할 표현식을 적어요. when 키워드는 case 키워드보다 더 들여쓰지 않아요. when 키워드 뒤에는 케이스 표현식이 when 표현식과 일치할 때 실행할 코드를 적어요. case는 선택적인 else 문을 가질 수 있는데, 이 문은 다른 어떤 경우에도 일치하지 않을 때 실행돼요.

케이스 표현식을 평가한 다음, 각 when 표현식과 비교해요. 이 비교에는 케이스 포섭 연산자(===)를 사용해요.

case 1
when 1
  puts "One"
when 2
  puts "Two"
else
  puts "Other"
end
# => One

# This is the same as:
tmp = 1
if 1 === tmp
  puts "One"
elsif 2 === tmp
  puts "Two"
else
  puts "Other"
end

케이스 포섭 연산자(===)

케이스 포섭 연산자(===)는 동등 연산자(==)와 조금 달라요. 이 연산자는 오른쪽 값이 왼쪽 값이 나타내는 집합의 원소인지 확인해요. 즉, 각 피연산자를 어느 쪽에 두는지가 중요해요. 어떻게 동작하는지는 왼쪽 값의 타입에 따라 달라요. 예를 들어 Range는 오른쪽 값이 범위 안에 있는지 확인하고, Object는 오른쪽 값이 Object의 인스턴스인지 확인해요.

(1..3) == 1  # => false
(1..3) === 1 # => true

String == "foo"  # => false
String === "foo" # => true

여러 표현식을 사용하는 case

각 값을 쉼표로 구분하면 하나의 when에 여러 표현식을 일치시킬 수 있어요. 표현식 중 하나라도 일치하면 코드를 실행해요. 하나의 경우에 가능한 값이 여러 개일 때 유용해요.

case 1
when 1, 2
  puts "One or two"
else
  puts "Other"
end

범위를 사용하는 case

case는 값이 범위 안에 있는지도 확인할 수 있어요. when 표현식에 범위를 적으면 돼요.

case var
when 1..3
  puts "One to three"
else
  puts "Other"
end

케이스 표현식이 없는 case

케이스 표현식이 필요 없으면 생략할 수 있어요. 이렇게 하면 각 when 표현식이 참인지 평가하게 돼요. 그래서 if-else-if 문처럼 동작해요.

case
when 1 == 1
  puts "One is equal to one"
when 1 > 2
  puts "One is greater than two"
else
  puts "Other"
end

한 줄 when

Crystal은 case 문을 한 줄로 쓸 수 있어요. 간단한 한 줄짜리 문이 있을 때 사용할 수 있어요. 한 줄로 쓸 때는 when <expression> then <statement>. 형태로 적어요. else 문에서 사용할 때는 else <statement> 형태로 적어요.

case var
when 1 then puts "One"
when 2 then puts "Two"
else puts "Other"
end

타입과 함께 사용하는 case

case는 타입과도 일치시킬 수 있어요. 이때는 is_a? 연산자로 변수의 타입을 확인해요. 예를 들어 유니언 타입에서 각 타입마다 분기를 하나씩 만들 수 있어요.

case var
when Int32
  puts "Int32"
when String
  puts "String"
else
  puts "Other"
end

암시적 객체

case는 암시적 객체를 사용할 수 있어요. 예를 들어 when 표현식을 케이스 표현식에 대한 메서드 호출로 쓸 수 있어요. 메서드가 참 같은 값을 반환하는지 거짓 같은 값을 반환하는지에 따라 일치 여부가 결정돼요.

case 1
when .odd?
  puts "Odd"
when .even?
  puts "Even"
end

지침

이 연습 문제에서는 블랙잭 게임의 첫 턴을 시뮬레이션해요.

1. 주어진 카드의 값을 계산해요.

두 장의 카드를 받고, 딜러의 앞면 카드를 볼 수 있어요. 모든 카드는 "ace", "king", "three", "two" 같은 문자열로 표현해요. 각 카드의 값은 다음과 같아요:

card value card value
ace 11 eight 8
two 2 nine 9
three 3 ten 10
four 4 jack 10
five 5 queen 10
six 6 king 10
seven 7 other 0
Note

일반적으로 에이스는 1 또는 11의 값을 가질 수 있지만, 단순하게 하기 위해 여기서는 11의 값만 가진다고 가정해요.

카드를 문자열 인자로 받는 Blackjack.parse_card 메서드를 구현해요. 이 메서드는 카드의 값을 정수로 반환해야 해요.

Blackjack.parse_card("ace")
# => 11

2. 값의 범위에 이름을 붙여요.

플레이어의 점수는 값의 범위로 분류해야 해요. 플레이어 점수는 플레이어의 두 카드 값을 더해서 계산해요. 사용하는 범위는 다음과 같아요:

range value
low [4, 11]
mid [12, 16]
high [17, 20]
blackjack [21]

두 장의 카드를 문자열 인자로 받는 Blackjack.card_range 메서드를 구현해요. 이 메서드는 두 카드가 속하는 값 범위의 이름을 반환해야 해요.

Blackjack.card_range("ten", "king")
# => "high"

3. 첫 턴의 결정 로직을 구현해요.

두 장의 카드와 딜러의 카드에 따라 게임 첫 턴의 전략이 있어요. 이때 선택할 수 있는 것은 다음과 같아요:

  • 스탠드 (S)
  • 히트 (H)
  • 스플릿 (P)
  • 자동 승리 (W)

아직 최적은 아니지만, 친구 Alex가 개발해 온 전략을 따라요. 그 전략은 다음과 같아요:

  • 에이스 한 쌍을 가지고 있으면 항상 스플릿해야 해요.
  • 블랙잭(두 카드의 합이 21인 경우)이고 딜러가 에이스, 그림 카드, 또는 10을 가지고 있지 않다면 자동으로 이겨요. 딜러가 그런 카드 중 하나를 가지고 있다면 스탠드하고, 다른 카드가 공개되기를 기다려야 해요.
  • 카드의 합이 [17, 20] 범위에 있으면 항상 스탠드해야 해요.
  • 카드의 합이 [12, 16] 범위에 있으면 항상 스탠드해야 해요. 단, 딜러가 7 이상을 가지고 있다면 항상 히트해야 해요.
  • 카드의 합이 11 이하이면 항상 히트해야 해요.

세 장의 카드를 문자열 인자로 받는 Blackjack.first_turn 메서드를 구현해요. 이 메서드는 해야 할 결정을 문자열로 반환해야 해요.

Blackjack.first_turn("ace", "ace", "two")
# => "P"
GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Crystal Exercism

블랙잭 문제를 시작해 볼 준비가 됐나요?

Exercism에 가입하고 Crystal 트랙을 개념 26개연습 문제 142개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.