Camicia

Camicia

Közepes

Bevezetés

Egy esős délután a konyhaasztalnál ülsz, és kártyázol a nagymamáddal. Ez a játék az ő változata a Camicia nevű játékra.

Először úgy tűnik, mintha csak egy újabb barátságos játszma lenne: lapok csapódnak le az asztalra, nevetés az asztalnál, és néha Nonna diadalmas vigyora. De ahogy a játék egyre húzódik, valami furcsa dolog történik. Ugyanazok a lapok kerülnek elő újra meg újra. Egyik lapot rakod le a másik után, a vég mégsem akar eljönni.

Elkezdesz tűnődni. Vajon véget ér valaha ez a játék? Vagy akár örökké is játszhatnánk?

Később a kíváncsiságtól hajtva rákeresel az interneten, és meglepetésedre rájössz, hogy ami történt, az nem csak balszerencse volt. Lehet, hogy te és a nagymamád a leghosszabb lehetséges sorozatok egyikére bukkantatok! Hirtelen a játék rabjává válsz. Ami egy kötetlen játékként indult, az küldetéssé változott: vajon meddig tarthat igazán egy ilyen játék? Találsz-e még annál is hosszabb sorozatot, mint amilyet a konyhaasztalnál játszottatok? Esetleg olyat, amely elég hosszú ahhoz, hogy új világrekordot állítson fel?

És így, mindössze egy pakli kártyával és némi algoritmikus leleményességgel felfegyverkezve, úgy döntesz, hogy utánajársz...

Utasítások

Ebben a feladatban egy olyan játékot fogsz szimulálni, amely nagyon hasonlít a klasszikus Camicia kártyajátékhoz. A programod megkapja két játékos paklijának kezdeti állapotát, és szimulálnia kell a játékot, amíg véget nem ér (vagy fel kell ismernie, hogy soha nem fog véget érni).

Szabályok

  • A pakli két játékos között oszlik meg. A játékos kártyáit balról jobbra olvassuk, ahol a bal szélső kártya a pakli teteje.
  • Egy kör abból áll, hogy mindkét játékos legalább egy kártyát játszik.
  • A játékosok felváltva teszik a paklijuk legfelső kártyáját a központi kupacra.
  • Ha a kártya számkártya (2-10), a játék egyszerűen a másik játékosra száll.
  • Ha a kártya fizetőkártya, büntetést kell fizetni:
    • J → az ellenfélnek 1 kártyát kell fizetnie
    • Q → az ellenfélnek 2 kártyát kell fizetnie
    • K → az ellenfélnek 3 kártyát kell fizetnie
    • A → az ellenfélnek 4 kártyát kell fizetnie
  • Ha a büntetést fizető játékos újabb fizetőkártyát fed fel, az a játékos abbahagyja a büntetés fizetését. A másik játékosnak ekkor az új fizetőkártya alapján kell büntetést fizetnie.
  • Ha a büntetést megszakítás nélkül teljesen megfizetik, az a játékos, aki a legutolsó fizetőkártyát letette, összegyűjti a központi kupacot, és a paklija aljára teszi. Ez a játékos kezdi a következő kört.
  • Ha egy játékosnak elfogynak a kártyái, és nem tud kártyát játszani (akár büntetés fizetése közben, akár amikor ő következik), a másik játékos gyűjti be a központi kupacot.
  • Azt a pillanatot, amikor egy játékos begyűjti a kártyákat a központi kupacból, ütésnek nevezzük.
  • Ha egy játékosnál egy ütés után az összes kártya van, a játék véget ér.
  • A játék ciklusba kerül, amint a paklik megegyeznek azzal, amilyenek korábban voltak a játék során, a számkártyákat nem számítva!

Példák

Egy kis példa egy befejeződő játszmára.

Kör Játékos A Játékos B Kupac Fizetendő büntetés
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 Játékos B: 4
1 7 8 Q 10 5 6 K 9 J 2 3 A 4 Játékos B: 3
1 7 8 Q 10 6 K 9 J 2 3 A 4 5 Játékos B: 2
1 7 8 Q 10 K 9 J 2 3 A 4 5 6 Játékos B: 1
1 7 8 Q 10 9 J 2 3 A 4 5 6 K Játékos A: 3
1 8 Q 10 9 J 2 3 A 4 5 6 K 7 Játékos A: 2
1 Q 10 9 J 2 3 A 4 5 6 K 7 8 Játékos A: 1
1 10 9 J 2 3 A 4 5 6 K 7 8 Q Játékos B: 2
1 10 J 2 3 A 4 5 6 K 7 8 Q 9 Játékos B: 1
1 10 - 2 3 A 4 5 6 K 7 8 Q 9 J Játékos 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 - -

állapot: "finished", kártyák: 13, ütések: 1

Ez egy kis példa egy ciklusba kerülő játszmára.

Kör Játékos A Játékos B Kupac Fizetendő büntetés
1 J 2 3 4 J 5 - -
1 2 3 4 J 5 J Játékos 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 Játékos 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 Játékos B: 1
3 4 J 3 5 J 2 -
4 4 5 J 2 J 3 - -

A 4. kör kezdete megegyezik a 2. kör kezdetével. Emlékezz rá, hogy a számkártyák értéke nem számít.

állapot: "loop", kártyák: 8, ütések: 3

A feladatod

  • A bemenet felhasználásával szimuláld a játékot a fenti szabályok szerint.
  • Határozd meg a játékra vonatkozó következő információkat:
    • Állapot: "finished" vagy "loop"
    • Kártyák: a játék során játszott kártyák összesített száma
    • Ütések: ahányszor a központi kupacot begyűjtötték
Advanced

Azoknak, akik egy izgalmasabb kihívást szeretnének vállalni: még mindig nyitott a vadászat a leghosszabb, befejeződő játék további rekordjaira. 653 534 134 886 878 245 000 (körülbelül 654 trillió) lehetőség van, és még nem számoltuk ki mindet!

A from_quote előnyei és hátrányai

A Card.from_quote függvény egy stringet alakít Card-dá. Ez lehetővé teszi, hogy ilyen kódot írj:

card : Card
card = "Q"

Ez a kód implicit módon meghívja a Card.from_quote("Q") függvényt fordítási időben. Ha ezzel a módszerrel (vagy az alább leírt egyéb módokon) futtatsz kódot fordítási időben, az potenciálisan felgyorsíthatja a programot, mivel így nem kell futásidőben a parse-olással és az adatok érvényesítésével foglalkoznia. Ráadásul gondoskodik róla, hogy az érvénytelen adatok (például egy hibás dátumformátum) már fordítási időben kiderüljenek, ne pedig futásidőben, ami robusztusabbá teszi az alkalmazásodat. Végül pedig széppé és olvashatóvá teszi a kódot. Például így néz ki a feladat egyik tesztje:

result = simulate_game({
    player_a: ["2", "A"],
    player_b: ["3", "4", "5", "6", "K"],
})

Lehet azonban, hogy aki ezt a kódot olvassa, azt hiszi, hogy a simulate_game függvény két List(Str)-et tartalmazó rekordot vár, pedig valójában két List(Card)-ot tartalmazó rekordról van szó. Ez zavarhoz és hibákhoz vezethet. Ezért talán inkább a kicsit hosszabb, de egyértelműbb formát választod: a "Q".Card-ot, ami szintén meghívja a from_quote függvényt fordítási időben.

A saját programjaidban viszont talán inkább a from_str függvényt használnád:

Card := { ... }.{
    from_str : Str -> Try(Card, [InvalidCard])
    from_str = |card_str| ...
}

A from_str függvényt (vagy bármely más tiszta függvényt) fordítási időben is meghívhatod, így:

Ok(card) = Card.from_str("Q")

Megjegyzés: ha ezt a definíciót a legfelső szintre teszed, az fordítási időben fut le (mivel a függvény tiszta, és a bemenete fordítási időben ismert). Egy függvényen belül viszont a from_str függvényt futásidőben használhatod.

Tippek

Mutasd meg a tippeket

A match-csel, valamint a [] és a [card, .. as rest] mintával különböztetheted meg az üres paklit a következő kártyától és a maradék kártyáktól. A { ..state, pile: new_pile }-hez hasonló rekordfrissítésekkel a játék állapotváltásait is explicit módon tarthatod, változtatható állapot nélkül. A Set tuple-t vagy listát is tárolhat kulcsként, amikor a korábban látott játékállapotokat követed nyomon.

Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg
Roc Exercism

Készen állsz elkezdeni a(z) Camicia feladatot?

Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Roc nyelvet 135 feladat segítségével, valódi emberi mentorálással, mindez ingyen.