Em uma tarde chuvosa, você está à mesa da cozinha jogando cartas com a sua avó. O jogo é a versão dela de Camicia.
No começo, parece só mais uma partida amistosa: cartas batendo na mesa, risadas ao redor e, de vez em quando, um sorriso vitorioso da Nonna. Mas, conforme o jogo se arrasta, algo estranho acontece. As mesmas cartas continuam voltando. Você joga carta após carta, mas o fim parece nunca chegar.
Você começa a se perguntar. Será que esse jogo algum dia vai acabar? Ou será que poderíamos continuar jogando para sempre?
Mais tarde, a curiosidade fala mais alto e você pesquisa na internet. Para a sua surpresa, descobre que o que aconteceu não foi só azar. Você e a sua avó podem ter esbarrado em uma das sequências mais longas possíveis! De repente, você não consegue mais parar. O que começou como um jogo casual virou uma missão: quanto tempo um jogo assim pode realmente durar? Você consegue encontrar uma sequência ainda mais longa do que a que jogou à mesa da cozinha? Talvez até longa o suficiente para bater um novo recorde mundial?
E assim, com nada além de um baralho e um pouco de engenhosidade algorítmica, você decide investigar...
Neste exercício, você vai simular um jogo muito parecido com o clássico jogo de cartas Camicia. Seu programa vai receber a configuração inicial dos baralhos de dois jogadores e deve simular o jogo até que ele termine (ou detectar que ele nunca vai terminar).
Um pequeno exemplo de uma partida que termina.
| Rodada | Jogador A | Jogador B | Pilha | Penalidade Devida |
|---|---|---|---|---|
| 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 | Jogador B: 4 |
| 1 | 7 8 Q 10 | 5 6 K 9 J | 2 3 A 4 | Jogador B: 3 |
| 1 | 7 8 Q 10 | 6 K 9 J | 2 3 A 4 5 | Jogador B: 2 |
| 1 | 7 8 Q 10 | K 9 J | 2 3 A 4 5 6 | Jogador B: 1 |
| 1 | 7 8 Q 10 | 9 J | 2 3 A 4 5 6 K | Jogador A: 3 |
| 1 | 8 Q 10 | 9 J | 2 3 A 4 5 6 K 7 | Jogador A: 2 |
| 1 | Q 10 | 9 J | 2 3 A 4 5 6 K 7 8 | Jogador A: 1 |
| 1 | 10 | 9 J | 2 3 A 4 5 6 K 7 8 Q | Jogador B: 2 |
| 1 | 10 | J | 2 3 A 4 5 6 K 7 8 Q 9 | Jogador B: 1 |
| 1 | 10 | - | 2 3 A 4 5 6 K 7 8 Q 9 J | Jogador 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
Este é um pequeno exemplo de uma partida que entra em loop.
| Rodada | Jogador A | Jogador B | Pilha | Penalidade Devida |
|---|---|---|---|---|
| 1 | J 2 3 | 4 J 5 | - | - |
| 1 | 2 3 | 4 J 5 | J | Jogador 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 | Jogador 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 | Jogador B: 1 |
| 3 | 4 | J 3 | 5 J 2 | - |
| 4 | 4 5 J 2 | J 3 | - | - |
O início da rodada 4 coincide com o início da rodada 2. Lembre-se, o valor das cartas numéricas não importa.
status: "loop", cards: 8, tricks: 3
"finished" ou "loop"
Para quem quer encarar um desafio mais empolgante, a busca por outros recordes para o jogo mais longo com final ainda está aberta. Existem 653.534.134.886.878.245.000 (aproximadamente 654 quintilhões) de possibilidades, e ainda não calculamos todas!
from_quote
A função Card.from_quote converte uma string em um Card.
Isso permite escrever um código como este:
card : Card
card = "Q"
Esse código chama Card.from_quote("Q") implicitamente em tempo de compilação.
Executar código em tempo de compilação com essa abordagem (ou com outros métodos descritos abaixo) pode deixar o programa mais rápido, já que ele não precisa lidar com parsing e validação de dados em tempo de execução.
Além disso, garante que dados inválidos (como um formato de data incorreto) sejam rejeitados em tempo de compilação, e não em tempo de execução, o que deixa seu app mais robusto.
Por fim, deixa o código agradável e legível.
Por exemplo, veja um dos testes deste exercício:
result = simulate_game({
player_a: ["2", "A"],
player_b: ["3", "4", "5", "6", "K"],
})
Porém, quem lê esse código pode supor que a função simulate_game recebe um registro contendo dois List(Str), quando na verdade é um registro contendo dois List(Card).
Isso pode gerar confusão e erros.
Por esse motivo, você pode preferir o formato um pouco mais longo, porém mais explícito: "Q".Card, que também chama from_quote em tempo de compilação.
Nos seus próprios programas, você pode preferir usar from_str:
Card := { ... }.{
from_str : Str -> Try(Card, [InvalidCard])
from_str = |card_str| ...
}
Você também pode chamar from_str (ou qualquer outra função pura) em tempo de compilação, assim:
Ok(card) = Card.from_str("Q")
Observação: colocar essa definição no nível superior faz com que ela seja executada em tempo de compilação (já que a função é pura e sua entrada é conhecida em tempo de compilação).
Dentro de uma função, você pode usar from_str em tempo de execução.
Use match com [] e [card, .. as rest] para distinguir um baralho vazio da próxima carta e das cartas restantes.
Atualizações de registro como { ..state, pile: new_pile } podem manter as transições do jogo explícitas sem estado mutável.
Um Set pode armazenar tuplas ou listas como chaves ao rastrear estados do jogo já vistos.
Crie sua conta no Exercism para aprender e dominar Roc com 135 exercícios e mentoria humana de verdade, tudo de graça.