diff --git a/config.json b/config.json index 05193524a..49da76d02 100644 --- a/config.json +++ b/config.json @@ -2300,6 +2300,22 @@ ], "difficulty": 5 }, + { + "slug": "camicia", + "name": "Camicia", + "uuid": "949a708f-4379-4c96-9fec-71885e8164d9", + "practices": [ + "discriminated-unions", + "records", + "recursion" + ], + "prerequisites": [ + "discriminated-unions", + "records", + "recursion" + ], + "difficulty": 5 + }, { "slug": "satellite", "name": "Satellite", diff --git a/exercises/Exercises.slnx b/exercises/Exercises.slnx index c25398833..11b6d9c4a 100644 --- a/exercises/Exercises.slnx +++ b/exercises/Exercises.slnx @@ -36,6 +36,7 @@ + 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/.meta/Example.fs b/exercises/practice/camicia/.meta/Example.fs new file mode 100644 index 000000000..c12cc657f --- /dev/null +++ b/exercises/practice/camicia/.meta/Example.fs @@ -0,0 +1,99 @@ +module Camicia + +open System +open System.Collections.Generic + +type GameStatus = + | Finished + | Loop + +type GameResult = { Status: GameStatus; Tricks: int; Cards: int } + +type Player = + | A + | B + +let private cardValue = + function + | "J" -> 1 + | "Q" -> 2 + | "K" -> 3 + | "A" -> 4 + | _ -> 0 + +let private otherPlayer = + function + | A -> B + | B -> A + +let private handFor player handA handB = + match player with + | A -> handA + | B -> handB + +let private positionKey (handA: Queue) (handB: Queue) activePlayer = + let handAKey = String.Join(",", handA) + let handBKey = String.Join(",", handB) + $"{handAKey}|{handBKey}|{activePlayer}" + +let private hasBeenSeen handA handB activePlayer (seen: HashSet) = + positionKey handA handB activePlayer |> seen.Add |> not + +let private finishedGame tricks cards pile = + let extraTrick = if Seq.isEmpty pile then 0 else 1 + { Status = Finished; Tricks = tricks + extraTrick; Cards = cards } + +let private loopedGame tricks cards = { Status = Loop; Tricks = tricks; Cards = cards } + +let private awardPile pile (winner: Queue) = + for card in pile do + winner.Enqueue card + + (pile: List).Clear() + +let simulateGame (playerA: string array) (playerB: string array) = + let handA = Queue(playerA |> Seq.map cardValue) + let handB = Queue(playerB |> Seq.map cardValue) + let pile = List() + let seen = HashSet() + let mutable activePlayer = A + let mutable tricks = 0 + let mutable cards = 0 + let mutable debt = 0 + + let rec play () = + if Seq.isEmpty pile && hasBeenSeen handA handB activePlayer seen then + loopedGame tricks cards + else + let activeHand = handFor activePlayer handA handB + let otherHand = handFor (otherPlayer activePlayer) handA handB + + if activeHand.Count = 0 then + finishedGame tricks cards pile + else + let card = activeHand.Dequeue() + pile.Add card + cards <- cards + 1 + + if card > 0 then + debt <- card + activePlayer <- otherPlayer activePlayer + play () + elif debt > 1 then + debt <- debt - 1 + play () + elif debt = 1 then + awardPile pile otherHand + tricks <- tricks + 1 + debt <- 0 + + if handA.Count = 0 || handB.Count = 0 then + finishedGame tricks cards pile + else + activePlayer <- otherPlayer activePlayer + play () + else + activePlayer <- otherPlayer activePlayer + play () + + play () diff --git a/exercises/practice/camicia/.meta/config.json b/exercises/practice/camicia/.meta/config.json new file mode 100644 index 000000000..efe05d9a9 --- /dev/null +++ b/exercises/practice/camicia/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "Camicia.fs" + ], + "test": [ + "CamiciaTests.fs" + ], + "example": [ + ".meta/Example.fs" + ] + }, + "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/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/Camicia.fs b/exercises/practice/camicia/Camicia.fs new file mode 100644 index 000000000..9be39c49e --- /dev/null +++ b/exercises/practice/camicia/Camicia.fs @@ -0,0 +1,9 @@ +module Camicia + +type GameStatus = + | Finished + | Loop + +type GameResult = { Status: GameStatus; Tricks: int; Cards: int } + +let simulateGame (playerA: string array) (playerB: string array) = failwith "You need to implement this function." diff --git a/exercises/practice/camicia/Camicia.fsproj b/exercises/practice/camicia/Camicia.fsproj new file mode 100644 index 000000000..2538048e3 --- /dev/null +++ b/exercises/practice/camicia/Camicia.fsproj @@ -0,0 +1,22 @@ + + + + net10.0 + Exercism + + false + + + + + + + + + + + + + + + diff --git a/exercises/practice/camicia/CamiciaTests.fs b/exercises/practice/camicia/CamiciaTests.fs new file mode 100644 index 000000000..a4cb77002 --- /dev/null +++ b/exercises/practice/camicia/CamiciaTests.fs @@ -0,0 +1,337 @@ +module CamiciaTests + +open FsUnit.Xunit +open Xunit + +open Camicia + +[] +let ``Two cards, one trick`` () = + let playerA = [|"2"|] + let playerB = [|"3"|] + let expected = { Status = Finished; Tricks = 1; Cards = 2 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Three cards, one trick`` () = + let playerA = [|"2"; "4"|] + let playerB = [|"3"|] + let expected = { Status = Finished; Tricks = 1; Cards = 3 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Four cards, one trick`` () = + let playerA = [|"2"; "4"|] + let playerB = [|"3"; "5"; "6"|] + let expected = { Status = Finished; Tricks = 1; Cards = 4 } + simulateGame playerA playerB |> should equal expected + +[] +let ``The ace reigns supreme`` () = + let playerA = [|"2"; "A"|] + let playerB = [|"3"; "4"; "5"; "6"; "7"|] + let expected = { Status = Finished; Tricks = 1; Cards = 7 } + simulateGame playerA playerB |> should equal expected + +[] +let ``The king beats ace`` () = + let playerA = [|"2"; "A"|] + let playerB = [|"3"; "4"; "5"; "6"; "K"|] + let expected = { Status = Finished; Tricks = 1; Cards = 7 } + simulateGame playerA playerB |> should equal expected + +[] +let ``The queen seduces the king`` () = + let playerA = [|"2"; "A"; "7"; "8"; "Q"|] + let playerB = [|"3"; "4"; "5"; "6"; "K"|] + let expected = { Status = Finished; Tricks = 1; Cards = 10 } + simulateGame playerA playerB |> should equal expected + +[] +let ``The jack betrays the queen`` () = + let playerA = [|"2"; "A"; "7"; "8"; "Q"|] + let playerB = [|"3"; "4"; "5"; "6"; "K"; "9"; "J"|] + let expected = { Status = Finished; Tricks = 1; Cards = 12 } + simulateGame playerA playerB |> should equal expected + +[] +let ``The 10 just wants to put on a show`` () = + let playerA = [|"2"; "A"; "7"; "8"; "Q"; "10"|] + let playerB = [|"3"; "4"; "5"; "6"; "K"; "9"; "J"|] + let expected = { Status = Finished; Tricks = 1; Cards = 13 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Simple loop with decks of 3 cards`` () = + let playerA = [|"J"; "2"; "3"|] + let playerB = [|"4"; "J"; "5"|] + let expected = { Status = Loop; Tricks = 3; Cards = 8 } + simulateGame playerA playerB |> should equal expected + +[] +let ``The story is starting to get a bit complicated`` () = + let 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" |] + let 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" |] + let expected = { Status = Finished; Tricks = 1; Cards = 361 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Two tricks`` () = + let playerA = [|"J"|] + let playerB = [|"3"; "J"|] + let expected = { Status = Finished; Tricks = 2; Cards = 5 } + simulateGame playerA playerB |> should equal expected + +[] +let ``More tricks`` () = + let playerA = [|"J"; "2"; "4"|] + let playerB = [|"3"; "J"; "A"|] + let expected = { Status = Finished; Tricks = 4; Cards = 12 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Simple loop with decks of 4 cards`` () = + let playerA = [|"2"; "3"; "J"; "6"|] + let playerB = [|"K"; "5"; "J"; "7"|] + let expected = { Status = Loop; Tricks = 4; Cards = 16 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Easy card combination`` () = + let 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" |] + let 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" |] + let expected = { Status = Finished; Tricks = 4; Cards = 40 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Easy card combination, inverted decks`` () = + let 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" |] + let 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" |] + let expected = { Status = Finished; Tricks = 4; Cards = 40 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Mirrored decks`` () = + let 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" |] + let 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" |] + let expected = { Status = Finished; Tricks = 4; Cards = 59 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Opposite decks`` () = + let 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" |] + let 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" |] + let expected = { Status = Finished; Tricks = 21; Cards = 151 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Random decks #1`` () = + let 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" |] + let 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" |] + let expected = { Status = Finished; Tricks = 76; Cards = 542 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Random decks #2`` () = + let 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" |] + let 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" |] + let expected = { Status = Finished; Tricks = 42; Cards = 327 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Kleber 1999`` () = + let 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" |] + let 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" |] + let expected = { Status = Finished; Tricks = 805; Cards = 5790 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Collins 2006`` () = + let 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" |] + let 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" |] + let expected = { Status = Finished; Tricks = 960; Cards = 6913 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Mann and Wu 2007`` () = + let 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" |] + let 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" |] + let expected = { Status = Finished; Tricks = 1007; Cards = 7157 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Nessler 2012`` () = + let 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" |] + let 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" |] + let expected = { Status = Finished; Tricks = 1015; Cards = 7207 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Anderson 2013`` () = + let 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" |] + let 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" |] + let expected = { Status = Finished; Tricks = 1016; Cards = 7225 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Rucklidge 2014`` () = + let 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" |] + let 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" |] + let expected = { Status = Finished; Tricks = 1122; Cards = 7959 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Nessler 2021`` () = + let 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" |] + let 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" |] + let expected = { Status = Finished; Tricks = 1106; Cards = 7972 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Nessler 2022`` () = + let 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" |] + let 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" |] + let expected = { Status = Finished; Tricks = 1164; Cards = 8344 } + simulateGame playerA playerB |> should equal expected + +[] +let ``Casella 2024, first infinite game found`` () = + let 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" |] + let 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" |] + let expected = { Status = Loop; Tricks = 66; Cards = 474 } + simulateGame playerA playerB |> should equal expected + diff --git a/generators/Generators.fs b/generators/Generators.fs index 59fccf3f4..b53f0fbc7 100644 --- a/generators/Generators.fs +++ b/generators/Generators.fs @@ -240,6 +240,54 @@ type Change() = | _ -> None | _ -> None +type Camicia() = + inherit ExerciseGenerator() + + let cardsPerLine = 8 + let continuationIndent = String.replicate 18 " " + + let renderHandRows rows = + let lastRow = (List.length rows) - 1 + + rows + |> List.mapi (fun index row -> + let prefix = + if index = 0 then "[| " else continuationIndent + + let suffix = + if index = lastRow then " |]" else ";" + + $"%s{prefix}%s{row}%s{suffix}") + |> String.concat "\n" + + let renderHand value = + let cards = + value + |> Seq.map Obj.render + |> Seq.toList + + if cards.Length <= cardsPerLine then + Array.renderStrings cards + else + cards + |> List.chunkBySize cardsPerLine + |> List.map (String.concat "; ") + |> renderHandRows + + override _.RenderExpected(_, _, value) = + let status = + value.["status"].ToObject() + |> String.upperCaseFirst + + let tricks = value.["tricks"].ToObject() + let cards = value.["cards"].ToObject() + + $"{{ Status = %s{status}; Tricks = %i{tricks}; Cards = %i{cards} }}" + + override _.RenderInput(_, _, value) = renderHand value + + override this.PropertiesWithIdentifier testCase = this.Properties testCase + type CircularBuffer() = inherit ExerciseGenerator()