트랙
/
Clojure
Clojure
/
연습 문제
/
카드 게임
카드 게임

카드 게임

학습 연습 문제

소개

Clojure에서 배열은 다른 언어의 배열과 마찬가지로 컬렉션이에요. Lisp 계열의 다른 언어들과 비슷하게, Clojure는 괄호로 배열을 표현해요.

Clojure 배열은 두 가지 방법 중 하나로 만들 수 있어요. list 함수로 배열을 만들거나, 리터럴 배열에 quote를 적용할 수 있어요.

배열이 특별한 이유는 Clojure가 배열을 _호출_로 취급하기 때문이에요. Clojure는 호출이 _연산자_로 시작한다고 기대하는데, 이는 보통 함수예요. 배열의 나머지 항목들은 _피연산자_로 간주되어 함수의 인자가 돼요.

Clojure가 배열을 이렇게 특별하게 취급하기 때문에 리터럴 배열을 직접 만들 수는 없어요. quote나 그 축약형인 '로 배열을 인용하면, 그 배열은 평가하지 말라는 뜻이에요.

요즘 언어들과 달리 Clojure의 배열은 _서로 다른 타입_의 항목을 담을 수 있어요. 예를 들어 '(2 "a" "b" 3)처럼요. 다른 Lisp들과 달리, Clojure에서 빈 배열은 참으로 취급되며 nil이나 false와 같지 않아요.

지침

Elyse는 곧 있을 Vegas 여행에서 포커를 비롯한 여러 카드 게임을 할 생각에 무척 들떠 있어요. "자기 추적"을 무척 좋아하는 편이라서, 기록을 도와줄 작은 함수 몇 개를 만들고 싶어 하고, 그 함수들을 함께 고민해 달라고 도움을 청했어요.

1. 포커 라운드 추적하기

Elyse는 특히 포커를 좋아해서, 자기가 몇 라운드를 플레이했는지, 그리고 그게 _어떤 라운드_인지 기록하고 싶어 해요. 라운드마다 고유한 번호가 있고, 테이블마다 현재 진행 중인 라운드 번호가 표시돼요. Elyse는 테이블 하나를 골라 앉아서 첫 라운드를 시작해요. 세 라운드를 플레이할 계획이에요.

현재 라운드 번호를 받아서, 그 라운드와 앞으로 다가올 _다음 두 라운드_를 담은 list 하나를 반환하는 rounds 함수를 구현해요:

(rounds 27)
;;=> (27 28 29)

2. 모든 라운드를 한곳에 모으기

Elyse는 첫 번째 테이블에서 몇 라운드를 플레이하고, 잠깐 쉬었다가 두 번째 테이블에서 몇 라운드를 더 플레이했어요... 그런데 테이블마다 서로 다른 배열이 생기고 말았어요! 두 배열을 하나로 합쳐서 모든 포커 라운드를 같은 곳에서 추적하고 싶어 해요.

두 개의 배열을 받아서, 첫 번째 list의 모든 라운드 뒤에 두 번째 list의 모든 라운드를 이어 붙인 list 하나를 반환하는 concat-rounds 함수를 구현해요:

(concat-rounds '(27 28 29) '(35 36))
;;=> (27 28 29 35 36)

3. 이전 라운드 찾기

이전 포커 라운드 몇 개에 대해 이야기하던 중, 다른 플레이어가 그중 두 라운드가 참 비슷하게 흘러갔다고 말해요. Elyse는 자기가 그 라운드를 플레이했는지 아닌지 잘 모르겠어요.

플레이한 라운드의 배열과 라운드 번호, 이렇게 두 개의 인자를 받는 contains-round? 함수를 구현해요. 라운드가 플레이한 라운드 배열에 있으면 true를, 없으면 false를 반환해요:

(contains-round? '(27 28 29 35 36) 29)
;;=> true

(contains-round? '(27 28 29 35 36) 30)
;;=> false

4. 카드 값 평균 내기

Elyse는 Black Joe라는 새 게임을 해보고 싶어 해요. 이 게임은, 손에 든 카드의 합이 목표 값이 되게 하는 것이 목표인 Black Jack과 비슷하지만, Black Joe에서는 카드 값의 _평균_이 7이 되게 하는 것이 목표예요. 평균은 카드 값을 모두 더한 뒤 그 합을 손에 든 카드 수로 나누면 구할 수 있어요.

Black Joe 패의 카드 값 평균을 반환하는 card-average 함수를 구현해요.

(card-average '(5 6 7))
;;=> 6.0

5. 다른 평균 구하기

Black Joe에서는 속도가 중요해요. Elyse는 평균을 더 빠르게 구할 방법을 찾아보려고 해요.

평균 비슷한 값을 구하는 두 가지 방법을 떠올렸어요:

  • 패에서 첫 번째 값과 마지막 값의 평균 구하기
  • 패의 중앙값(가운데 카드) 사용하기

패에 든 카드 값들을 담은 배열 hand가 주어질 때, approx-average? 함수를 구현해요.

위에서 말한 전략 중 하나 or _둘 다_가 _실제 평균_과 같은 값을 내놓으면 true를 반환해요.

참고: 중앙값을 더 쉽게 찾을 수 있도록, 모든 패의 카드 수는 홀수예요.

(approx-average? '(1 2 3))
;;=> true

(approx-average? '(2 3 4 8 8))
;;=> true

(approx-average? '(1 2 3 5 9))
;;=> false

6. 더 많은 평균 기법

평균 실험 결과에 흥미를 느낀 Elyse는 짝수 위치의 카드 평균과 홀수 위치의 카드 평균이 같은 결과를 내는지 궁금해졌어요. 이번에도 테스트 함수를 만들 차례예요!

짝수 인덱스의 카드 평균이 홀수 인덱스의 카드 평균과 같은지를 나타내는 불리언을 반환하는 average-even-odd? 함수를 구현해요.

(average-even-odd? '(1 2 3))
;;=> true

(average-even-odd? '(1 2 3 4))
;;=> false

7. 보너스 라운드 규칙

Black Joe에서는 11번째 패마다 보너스 규칙이 적용되는 보너스 패예요. 마지막으로 뽑은 카드가 잭이면 그 값을 두 배로 만들어요.

패를 받아서 마지막 카드가 잭(11)인지 확인하는 maybe-double-last 함수를 구현해요. 마지막 카드가 잭(11)이라면, 패를 반환하기 전에 그 값을 두 배로 만들어요.

(maybe-double-last '(5 9 11))
;;=> '(5 9 22)

(maybe-double-last '(5 9 10))
;;=> '(5 9 10)
GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Clojure Exercism

카드 게임 문제를 시작해 볼 준비가 됐나요?

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