Skip to content

Commit f024c0c

Browse files
authored
Add camicia (#633)
* add `camicia` * Add .busted file * Format spec_generator.lua * snake_case and dedents * Single quotes * Revert stub
1 parent 1aa327c commit f024c0c

File tree

10 files changed

+1768
-0
lines changed

10 files changed

+1768
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,14 @@
10281028
"trees"
10291029
]
10301030
},
1031+
{
1032+
"slug": "camicia",
1033+
"name": "Camicia",
1034+
"uuid": "08b9ebde-ca0a-4ba5-bd89-8bbc1e007036",
1035+
"practices": [],
1036+
"prerequisites": [],
1037+
"difficulty": 5
1038+
},
10311039
{
10321040
"slug": "connect",
10331041
"name": "Connect",

exercises/practice/camicia/.busted

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
return {
2+
default = {
3+
ROOT = { '.' }
4+
}
5+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Instructions
2+
3+
In this exercise, you will simulate a game very similar to the classic card game **Camicia**.
4+
Your program will receive the initial configuration of two players' decks and must simulate the game until it ends (or detect that it will never end).
5+
6+
## Rules
7+
8+
- The deck is split between **two players**.
9+
The player's cards are read from left to right, where the leftmost card is the top of the deck.
10+
- A round consists of both players playing at least one card.
11+
- Players take turns placing the **top card** of their deck onto a central pile.
12+
- If the card is a **number card** (2-10), play simply passes to the other player.
13+
- If the card is a **payment card**, a penalty must be paid:
14+
- **J** → opponent must pay 1 card
15+
- **Q** → opponent must pay 2 cards
16+
- **K** → opponent must pay 3 cards
17+
- **A** → opponent must pay 4 cards
18+
- If the player paying a penalty reveals another payment card, that player stops paying the penalty.
19+
The other player must then pay a penalty based on the new payment card.
20+
- If the penalty is fully paid without interruption, the player who placed the **last payment card** collects the central pile and places it at the bottom of their deck.
21+
That player then starts the next round.
22+
- If a player runs out of cards and is unable to play a card (either while paying a penalty or when it is their turn), the other player collects the central pile.
23+
- The moment when a player collects cards from the central pile is called a **trick**.
24+
- If a player has all the cards in their possession after a trick, the game **ends**.
25+
- The game **enters a loop** as soon as the decks are identical to what they were earlier during the game, **not** counting number cards!
26+
27+
## Examples
28+
29+
A small example of a match that ends.
30+
31+
| Round | Player A | Player B | Pile | Penalty Due |
32+
| :---- | :----------- | :------------------------- | :------------------------- | :---------- |
33+
| 1 | 2 A 7 8 Q 10 | 3 4 5 6 K 9 J | | - |
34+
| 1 | A 7 8 Q 10 | 3 4 5 6 K 9 J | 2 | - |
35+
| 1 | A 7 8 Q 10 | 4 5 6 K 9 J | 2 3 | - |
36+
| 1 | 7 8 Q 10 | 4 5 6 K 9 J | 2 3 A | Player B: 4 |
37+
| 1 | 7 8 Q 10 | 5 6 K 9 J | 2 3 A 4 | Player B: 3 |
38+
| 1 | 7 8 Q 10 | 6 K 9 J | 2 3 A 4 5 | Player B: 2 |
39+
| 1 | 7 8 Q 10 | K 9 J | 2 3 A 4 5 6 | Player B: 1 |
40+
| 1 | 7 8 Q 10 | 9 J | 2 3 A 4 5 6 K | Player A: 3 |
41+
| 1 | 8 Q 10 | 9 J | 2 3 A 4 5 6 K 7 | Player A: 2 |
42+
| 1 | Q 10 | 9 J | 2 3 A 4 5 6 K 7 8 | Player A: 1 |
43+
| 1 | 10 | 9 J | 2 3 A 4 5 6 K 7 8 Q | Player B: 2 |
44+
| 1 | 10 | J | 2 3 A 4 5 6 K 7 8 Q 9 | Player B: 1 |
45+
| 1 | 10 | - | 2 3 A 4 5 6 K 7 8 Q 9 J | Player A: 1 |
46+
| 1 | - | - | 2 3 A 4 5 6 K 7 8 Q 9 J 10 | - |
47+
| 2 | - | 2 3 A 4 5 6 K 7 8 Q 9 J 10 | - | - |
48+
49+
status: `"finished"`, cards: 13, tricks: 1
50+
51+
This is a small example of a match that loops.
52+
53+
| Round | Player A | Player B | Pile | Penalty Due |
54+
| :---- | :------- | :------- | :---- | :---------- |
55+
| 1 | J 2 3 | 4 J 5 | - | - |
56+
| 1 | 2 3 | 4 J 5 | J | Player B: 1 |
57+
| 1 | 2 3 | J 5 | J 4 | - |
58+
| 2 | 2 3 J 4 | J 5 | - | - |
59+
| 2 | 3 J 4 | J 5 | 2 | - |
60+
| 2 | 3 J 4 | 5 | 2 J | Player A: 1 |
61+
| 2 | J 4 | 5 | 2 J 3 | - |
62+
| 3 | J 4 | 5 2 J 3 | - | - |
63+
| 3 | J 4 | 2 J 3 | 5 | - |
64+
| 3 | 4 | 2 J 3 | 5 J | Player B: 1 |
65+
| 3 | 4 | J 3 | 5 J 2 | - |
66+
| 4 | 4 5 J 2 | J 3 | - | - |
67+
68+
The start of round 4 matches the start of round 2.
69+
Recall, the value of the number cards does not matter.
70+
71+
status: `"loop"`, cards: 8, tricks: 3
72+
73+
## Your Task
74+
75+
- Using the input, simulate the game following the rules above.
76+
- Determine the following information regarding the game:
77+
- **Status**: `"finished"` or `"loop"`
78+
- **Cards**: total number of cards played throughout the game
79+
- **Tricks**: number of times the central pile was collected
80+
81+
~~~~exercism/advanced
82+
For those who want to take on a more exciting challenge, the hunt for other records for the longest game with an end is still open.
83+
There are 653,534,134,886,878,245,000 (approximately 654 quintillion) possibilities, and we haven't calculated them all yet!
84+
~~~~
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Introduction
2+
3+
One rainy afternoon, you sit at the kitchen table playing cards with your grandmother.
4+
The game is her take on [Camicia][bmn].
5+
6+
At first it feels like just another friendly match: cards slapped down, laughter across the table, the occasional victorious grin from Nonna.
7+
But as the game stretches on, something strange happens.
8+
The same cards keep cycling back.
9+
You play card after card, yet the end never seems to come.
10+
11+
You start to wonder.
12+
_Will this game ever finish?
13+
Or could we keep playing forever?_
14+
15+
Later, driven by curiosity, you search online and to your surprise you discover that what happened wasn't just bad luck.
16+
You and your grandmother may have stumbled upon one of the longest possible sequences!
17+
Suddenly, you're hooked.
18+
What began as a casual game has turned into a quest: _how long can such a game really last?_
19+
_Can you find a sequence even longer than the one you played at the kitchen table?_
20+
_Perhaps even long enough to set a new world record?_
21+
22+
And so, armed with nothing but a deck of cards and some algorithmic ingenuity, you decide to investigate...
23+
24+
[bmn]: https://en.wikipedia.org/wiki/Beggar-my-neighbour
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"BNAndras"
4+
],
5+
"files": {
6+
"solution": [
7+
"camicia.lua"
8+
],
9+
"test": [
10+
"camicia_spec.lua"
11+
],
12+
"example": [
13+
".meta/example.lua"
14+
]
15+
},
16+
"blurb": "Simulate the card game and determine whether the match ends or enters an infinite loop.",
17+
"source": "Beggar-My-Neighbour",
18+
"source_url": "https://www.richardpmann.com/beggar-my-neighbour-records.html"
19+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
local function get_value(card)
2+
if card == 'J' then
3+
return 1
4+
elseif card == 'Q' then
5+
return 2
6+
elseif card == 'K' then
7+
return 3
8+
elseif card == 'A' then
9+
return 4
10+
else
11+
return 0
12+
end
13+
end
14+
15+
local function simulate_game(playerA, playerB)
16+
local handA = {}
17+
for _, card in ipairs(playerA) do
18+
table.insert(handA, get_value(card))
19+
end
20+
21+
local handB = {}
22+
for _, card in ipairs(playerB) do
23+
table.insert(handB, get_value(card))
24+
end
25+
26+
local turn = 'A'
27+
local pile = {}
28+
local seen = {}
29+
local totalTricks = 0
30+
local cardsPlayed = 0
31+
local currentDebt = 0
32+
33+
while true do
34+
if #pile == 0 then
35+
local round = table.concat(handA, ',') .. '|' .. table.concat(handB, ',') .. '|' .. turn
36+
if seen[round] then
37+
return { status = 'loop', tricks = totalTricks, cards = cardsPlayed }
38+
end
39+
seen[round] = true
40+
end
41+
42+
local activeHand = turn == 'A' and handA or handB
43+
local otherHand = turn == 'A' and handB or handA
44+
45+
if #activeHand == 0 then
46+
local extraTrick = #pile == 0 and 0 or 1
47+
return { status = 'finished', tricks = totalTricks + extraTrick, cards = cardsPlayed }
48+
end
49+
50+
local cardVal = table.remove(activeHand, 1)
51+
table.insert(pile, cardVal)
52+
cardsPlayed = cardsPlayed + 1
53+
54+
if cardVal > 0 then
55+
currentDebt = cardVal
56+
if turn == 'A' then
57+
turn = 'B'
58+
else
59+
turn = 'A'
60+
end
61+
elseif currentDebt > 0 then
62+
currentDebt = currentDebt - 1
63+
if currentDebt == 0 then
64+
for _, p in ipairs(pile) do
65+
table.insert(otherHand, p)
66+
end
67+
pile = {}
68+
totalTricks = totalTricks + 1
69+
currentDebt = 0
70+
71+
if #handA == 0 or #handB == 0 then
72+
return { status = 'finished', tricks = totalTricks, cards = cardsPlayed }
73+
end
74+
75+
if turn == 'A' then
76+
turn = 'B'
77+
else
78+
turn = 'A'
79+
end
80+
end
81+
else
82+
if turn == 'A' then
83+
turn = 'B'
84+
else
85+
turn = 'A'
86+
end
87+
end
88+
end
89+
end
90+
91+
return { simulate_game = simulate_game }
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
return {
2+
module_name = 'camicia',
3+
4+
generate_test = function(case)
5+
local lines = {}
6+
local function snake_case(str)
7+
local s = str:gsub('%u', function(c)
8+
return '_' .. c:lower()
9+
end)
10+
if s:sub(1, 1) == '_' then
11+
s = s:sub(2)
12+
end
13+
return s
14+
end
15+
local function string_array(arr)
16+
if #arr == 0 then
17+
return "{}"
18+
end
19+
local formatted = {}
20+
for _, v in ipairs(arr) do
21+
table.insert(formatted, string.format("'%s'", v))
22+
end
23+
return string.format("{ %s }", table.concat(formatted, ", "))
24+
end
25+
26+
table.insert(lines, string.format("local playerA = %s", string_array(case.input.playerA)))
27+
table.insert(lines, string.format("local playerB = %s", string_array(case.input.playerB)))
28+
29+
local expected = string.format("{ status = '%s', tricks = %d, cards = %d }", case.expected.status,
30+
case.expected.tricks, case.expected.cards)
31+
32+
table.insert(lines, string.format("local expected = %s", expected))
33+
34+
table.insert(lines, string.format("local result = camicia.%s(playerA, playerB)", snake_case(case.property)))
35+
table.insert(lines, "assert.are.same(expected, result)")
36+
37+
return table.concat(lines, "\n")
38+
end
39+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[0b7f737c-3ecd-4a55-b34d-e65c62a85c28]
13+
description = "two cards, one trick"
14+
15+
[27c19d75-53a5-48e5-b33b-232c3884d4f3]
16+
description = "three cards, one trick"
17+
18+
[9b02dd49-efaf-4b71-adca-a05c18a7c5b0]
19+
description = "four cards, one trick"
20+
21+
[fa3f4479-466a-4734-a001-ab79bfe27260]
22+
description = "the ace reigns supreme"
23+
24+
[07629689-f589-4f54-a6d1-8ce22776ce72]
25+
description = "the king beats ace"
26+
27+
[54d4a1c5-76fb-4d1e-8358-0e0296ac0601]
28+
description = "the queen seduces the king"
29+
30+
[c875500c-ff3d-47a4-bd1e-b60b90da80aa]
31+
description = "the jack betrays the queen"
32+
33+
[436875da-96ca-4149-be22-0b78173b8125]
34+
description = "the 10 just wants to put on a show"
35+
36+
[5be39bb6-1b34-4ce6-a1cd-0fcc142bb272]
37+
description = "simple loop with decks of 3 cards"
38+
39+
[2795dc21-0a2a-4c38-87c2-5a42e1ff15eb]
40+
description = "the story is starting to get a bit complicated"
41+
42+
[6999dfac-3fdc-41e2-b64b-38f4be228712]
43+
description = "two tricks"
44+
45+
[83dcd4f3-e089-4d54-855a-73f5346543a3]
46+
description = "more tricks"
47+
48+
[3107985a-f43e-486a-9ce8-db51547a9941]
49+
description = "simple loop with decks of 4 cards"
50+
51+
[dca32c31-11ed-49f6-b078-79ab912c1f7b]
52+
description = "easy card combination"
53+
54+
[1f8488d0-48d3-45ae-b819-59cedad0a5f4]
55+
description = "easy card combination, inverted decks"
56+
57+
[98878d35-623a-4d05-b81a-7bdc569eb88d]
58+
description = "mirrored decks"
59+
60+
[3e0ba597-ca10-484b-87a3-31a7df7d6da3]
61+
description = "opposite decks"
62+
63+
[92334ddb-aaa7-47fa-ab36-e928a8a6a67c]
64+
description = "random decks #1"
65+
66+
[30477523-9651-4860-84a3-e1ac461bb7fa]
67+
description = "random decks #2"
68+
69+
[20967de8-9e94-4e0e-9010-14bc1c157432]
70+
description = "Kleber 1999"
71+
72+
[9f2fdfe8-27f3-4323-816d-6bce98a9c6f7]
73+
description = "Collins 2006"
74+
75+
[c90b6f8d-7013-49f3-b5cb-14ea006cca1d]
76+
description = "Mann and Wu 2007"
77+
78+
[a3f1fbc5-1d0b-499a-92a5-22932dfc6bc8]
79+
description = "Nessler 2012"
80+
81+
[9cefb1ba-e6d1-4ab7-9d8f-76d8e0976d5f]
82+
description = "Anderson 2013"
83+
84+
[d37c0318-5be6-48d0-ab72-a7aaaff86179]
85+
description = "Rucklidge 2014"
86+
87+
[4305e479-ba87-432f-8a29-cd2bd75d2f05]
88+
description = "Nessler 2021"
89+
90+
[252f5cc3-b86d-4251-87ce-f920b7a6a559]
91+
description = "Nessler 2022"
92+
93+
[b9efcfa4-842f-4542-8112-8389c714d958]
94+
description = "Casella 2024, first infinite game found"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
local function simulate_game(playerA, playerB)
2+
end
3+
4+
return { simulate_game = simulate_game }

0 commit comments

Comments
 (0)