diff --git a/config.json b/config.json index 0711c511e..ec3523afd 100644 --- a/config.json +++ b/config.json @@ -2371,6 +2371,28 @@ ], "difficulty": 6 }, + { + "slug": "camicia", + "name": "Camicia", + "uuid": "61976949-e1a6-4494-9ca0-d6d771e2e8e8", + "practices": [], + "prerequisites": [ + "strings", + "tuples", + "mapsets", + "maps", + "structs", + "lists", + "enum", + "case", + "if", + "cond", + "pattern-matching", + "recursion", + "tail-call-recursion" + ], + "difficulty": 6 + }, { "slug": "game-of-life", "name": "Game of Life", diff --git a/exercises/practice/camicia/.docs/instructions.md b/exercises/practice/camicia/.docs/instructions.md new file mode 100644 index 000000000..db62fcef2 --- /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 000000000..761d8a82c --- /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/.formatter.exs b/exercises/practice/camicia/.formatter.exs new file mode 100644 index 000000000..d2cda26ed --- /dev/null +++ b/exercises/practice/camicia/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/exercises/practice/camicia/.gitignore b/exercises/practice/camicia/.gitignore new file mode 100644 index 000000000..e1a2b1fcb --- /dev/null +++ b/exercises/practice/camicia/.gitignore @@ -0,0 +1,24 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +camicia-*.tar + diff --git a/exercises/practice/camicia/.meta/config.json b/exercises/practice/camicia/.meta/config.json new file mode 100644 index 000000000..f8561f3ce --- /dev/null +++ b/exercises/practice/camicia/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "FraSanga" + ], + "files": { + "solution": [ + "lib/camicia.ex" + ], + "test": [ + "test/camicia_test.exs" + ], + "example": [ + ".meta/example.ex" + ] + }, + "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.ex b/exercises/practice/camicia/.meta/example.ex new file mode 100644 index 000000000..3d53170d8 --- /dev/null +++ b/exercises/practice/camicia/.meta/example.ex @@ -0,0 +1,150 @@ +defmodule Camicia do + @doc """ + Simulate a card game between two players. + Each player has a deck of cards represented as a list of strings. + Returns a tuple with the result of the game: + - `{:finished, cards, tricks}` if the game finishes with a winner + - `{:loop, cards, tricks}` if the game enters a loop + `cards` is the number of cards played. + `tricks` is the number of central piles collected. + + ## Examples + + iex> Camicia.simulate(["2"], ["3"]) + {:finished, 2, 1} + + iex> Camicia.simulate(["J", "2", "3"], ["4", "J", "5"]) + {:loop, 8, 3} + """ + @penalties %{"J" => 1, "Q" => 2, "K" => 3, "A" => 4} + @payment_cards Map.keys(@penalties) + + @spec simulate(list(String.t()), list(String.t())) :: + {:finished | :loop, non_neg_integer(), non_neg_integer()} + def simulate(player_a, player_b) do + initial_state = %{seen: MapSet.new(), cards: 0, tricks: 0, current_player: :player_a} + do_simulate(format_deck(player_a), format_deck(player_b), initial_state) + end + + defp do_simulate([], _player_b, %{cards: cards, tricks: tricks}), + do: {:finished, cards, tricks} + + defp do_simulate(_player_a, [], %{cards: cards, tricks: tricks}), + do: {:finished, cards, tricks} + + defp do_simulate(player_a, player_b, %{ + seen: seen, + cards: cards, + tricks: tricks, + current_player: current_player + }) do + hands = {player_a, player_b} + + if MapSet.member?(seen, hands) do + {:loop, cards, tricks} + else + new_seen = MapSet.put(seen, hands) + state = %{cards: cards, tricks: tricks, current_player: current_player} + + {winner, loser, %{current_player: next_player} = new_state} = + case current_player do + :player_a -> round(player_a, player_b, state) + :player_b -> round(player_b, player_a, state) + end + + case next_player do + :player_a -> do_simulate(winner, loser, new_state |> Map.put(:seen, new_seen)) + :player_b -> do_simulate(loser, winner, new_state |> Map.put(:seen, new_seen)) + end + end + end + + defp round(player, opponent, state), + do: do_round(player, opponent, state, %{type: :number, central_pile: []}) + + defp do_round([], opponent, %{cards: cards, tricks: tricks, current_player: current_player}, %{ + type: _, + central_pile: central_pile + }) do + {opponent ++ Enum.reverse(central_pile), [], + %{cards: cards, tricks: tricks + 1, current_player: change_player(current_player)}} + end + + defp do_round( + [card | rest], + opponent, + %{cards: cards, tricks: tricks, current_player: current_player}, + %{type: :number, central_pile: central_pile} + ) do + new_state = %{cards: cards + 1, tricks: tricks, current_player: change_player(current_player)} + + case card do + "-" -> + do_round( + opponent, + rest, + new_state, + %{type: :number, central_pile: [card | central_pile]} + ) + + payment_card when payment_card in @payment_cards -> + penalty = Map.get(@penalties, payment_card) + + do_round( + opponent, + rest, + new_state, + %{type: {:payment, penalty}, central_pile: [card | central_pile]} + ) + end + end + + defp do_round( + player, + opponent, + %{cards: cards, tricks: tricks, current_player: current_player}, + %{type: {:payment, 0}, central_pile: central_pile} + ) do + {opponent ++ Enum.reverse(central_pile), player, + %{cards: cards, tricks: tricks + 1, current_player: change_player(current_player)}} + end + + defp do_round( + [card | rest], + opponent, + %{cards: cards, tricks: tricks, current_player: current_player}, + %{type: {:payment, penalty}, central_pile: central_pile} + ) do + case card do + "-" -> + do_round( + rest, + opponent, + %{cards: cards + 1, tricks: tricks, current_player: current_player}, + %{type: {:payment, penalty - 1}, central_pile: [card | central_pile]} + ) + + payment_card when payment_card in @payment_cards -> + penalty = Map.get(@penalties, payment_card) + + do_round( + opponent, + rest, + %{cards: cards + 1, tricks: tricks, current_player: change_player(current_player)}, + %{type: {:payment, penalty}, central_pile: [card | central_pile]} + ) + end + end + + defp format_deck(deck) do + Enum.map(deck, fn card -> + cond do + card in @payment_cards -> card + true -> "-" + end + end) + end + + defp change_player(:player_a), do: :player_b + defp change_player(:player_b), do: :player_a +end diff --git a/exercises/practice/camicia/.meta/tests.toml b/exercises/practice/camicia/.meta/tests.toml new file mode 100644 index 000000000..18d3fdd99 --- /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/lib/camicia.ex b/exercises/practice/camicia/lib/camicia.ex new file mode 100644 index 000000000..83e38bec4 --- /dev/null +++ b/exercises/practice/camicia/lib/camicia.ex @@ -0,0 +1,24 @@ +defmodule Camicia do + @doc """ + Simulate a card game between two players. + Each player has a deck of cards represented as a list of strings. + Returns a tuple with the result of the game: + - `{:finished, cards, tricks}` if the game finishes with a winner + - `{:loop, cards, tricks}` if the game enters a loop + `cards` is the number of cards played. + `tricks` is the number of central piles collected. + + ## Examples + + iex> Camicia.simulate(["2"], ["3"]) + {:finished, 2, 1} + + iex> Camicia.simulate(["J", "2", "3"], ["4", "J", "5"]) + {:loop, 8, 3} + """ + + @spec simulate(list(String.t()), list(String.t())) :: + {:finished | :loop, non_neg_integer(), non_neg_integer()} + def simulate(player_a, player_b) do + end +end diff --git a/exercises/practice/camicia/mix.exs b/exercises/practice/camicia/mix.exs new file mode 100644 index 000000000..2c5600ed0 --- /dev/null +++ b/exercises/practice/camicia/mix.exs @@ -0,0 +1,28 @@ +defmodule Camicia.MixProject do + use Mix.Project + + def project do + [ + app: :camicia, + version: "0.1.0", + # elixir: "~> 1.8", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + # {:dep_from_hexpm, "~> 0.3.0"}, + # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} + ] + end +end diff --git a/exercises/practice/camicia/test/camicia_test.exs b/exercises/practice/camicia/test/camicia_test.exs new file mode 100644 index 000000000..df7445ada --- /dev/null +++ b/exercises/practice/camicia/test/camicia_test.exs @@ -0,0 +1,1432 @@ +defmodule CamiciaTest do + use ExUnit.Case + + # @tag :pending + test "two cards, one trick" do + player_a = ["2"] + player_b = ["3"] + expected = {:finished, 2, 1} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "three cards, one trick" do + player_a = ["2", "4"] + player_b = ["3"] + expected = {:finished, 3, 1} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "four cards, one trick" do + player_a = ["2", "4"] + player_b = ["3", "5", "6"] + expected = {:finished, 4, 1} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "the ace reigns supreme" do + player_a = ["2", "A"] + player_b = ["3", "4", "5", "6", "7"] + expected = {:finished, 7, 1} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "the king beats ace" do + player_a = ["2", "A"] + player_b = ["3", "4", "5", "6", "K"] + expected = {:finished, 7, 1} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "the queen seduces the king" do + player_a = ["2", "A", "7", "8", "Q"] + player_b = ["3", "4", "5", "6", "K"] + expected = {:finished, 10, 1} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "the jack betrays the queen" do + player_a = ["2", "A", "7", "8", "Q"] + player_b = ["3", "4", "5", "6", "K", "9", "J"] + expected = {:finished, 12, 1} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "the 10 just wants to put on a show" do + player_a = ["2", "A", "7", "8", "Q", "10"] + player_b = ["3", "4", "5", "6", "K", "9", "J"] + expected = {:finished, 13, 1} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "simple loop with decks of 3 cards" do + player_a = ["J", "2", "3"] + player_b = ["4", "J", "5"] + expected = {:loop, 8, 3} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "the story is starting to get a bit complicated" do + player_a = [ + "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" + ] + + player_b = [ + "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" + ] + + expected = {:finished, 361, 1} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "two tricks" do + player_a = ["J"] + player_b = ["3", "J"] + expected = {:finished, 5, 2} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "more tricks" do + player_a = ["J", "2", "4"] + player_b = ["3", "J", "A"] + expected = {:finished, 12, 4} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "simple loop with decks of 4 cards" do + player_a = ["2", "3", "J", "6"] + player_b = ["K", "5", "J", "7"] + expected = {:loop, 16, 4} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "easy card combination" do + player_a = [ + "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" + ] + + player_b = [ + "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" + ] + + expected = {:finished, 40, 4} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "easy card combination, inverted decks" do + player_a = [ + "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" + ] + + player_b = [ + "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" + ] + + expected = {:finished, 40, 4} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "mirrored decks" do + player_a = [ + "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" + ] + + player_b = [ + "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" + ] + + expected = {:finished, 59, 4} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "opposite decks" do + player_a = [ + "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" + ] + + player_b = [ + "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" + ] + + expected = {:finished, 151, 21} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "random decks #1" do + player_a = [ + "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" + ] + + player_b = [ + "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" + ] + + expected = {:finished, 542, 76} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "random decks #2" do + player_a = [ + "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" + ] + + player_b = [ + "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" + ] + + expected = {:finished, 327, 42} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "Kleber 1999" do + player_a = [ + "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" + ] + + player_b = [ + "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" + ] + + expected = {:finished, 5790, 805} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "Collins 2006" do + player_a = [ + "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" + ] + + player_b = [ + "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" + ] + + expected = {:finished, 6913, 960} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "Mann and Wu 2007" do + player_a = [ + "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" + ] + + player_b = [ + "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" + ] + + expected = {:finished, 7157, 1007} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "Nessler 2012" do + player_a = [ + "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" + ] + + player_b = [ + "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" + ] + + expected = {:finished, 7207, 1015} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "Anderson 2013" do + player_a = [ + "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" + ] + + player_b = [ + "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" + ] + + expected = {:finished, 7225, 1016} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "Rucklidge 2014" do + player_a = [ + "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" + ] + + player_b = [ + "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" + ] + + expected = {:finished, 7959, 1122} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "Nessler 2021" do + player_a = [ + "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" + ] + + player_b = [ + "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" + ] + + expected = {:finished, 7972, 1106} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "Nessler 2022" do + player_a = [ + "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" + ] + + player_b = [ + "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" + ] + + expected = {:finished, 8344, 1164} + assert Camicia.simulate(player_a, player_b) == expected + end + + @tag :pending + test "Casella 2024, first infinite game found" do + player_a = [ + "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" + ] + + player_b = [ + "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" + ] + + expected = {:loop, 474, 66} + assert Camicia.simulate(player_a, player_b) == expected + end +end diff --git a/exercises/practice/camicia/test/test_helper.exs b/exercises/practice/camicia/test/test_helper.exs new file mode 100644 index 000000000..35fc5bff8 --- /dev/null +++ b/exercises/practice/camicia/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true)