Camicia

Camicia

보통

소개

어느 비 오는 오후, 할머니와 부엌 식탁에 마주 앉아 카드 놀이를 하고 있어요. 이 게임은 Camicia를 할머니 식으로 변형한 거예요.

처음에는 그저 평범한 친선 경기처럼 느껴져요. 카드를 탁탁 내려놓는 소리, 식탁을 가로지르는 웃음, 이따금 Nonna가 짓는 의기양양한 미소. 그런데 게임이 점점 길어지면서 이상한 일이 벌어져요. 같은 카드가 계속해서 돌아와요. 카드를 아무리 내도 끝이 날 기미가 보이지 않아요.

궁금해지기 시작해요. 이 게임은 언젠가 끝날까요? 아니면 영원히 계속할 수 있을까요?

나중에 궁금증을 못 이기고 인터넷을 검색해 보는데, 놀랍게도 방금 겪은 일이 단순히 운이 나빴던 게 아니라는 걸 알게 돼요. 할머니와 함께 가능한 한 가장 긴 수열 중 하나를 우연히 발견한 걸지도 몰라요! 그 순간, 푹 빠져들어요. 가볍게 시작한 게임이 하나의 탐구로 바뀌어요. 이런 게임이 정말 얼마나 오래갈 수 있을까요? 부엌 식탁에서 했던 것보다 더 긴 수열을 찾을 수 있을까요? 어쩌면 새로운 세계 기록을 세울 만큼 길 수도 있을까요?

그리하여 카드 한 벌과 약간의 알고리즘적 재치만을 무기로, 직접 알아보기로 해요...

지침

이 연습 문제에서는 고전 카드 게임인 Camicia와 아주 비슷한 게임을 시뮬레이션해요. 프로그램은 두 플레이어의 덱 초기 상태를 입력받아, 게임이 끝날 때까지(또는 절대 끝나지 않는다는 사실을 알아낼 때까지) 게임을 시뮬레이션해야 해요.

규칙

  • 덱은 두 플레이어에게 나뉘어요. 플레이어의 카드는 왼쪽에서 오른쪽으로 읽는데, 가장 왼쪽 카드가 덱의 맨 위예요.
  • 한 라운드는 두 플레이어가 각각 최소 한 장씩 카드를 내는 것으로 이루어져요.
  • 플레이어는 번갈아 가며 자기 덱의 맨 위 카드를 가운데 더미에 놓아요.
  • 카드가 숫자 카드(2-10)라면, 차례가 그냥 상대 플레이어에게 넘어가요.
  • 카드가 지불 카드라면 벌칙을 지불해야 해요:
    • J → 상대는 카드 1장을 지불해야 해요
    • Q → 상대는 카드 2장을 지불해야 해요
    • K → 상대는 카드 3장을 지불해야 해요
    • A → 상대는 카드 4장을 지불해야 해요
  • 벌칙을 지불하는 플레이어가 또 다른 지불 카드를 공개하면, 그 플레이어는 벌칙 지불을 멈춰요. 그러면 상대 플레이어가 새로운 지불 카드를 기준으로 벌칙을 지불해야 해요.
  • 벌칙이 중단 없이 모두 지불되면, 마지막 지불 카드를 놓은 플레이어가 가운데 더미를 가져다가 자기 덱의 맨 아래에 놓아요. 그다음 그 플레이어가 다음 라운드를 시작해요.
  • 카드가 바닥나서 카드를 낼 수 없게 되면(벌칙을 지불하는 중이든 자기 차례든), 상대 플레이어가 가운데 더미를 가져가요.
  • 플레이어가 가운데 더미에서 카드를 가져가는 순간을 트릭이라고 해요.
  • 트릭 후에 한 플레이어가 모든 카드를 손에 쥐고 있으면 게임이 끝나요.
  • 게임 도중 덱이 이전의 어느 시점과 똑같아지는 순간 게임이 루프에 빠져요. 이때 숫자 카드는 세지 않아요!

예시

끝나는 한 판의 작은 예시예요.

라운드 플레이어 A 플레이어 B 더미 지불할 벌칙
1 2 A 7 8 Q 10 3 4 5 6 K 9 J -
1 A 7 8 Q 10 3 4 5 6 K 9 J 2 -
1 A 7 8 Q 10 4 5 6 K 9 J 2 3 -
1 7 8 Q 10 4 5 6 K 9 J 2 3 A Player B: 4
1 7 8 Q 10 5 6 K 9 J 2 3 A 4 Player B: 3
1 7 8 Q 10 6 K 9 J 2 3 A 4 5 Player B: 2
1 7 8 Q 10 K 9 J 2 3 A 4 5 6 Player B: 1
1 7 8 Q 10 9 J 2 3 A 4 5 6 K Player A: 3
1 8 Q 10 9 J 2 3 A 4 5 6 K 7 Player A: 2
1 Q 10 9 J 2 3 A 4 5 6 K 7 8 Player A: 1
1 10 9 J 2 3 A 4 5 6 K 7 8 Q Player B: 2
1 10 J 2 3 A 4 5 6 K 7 8 Q 9 Player B: 1
1 10 - 2 3 A 4 5 6 K 7 8 Q 9 J Player A: 1
1 - - 2 3 A 4 5 6 K 7 8 Q 9 J 10 -
2 - 2 3 A 4 5 6 K 7 8 Q 9 J 10 - -

status: "finished", cards: 13, tricks: 1

루프에 빠지는 한 판의 작은 예시예요.

라운드 플레이어 A 플레이어 B 더미 지불할 벌칙
1 J 2 3 4 J 5 - -
1 2 3 4 J 5 J Player B: 1
1 2 3 J 5 J 4 -
2 2 3 J 4 J 5 - -
2 3 J 4 J 5 2 -
2 3 J 4 5 2 J Player A: 1
2 J 4 5 2 J 3 -
3 J 4 5 2 J 3 - -
3 J 4 2 J 3 5 -
3 4 2 J 3 5 J Player B: 1
3 4 J 3 5 J 2 -
4 4 5 J 2 J 3 - -

4라운드의 시작은 2라운드의 시작과 같아요. 숫자 카드의 값은 중요하지 않다는 점을 기억해요.

status: "loop", cards: 8, tricks: 3

과제

  • 주어진 입력을 사용해, 위 규칙에 따라 게임을 시뮬레이션해요.
  • 게임에 대해 다음 정보를 알아내요:
    • Status: "finished" 또는 "loop"
    • Cards: 게임 전체에서 낸 카드의 총 장수
    • Tricks: 가운데 더미를 가져간 횟수
Advanced

더 흥미진진한 도전을 원한다면, 끝나는 게임 중 가장 긴 게임의 다른 기록을 찾는 작업은 아직 진행 중이에요. 경우의 수가 653,534,134,886,878,245,000가지(약 6.54해)나 되는데, 아직 다 계산하지 못했어요!

from_quote의 장단점

Card.from_quote 함수는 문자열을 Card로 파싱해요. 덕분에 다음과 같은 코드를 작성할 수 있어요:

card : Card
card = "Q"

이 코드는 컴파일 시점에 Card.from_quote("Q")를 암시적으로 호출해요. 이 방식(또는 아래에서 설명하는 다른 방법)으로 컴파일 시점에 코드를 실행하면 런타임에 파싱과 데이터 검증을 처리할 필요가 없어서 프로그램이 더 빨라질 수 있어요. 게다가 잘못된 데이터(예를 들어 잘못된 날짜 형식)가 런타임이 아니라 컴파일 시점에 거부되므로 앱이 더 견고해져요. 마지막으로, 코드가 깔끔하고 읽기 쉬워져요. 예를 들어, 이 연습 문제의 테스트 중 하나는 다음과 같아요:

result = simulate_game({
    player_a: ["2", "A"],
    player_b: ["3", "4", "5", "6", "K"],
})

하지만 이 코드를 읽는 사람은 simulate_game 함수가 List(Str) 두 개를 담은 레코드를 받는다고 생각할 수 있는데, 실제로는 List(Card) 두 개를 담은 레코드예요. 이 때문에 혼란이나 실수가 생길 수 있어요. 그래서 조금 길지만 더 명확한 형식인 "Q".Card를 선호할 수도 있어요. 이 형식도 컴파일 시점에 from_quote 함수를 호출해요.

직접 작성하는 프로그램에서는 대신 from_str 함수를 사용하는 편이 좋을 수도 있어요:

Card := { ... }.{
    from_str : Str -> Try(Card, [InvalidCard])
    from_str = |card_str| ...
}

이렇게 컴파일 시점에 from_str뿐 아니라 다른 순수 함수도 호출할 수 있어요:

Ok(card) = Card.from_str("Q")

참고: 이 정의를 최상위에 두면 컴파일 시점에 실행돼요(함수가 순수하고 입력이 컴파일 시점에 알려져 있기 때문이에요). 함수 안에서는 런타임에 from_str 함수를 사용할 수 있어요.

힌트

힌트 보기

[]와 [card, .. as rest]를 사용하는 match로 빈 덱과 다음 카드, 남은 카드를 구분해요. { ..state, pile: new_pile } 같은 레코드 업데이트를 사용하면 변경 가능한 상태 없이도 게임 상태 전환을 명시적으로 유지할 수 있어요. 이전에 본 게임 상태를 추적할 때는 Set에 튜플이나 리스트를 키로 저장할 수 있어요.

GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Roc Exercism

Camicia 문제를 시작해 볼 준비가 됐나요?

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