diff --git a/config.json b/config.json index 5743640..c4a6b0b 100644 --- a/config.json +++ b/config.json @@ -1028,6 +1028,14 @@ "trees" ] }, + { + "slug": "camicia", + "name": "Camicia", + "uuid": "08b9ebde-ca0a-4ba5-bd89-8bbc1e007036", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, { "slug": "connect", "name": "Connect", diff --git a/exercises/practice/camicia/.busted b/exercises/practice/camicia/.busted new file mode 100644 index 0000000..86b84e7 --- /dev/null +++ b/exercises/practice/camicia/.busted @@ -0,0 +1,5 @@ +return { + default = { + ROOT = { '.' } + } +} diff --git a/exercises/practice/camicia/.docs/instructions.md b/exercises/practice/camicia/.docs/instructions.md new file mode 100644 index 0000000..db62fce --- /dev/null +++ b/exercises/practice/camicia/.docs/instructions.md @@ -0,0 +1,84 @@ +# Instructions + +In this exercise, you will simulate a game very similar to the classic card game **Camicia**. +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). + +## Rules + +- The deck is split between **two players**. + The player's cards are read from left to right, where the leftmost card is the top of the deck. +- A round consists of both players playing at least one card. +- Players take turns placing the **top card** of their deck onto a central pile. +- If the card is a **number card** (2-10), play simply passes to the other player. +- If the card is a **payment card**, a penalty must be paid: + - **J** → opponent must pay 1 card + - **Q** → opponent must pay 2 cards + - **K** → opponent must pay 3 cards + - **A** → opponent must pay 4 cards +- If the player paying a penalty reveals another payment card, that player stops paying the penalty. + The other player must then pay a penalty based on the new payment card. +- 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. + That player then starts the next round. +- 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. +- The moment when a player collects cards from the central pile is called a **trick**. +- If a player has all the cards in their possession after a trick, the game **ends**. +- The game **enters a loop** as soon as the decks are identical to what they were earlier during the game, **not** counting number cards! + +## Examples + +A small example of a match that ends. + +| Round | Player A | Player B | Pile | Penalty Due | +| :---- | :----------- | :------------------------- | :------------------------- | :---------- | +| 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 + +This is a small example of a match that loops. + +| Round | Player A | Player B | Pile | Penalty Due | +| :---- | :------- | :------- | :---- | :---------- | +| 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 | - | - | + +The start of round 4 matches the start of round 2. +Recall, the value of the number cards does not matter. + +status: `"loop"`, cards: 8, tricks: 3 + +## Your Task + +- Using the input, simulate the game following the rules above. +- Determine the following information regarding the game: + - **Status**: `"finished"` or `"loop"` + - **Cards**: total number of cards played throughout the game + - **Tricks**: number of times the central pile was collected + +~~~~exercism/advanced +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. +There are 653,534,134,886,878,245,000 (approximately 654 quintillion) possibilities, and we haven't calculated them all yet! +~~~~ diff --git a/exercises/practice/camicia/.docs/introduction.md b/exercises/practice/camicia/.docs/introduction.md new file mode 100644 index 0000000..761d8a8 --- /dev/null +++ b/exercises/practice/camicia/.docs/introduction.md @@ -0,0 +1,24 @@ +# Introduction + +One rainy afternoon, you sit at the kitchen table playing cards with your grandmother. +The game is her take on [Camicia][bmn]. + +At first it feels like just another friendly match: cards slapped down, laughter across the table, the occasional victorious grin from Nonna. +But as the game stretches on, something strange happens. +The same cards keep cycling back. +You play card after card, yet the end never seems to come. + +You start to wonder. +_Will this game ever finish? +Or could we keep playing forever?_ + +Later, driven by curiosity, you search online and to your surprise you discover that what happened wasn't just bad luck. +You and your grandmother may have stumbled upon one of the longest possible sequences! +Suddenly, you're hooked. +What began as a casual game has turned into a quest: _how long can such a game really last?_ +_Can you find a sequence even longer than the one you played at the kitchen table?_ +_Perhaps even long enough to set a new world record?_ + +And so, armed with nothing but a deck of cards and some algorithmic ingenuity, you decide to investigate... + +[bmn]: https://en.wikipedia.org/wiki/Beggar-my-neighbour diff --git a/exercises/practice/camicia/.meta/config.json b/exercises/practice/camicia/.meta/config.json new file mode 100644 index 0000000..64380c2 --- /dev/null +++ b/exercises/practice/camicia/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "camicia.lua" + ], + "test": [ + "camicia_spec.lua" + ], + "example": [ + ".meta/example.lua" + ] + }, + "blurb": "Simulate the card game and determine whether the match ends or enters an infinite loop.", + "source": "Beggar-My-Neighbour", + "source_url": "https://www.richardpmann.com/beggar-my-neighbour-records.html" +} diff --git a/exercises/practice/camicia/.meta/example.lua b/exercises/practice/camicia/.meta/example.lua new file mode 100644 index 0000000..87cb144 --- /dev/null +++ b/exercises/practice/camicia/.meta/example.lua @@ -0,0 +1,91 @@ +local function get_value(card) + if card == 'J' then + return 1 + elseif card == 'Q' then + return 2 + elseif card == 'K' then + return 3 + elseif card == 'A' then + return 4 + else + return 0 + end +end + +local function simulate_game(playerA, playerB) + local handA = {} + for _, card in ipairs(playerA) do + table.insert(handA, get_value(card)) + end + + local handB = {} + for _, card in ipairs(playerB) do + table.insert(handB, get_value(card)) + end + + local turn = 'A' + local pile = {} + local seen = {} + local totalTricks = 0 + local cardsPlayed = 0 + local currentDebt = 0 + + while true do + if #pile == 0 then + local round = table.concat(handA, ',') .. '|' .. table.concat(handB, ',') .. '|' .. turn + if seen[round] then + return { status = 'loop', tricks = totalTricks, cards = cardsPlayed } + end + seen[round] = true + end + + local activeHand = turn == 'A' and handA or handB + local otherHand = turn == 'A' and handB or handA + + if #activeHand == 0 then + local extraTrick = #pile == 0 and 0 or 1 + return { status = 'finished', tricks = totalTricks + extraTrick, cards = cardsPlayed } + end + + local cardVal = table.remove(activeHand, 1) + table.insert(pile, cardVal) + cardsPlayed = cardsPlayed + 1 + + if cardVal > 0 then + currentDebt = cardVal + if turn == 'A' then + turn = 'B' + else + turn = 'A' + end + elseif currentDebt > 0 then + currentDebt = currentDebt - 1 + if currentDebt == 0 then + for _, p in ipairs(pile) do + table.insert(otherHand, p) + end + pile = {} + totalTricks = totalTricks + 1 + currentDebt = 0 + + if #handA == 0 or #handB == 0 then + return { status = 'finished', tricks = totalTricks, cards = cardsPlayed } + end + + if turn == 'A' then + turn = 'B' + else + turn = 'A' + end + end + else + if turn == 'A' then + turn = 'B' + else + turn = 'A' + end + end + end +end + +return { simulate_game = simulate_game } diff --git a/exercises/practice/camicia/.meta/spec_generator.lua b/exercises/practice/camicia/.meta/spec_generator.lua new file mode 100644 index 0000000..7328187 --- /dev/null +++ b/exercises/practice/camicia/.meta/spec_generator.lua @@ -0,0 +1,39 @@ +return { + module_name = 'camicia', + + generate_test = function(case) + local lines = {} + local function snake_case(str) + local s = str:gsub('%u', function(c) + return '_' .. c:lower() + end) + if s:sub(1, 1) == '_' then + s = s:sub(2) + end + return s + end + local function string_array(arr) + if #arr == 0 then + return "{}" + end + local formatted = {} + for _, v in ipairs(arr) do + table.insert(formatted, string.format("'%s'", v)) + end + return string.format("{ %s }", table.concat(formatted, ", ")) + end + + table.insert(lines, string.format("local playerA = %s", string_array(case.input.playerA))) + table.insert(lines, string.format("local playerB = %s", string_array(case.input.playerB))) + + local expected = string.format("{ status = '%s', tricks = %d, cards = %d }", case.expected.status, + case.expected.tricks, case.expected.cards) + + table.insert(lines, string.format("local expected = %s", expected)) + + table.insert(lines, string.format("local result = camicia.%s(playerA, playerB)", snake_case(case.property))) + table.insert(lines, "assert.are.same(expected, result)") + + return table.concat(lines, "\n") + end +} diff --git a/exercises/practice/camicia/.meta/tests.toml b/exercises/practice/camicia/.meta/tests.toml new file mode 100644 index 0000000..18d3fdd --- /dev/null +++ b/exercises/practice/camicia/.meta/tests.toml @@ -0,0 +1,94 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[0b7f737c-3ecd-4a55-b34d-e65c62a85c28] +description = "two cards, one trick" + +[27c19d75-53a5-48e5-b33b-232c3884d4f3] +description = "three cards, one trick" + +[9b02dd49-efaf-4b71-adca-a05c18a7c5b0] +description = "four cards, one trick" + +[fa3f4479-466a-4734-a001-ab79bfe27260] +description = "the ace reigns supreme" + +[07629689-f589-4f54-a6d1-8ce22776ce72] +description = "the king beats ace" + +[54d4a1c5-76fb-4d1e-8358-0e0296ac0601] +description = "the queen seduces the king" + +[c875500c-ff3d-47a4-bd1e-b60b90da80aa] +description = "the jack betrays the queen" + +[436875da-96ca-4149-be22-0b78173b8125] +description = "the 10 just wants to put on a show" + +[5be39bb6-1b34-4ce6-a1cd-0fcc142bb272] +description = "simple loop with decks of 3 cards" + +[2795dc21-0a2a-4c38-87c2-5a42e1ff15eb] +description = "the story is starting to get a bit complicated" + +[6999dfac-3fdc-41e2-b64b-38f4be228712] +description = "two tricks" + +[83dcd4f3-e089-4d54-855a-73f5346543a3] +description = "more tricks" + +[3107985a-f43e-486a-9ce8-db51547a9941] +description = "simple loop with decks of 4 cards" + +[dca32c31-11ed-49f6-b078-79ab912c1f7b] +description = "easy card combination" + +[1f8488d0-48d3-45ae-b819-59cedad0a5f4] +description = "easy card combination, inverted decks" + +[98878d35-623a-4d05-b81a-7bdc569eb88d] +description = "mirrored decks" + +[3e0ba597-ca10-484b-87a3-31a7df7d6da3] +description = "opposite decks" + +[92334ddb-aaa7-47fa-ab36-e928a8a6a67c] +description = "random decks #1" + +[30477523-9651-4860-84a3-e1ac461bb7fa] +description = "random decks #2" + +[20967de8-9e94-4e0e-9010-14bc1c157432] +description = "Kleber 1999" + +[9f2fdfe8-27f3-4323-816d-6bce98a9c6f7] +description = "Collins 2006" + +[c90b6f8d-7013-49f3-b5cb-14ea006cca1d] +description = "Mann and Wu 2007" + +[a3f1fbc5-1d0b-499a-92a5-22932dfc6bc8] +description = "Nessler 2012" + +[9cefb1ba-e6d1-4ab7-9d8f-76d8e0976d5f] +description = "Anderson 2013" + +[d37c0318-5be6-48d0-ab72-a7aaaff86179] +description = "Rucklidge 2014" + +[4305e479-ba87-432f-8a29-cd2bd75d2f05] +description = "Nessler 2021" + +[252f5cc3-b86d-4251-87ce-f920b7a6a559] +description = "Nessler 2022" + +[b9efcfa4-842f-4542-8112-8389c714d958] +description = "Casella 2024, first infinite game found" diff --git a/exercises/practice/camicia/camicia.lua b/exercises/practice/camicia/camicia.lua new file mode 100644 index 0000000..78fa7ef --- /dev/null +++ b/exercises/practice/camicia/camicia.lua @@ -0,0 +1,4 @@ +local function simulate_game(playerA, playerB) +end + +return { simulate_game = simulate_game } diff --git a/exercises/practice/camicia/camicia_spec.lua b/exercises/practice/camicia/camicia_spec.lua new file mode 100644 index 0000000..ae12772 --- /dev/null +++ b/exercises/practice/camicia/camicia_spec.lua @@ -0,0 +1,1400 @@ +local camicia = require('camicia') + +describe('camicia', function() + it('two cards, one trick', function() + local playerA = { '2' } + local playerB = { '3' } + local expected = { status = 'finished', tricks = 1, cards = 2 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('three cards, one trick', function() + local playerA = { '2', '4' } + local playerB = { '3' } + local expected = { status = 'finished', tricks = 1, cards = 3 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('four cards, one trick', function() + local playerA = { '2', '4' } + local playerB = { '3', '5', '6' } + local expected = { status = 'finished', tricks = 1, cards = 4 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('the ace reigns supreme', function() + local playerA = { '2', 'A' } + local playerB = { '3', '4', '5', '6', '7' } + local expected = { status = 'finished', tricks = 1, cards = 7 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('the king beats ace', function() + local playerA = { '2', 'A' } + local playerB = { '3', '4', '5', '6', 'K' } + local expected = { status = 'finished', tricks = 1, cards = 7 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('the queen seduces the king', function() + local playerA = { '2', 'A', '7', '8', 'Q' } + local playerB = { '3', '4', '5', '6', 'K' } + local expected = { status = 'finished', tricks = 1, cards = 10 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('the jack betrays the queen', function() + local playerA = { '2', 'A', '7', '8', 'Q' } + local playerB = { '3', '4', '5', '6', 'K', '9', 'J' } + local expected = { status = 'finished', tricks = 1, cards = 12 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('the 10 just wants to put on a show', function() + local playerA = { '2', 'A', '7', '8', 'Q', '10' } + local playerB = { '3', '4', '5', '6', 'K', '9', 'J' } + local expected = { status = 'finished', tricks = 1, cards = 13 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('simple loop with decks of 3 cards', function() + local playerA = { 'J', '2', '3' } + local playerB = { '4', 'J', '5' } + local expected = { status = 'loop', tricks = 3, cards = 8 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('the story is starting to get a bit complicated', function() + local playerA = { + '2', + '6', + '6', + 'J', + '4', + 'K', + 'Q', + '10', + 'K', + 'J', + 'Q', + '2', + '3', + 'K', + '5', + '6', + 'Q', + 'Q', + 'A', + 'A', + '6', + '9', + 'K', + 'A', + '8', + 'K', + '2', + 'A', + '9', + 'A', + 'Q', + '4', + 'K', + 'K', + 'K', + '3', + '5', + 'K', + '8', + 'Q', + '3', + 'Q', + '7', + 'J', + 'K', + 'J', + '9', + 'J', + '3', + '3', + 'K', + 'K', + 'Q', + 'A', + 'K', + '7', + '10', + 'A', + 'Q', + '7', + '10', + 'J', + '4', + '5', + 'J', + '9', + '10', + 'Q', + 'J', + 'J', + 'K', + '6', + '10', + 'J', + '6', + 'Q', + 'J', + '5', + 'J', + 'Q', + 'Q', + '8', + '3', + '8', + 'A', + '2', + '6', + '9', + 'K', + '7', + 'J', + 'K', + 'K', + '8', + 'K', + 'Q', + '6', + '10', + 'J', + '10', + 'J', + 'Q', + 'J', + '10', + '3', + '8', + 'K', + 'A', + '6', + '9', + 'K', + '2', + 'A', + 'A', + '10', + 'J', + '6', + 'A', + '4', + 'J', + 'A', + 'J', + 'J', + '6', + '2', + 'J', + '3', + 'K', + '2', + '5', + '9', + 'J', + '9', + '6', + 'K', + 'A', + '5', + 'Q', + 'J', + '2', + 'Q', + 'K', + 'A', + '3', + 'K', + 'J', + 'K', + '2', + '5', + '6', + 'Q', + 'J', + 'Q', + 'Q', + 'J', + '2', + 'J', + '9', + 'Q', + '7', + '7', + 'A', + 'Q', + '7', + 'Q', + 'J', + 'K', + 'J', + 'A', + '7', + '7', + '8', + 'Q', + '10', + 'J', + '10', + 'J', + 'J', + '9', + '2', + 'A', + '2' + } + local playerB = { + '7', + '2', + '10', + 'K', + '8', + '2', + 'J', + '9', + 'A', + '5', + '6', + 'J', + 'Q', + '6', + 'K', + '6', + '5', + 'A', + '4', + 'Q', + '7', + 'J', + '7', + '10', + '2', + 'Q', + '8', + '2', + '2', + 'K', + 'J', + 'A', + '5', + '5', + 'A', + '4', + 'Q', + '6', + 'Q', + 'K', + '10', + '8', + 'Q', + '2', + '10', + 'J', + 'A', + 'Q', + '8', + 'Q', + 'Q', + 'J', + 'J', + 'A', + 'A', + '9', + '10', + 'J', + 'K', + '4', + 'Q', + '10', + '10', + 'J', + 'K', + '10', + '2', + 'J', + '7', + 'A', + 'K', + 'K', + 'J', + 'A', + 'J', + '10', + '8', + 'K', + 'A', + '7', + 'Q', + 'Q', + 'J', + '3', + 'Q', + '4', + 'A', + '3', + 'A', + 'Q', + 'Q', + 'Q', + '5', + '4', + 'K', + 'J', + '10', + 'A', + 'Q', + 'J', + '6', + 'J', + 'A', + '10', + 'A', + '5', + '8', + '3', + 'K', + '5', + '9', + 'Q', + '8', + '7', + '7', + 'J', + '7', + 'Q', + 'Q', + 'Q', + 'A', + '7', + '8', + '9', + 'A', + 'Q', + 'A', + 'K', + '8', + 'A', + 'A', + 'J', + '8', + '4', + '8', + 'K', + 'J', + 'A', + '10', + 'Q', + '8', + 'J', + '8', + '6', + '10', + 'Q', + 'J', + 'J', + 'A', + 'A', + 'J', + '5', + 'Q', + '6', + 'J', + 'K', + 'Q', + '8', + 'K', + '4', + 'Q', + 'Q', + '6', + 'J', + 'K', + '4', + '7', + 'J', + 'J', + '9', + '9', + 'A', + 'Q', + 'Q', + 'K', + 'A', + '6', + '5', + 'K' + } + local expected = { status = 'finished', tricks = 1, cards = 361 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('two tricks', function() + local playerA = { 'J' } + local playerB = { '3', 'J' } + local expected = { status = 'finished', tricks = 2, cards = 5 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('more tricks', function() + local playerA = { 'J', '2', '4' } + local playerB = { '3', 'J', 'A' } + local expected = { status = 'finished', tricks = 4, cards = 12 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('simple loop with decks of 4 cards', function() + local playerA = { '2', '3', 'J', '6' } + local playerB = { 'K', '5', 'J', '7' } + local expected = { status = 'loop', tricks = 4, cards = 16 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('easy card combination', function() + local playerA = { + '4', + '8', + '7', + '5', + '4', + '10', + '3', + '9', + '7', + '3', + '10', + '10', + '6', + '8', + '2', + '8', + '5', + '4', + '5', + '9', + '6', + '5', + '2', + '8', + '10', + '9' + } + local playerB = { + '6', + '9', + '4', + '7', + '2', + '2', + '3', + '6', + '7', + '3', + 'A', + 'A', + 'A', + 'A', + 'K', + 'K', + 'K', + 'K', + 'Q', + 'Q', + 'Q', + 'Q', + 'J', + 'J', + 'J', + 'J' + } + local expected = { status = 'finished', tricks = 4, cards = 40 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('easy card combination, inverted decks', function() + local playerA = { + '3', + '3', + '5', + '7', + '3', + '2', + '10', + '7', + '6', + '7', + 'A', + 'A', + 'A', + 'A', + 'K', + 'K', + 'K', + 'K', + 'Q', + 'Q', + 'Q', + 'Q', + 'J', + 'J', + 'J', + 'J' + } + local playerB = { + '5', + '10', + '8', + '2', + '6', + '7', + '2', + '4', + '9', + '2', + '6', + '10', + '10', + '5', + '4', + '8', + '4', + '8', + '6', + '9', + '8', + '5', + '9', + '3', + '4', + '9' + } + local expected = { status = 'finished', tricks = 4, cards = 40 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('mirrored decks', function() + local playerA = { + '2', + 'A', + '3', + 'A', + '3', + 'K', + '4', + 'K', + '2', + 'Q', + '2', + 'Q', + '10', + 'J', + '5', + 'J', + '6', + '10', + '2', + '9', + '10', + '7', + '3', + '9', + '6', + '9' + } + local playerB = { + '6', + 'A', + '4', + 'A', + '7', + 'K', + '4', + 'K', + '7', + 'Q', + '7', + 'Q', + '5', + 'J', + '8', + 'J', + '4', + '5', + '8', + '9', + '10', + '6', + '8', + '3', + '8', + '5' + } + local expected = { status = 'finished', tricks = 4, cards = 59 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('opposite decks', function() + local playerA = { + '4', + 'A', + '9', + 'A', + '4', + 'K', + '9', + 'K', + '6', + 'Q', + '8', + 'Q', + '8', + 'J', + '10', + 'J', + '9', + '8', + '4', + '6', + '3', + '6', + '5', + '2', + '4', + '3' + } + local playerB = { + '10', + '7', + '3', + '2', + '9', + '2', + '7', + '8', + '7', + '5', + 'J', + '7', + 'J', + '10', + 'Q', + '10', + 'Q', + '3', + 'K', + '5', + 'K', + '6', + 'A', + '2', + 'A', + '5' + } + local expected = { status = 'finished', tricks = 21, cards = 151 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('random decks #1', function() + local playerA = { + 'K', + '10', + '9', + '8', + 'J', + '8', + '6', + '9', + '7', + 'A', + 'K', + '5', + '4', + '4', + 'J', + '5', + 'J', + '4', + '3', + '5', + '8', + '6', + '7', + '7', + '4', + '9' + } + local playerB = { + '6', + '3', + 'K', + 'A', + 'Q', + '10', + 'A', + '2', + 'Q', + '8', + '2', + '10', + '10', + '2', + 'Q', + '3', + 'K', + '9', + '7', + 'A', + '3', + 'Q', + '5', + 'J', + '2', + '6' + } + local expected = { status = 'finished', tricks = 76, cards = 542 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('random decks #2', function() + local playerA = { + '8', + 'A', + '4', + '8', + '5', + 'Q', + 'J', + '2', + '6', + '2', + '9', + '7', + 'K', + 'A', + '8', + '10', + 'K', + '8', + '10', + '9', + 'K', + '6', + '7', + '3', + 'K', + '9' + } + local playerB = { + '10', + '5', + '2', + '6', + 'Q', + 'J', + 'A', + '9', + '5', + '5', + '3', + '7', + '3', + 'J', + 'A', + '2', + 'Q', + '3', + 'J', + 'Q', + '4', + '10', + '4', + '7', + '4', + '6' + } + local expected = { status = 'finished', tricks = 42, cards = 327 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('kleber 1999', function() + local playerA = { + '4', + '8', + '9', + 'J', + 'Q', + '8', + '5', + '5', + 'K', + '2', + 'A', + '9', + '8', + '5', + '10', + 'A', + '4', + 'J', + '3', + 'K', + '6', + '9', + '2', + 'Q', + 'K', + '7' + } + local playerB = { + '10', + 'J', + '3', + '2', + '4', + '10', + '4', + '7', + '5', + '3', + '6', + '6', + '7', + 'A', + 'J', + 'Q', + 'A', + '7', + '2', + '10', + '3', + 'K', + '9', + '6', + '8', + 'Q' + } + local expected = { status = 'finished', tricks = 805, cards = 5790 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('collins 2006', function() + local playerA = { + 'A', + '8', + 'Q', + 'K', + '9', + '10', + '3', + '7', + '4', + '2', + 'Q', + '3', + '2', + '10', + '9', + 'K', + 'A', + '8', + '7', + '7', + '4', + '5', + 'J', + '9', + '2', + '10' + } + local playerB = { + '4', + 'J', + 'A', + 'K', + '8', + '5', + '6', + '6', + 'A', + '6', + '5', + 'Q', + '4', + '6', + '10', + '8', + 'J', + '2', + '5', + '7', + 'Q', + 'J', + '3', + '3', + 'K', + '9' + } + local expected = { status = 'finished', tricks = 960, cards = 6913 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('mann and wu 2007', function() + local playerA = { + 'K', + '2', + 'K', + 'K', + '3', + '3', + '6', + '10', + 'K', + '6', + 'A', + '2', + '5', + '5', + '7', + '9', + 'J', + 'A', + 'A', + '3', + '4', + 'Q', + '4', + '8', + 'J', + '6' + } + local playerB = { + '4', + '5', + '2', + 'Q', + '7', + '9', + '9', + 'Q', + '7', + 'J', + '9', + '8', + '10', + '3', + '10', + 'J', + '4', + '10', + '8', + '6', + '8', + '7', + 'A', + 'Q', + '5', + '2' + } + local expected = { status = 'finished', tricks = 1007, cards = 7157 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('nessler 2012', function() + local playerA = { + '10', + '3', + '6', + '7', + 'Q', + '2', + '9', + '8', + '2', + '8', + '4', + 'A', + '10', + '6', + 'K', + '2', + '10', + 'A', + '5', + 'A', + '2', + '4', + 'Q', + 'J', + 'K', + '4' + } + local playerB = { + '10', + 'Q', + '4', + '6', + 'J', + '9', + '3', + 'J', + '9', + '3', + '3', + 'Q', + 'K', + '5', + '9', + '5', + 'K', + '6', + '5', + '7', + '8', + 'J', + 'A', + '7', + '8', + '7' + } + local expected = { status = 'finished', tricks = 1015, cards = 7207 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('anderson 2013', function() + local playerA = { + '6', + '7', + 'A', + '3', + 'Q', + '3', + '5', + 'J', + '3', + '2', + 'J', + '7', + '4', + '5', + 'Q', + '10', + '5', + 'A', + 'J', + '2', + 'K', + '8', + '9', + '9', + 'K', + '3' + } + local playerB = { + '4', + 'J', + '6', + '9', + '8', + '5', + '10', + '7', + '9', + 'Q', + '2', + '7', + '10', + '8', + '4', + '10', + 'A', + '6', + '4', + 'A', + '6', + '8', + 'Q', + 'K', + 'K', + '2' + } + local expected = { status = 'finished', tricks = 1016, cards = 7225 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('rucklidge 2014', function() + local playerA = { + '8', + 'J', + '2', + '9', + '4', + '4', + '5', + '8', + 'Q', + '3', + '9', + '3', + '6', + '2', + '8', + 'A', + 'A', + 'A', + '9', + '4', + '7', + '2', + '5', + 'Q', + 'Q', + '3' + } + local playerB = { + 'K', + '7', + '10', + '6', + '3', + 'J', + 'A', + '7', + '6', + '5', + '5', + '8', + '10', + '9', + '10', + '4', + '2', + '7', + 'K', + 'Q', + '10', + 'K', + '6', + 'J', + 'J', + 'K' + } + local expected = { status = 'finished', tricks = 1122, cards = 7959 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('nessler 2021', function() + local playerA = { + '7', + '2', + '3', + '4', + 'K', + '9', + '6', + '10', + 'A', + '8', + '9', + 'Q', + '7', + 'A', + '4', + '8', + 'J', + 'J', + 'A', + '4', + '3', + '2', + '5', + '6', + '6', + 'J' + } + local playerB = { + '3', + '10', + '8', + '9', + '8', + 'K', + 'K', + '2', + '5', + '5', + '7', + '6', + '4', + '3', + '5', + '7', + 'A', + '9', + 'J', + 'K', + '2', + 'Q', + '10', + 'Q', + '10', + 'Q' + } + local expected = { status = 'finished', tricks = 1106, cards = 7972 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('nessler 2022', function() + local playerA = { + '2', + '10', + '10', + 'A', + 'J', + '3', + '8', + 'Q', + '2', + '5', + '5', + '5', + '9', + '2', + '4', + '3', + '10', + 'Q', + 'A', + 'K', + 'Q', + 'J', + 'J', + '9', + 'Q', + 'K' + } + local playerB = { + '10', + '7', + '6', + '3', + '6', + 'A', + '8', + '9', + '4', + '3', + 'K', + 'J', + '6', + 'K', + '4', + '9', + '7', + '8', + '5', + '7', + '8', + '2', + 'A', + '7', + '4', + '6' + } + local expected = { status = 'finished', tricks = 1164, cards = 8344 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) + + it('casella 2024, first infinite game found', function() + local playerA = { + '2', + '8', + '4', + 'K', + '5', + '2', + '3', + 'Q', + '6', + 'K', + 'Q', + 'A', + 'J', + '3', + '5', + '9', + '8', + '3', + 'A', + 'A', + 'J', + '4', + '4', + 'J', + '7', + '5' + } + local playerB = { + '7', + '7', + '8', + '6', + '10', + '10', + '6', + '10', + '7', + '2', + 'Q', + '6', + '3', + '2', + '4', + 'K', + 'Q', + '10', + 'J', + '5', + '9', + '8', + '9', + '9', + 'K', + 'A' + } + local expected = { status = 'loop', tricks = 66, cards = 474 } + local result = camicia.simulate_game(playerA, playerB) + assert.are.same(expected, result) + end) +end)