1- import java .util .*;
1+ import java .util .ArrayDeque ;
2+ import java .util .Deque ;
3+ import java .util .HashSet ;
4+ import java .util .List ;
5+ import java .util .Set ;
26
3- class Camicia {
7+ public class Camicia {
48
5- private MatchResult matchResult ;
9+ private Status status ;
10+ private int cards ;
11+ private int tricks ;
612
7- void simulateGame (List <String > playerAList , List <String > playerBList ) {
8- Turn initialTurn = new Turn (playerAList , playerBList );
9- boolean win = initialTurn .playTurn ();
10- System .out .println (initialTurn );
11- Turn turn = initialTurn ;
13+ private enum Player {
14+ PLAYER_A , PLAYER_B
15+ }
16+
17+ private enum Status {
18+ FINISHED , LOOP
19+ }
20+
21+ private static int penaltyOf (String card ) {
22+ return switch (card ) {
23+ case "J" -> 1 ;
24+ case "Q" -> 2 ;
25+ case "K" -> 3 ;
26+ case "A" -> 4 ;
27+ default -> 0 ;
28+ };
29+ }
30+
31+ private static boolean isPaymentCard (String card ) {
32+ return penaltyOf (card ) > 0 ;
33+ }
34+
35+ /**
36+ * Return a snapshot of the current state
37+ */
38+ private static String stateKey (Deque <String > deckA , Deque <String > deckB ) {
39+ StringBuilder sb = new StringBuilder ();
40+
41+ for (String card : deckA ) {
42+ sb .append (isPaymentCard (card ) ? card : "N" );
43+ }
1244
13- while (!win ) {
14- Turn newTurn = new Turn (turn );
15- win = newTurn .playTurn ();
16- System .out .println (newTurn );
45+ sb .append ('|' );
1746
18- turn = newTurn ;
47+ for (String card : deckB ) {
48+ sb .append (isPaymentCard (card ) ? card : "N" );
1949 }
2050
21- matchResult = MatchResult .builder ()
22- .withStatus (MatchResult .Status .FINISHED )
23- .withCards (turn .getPlayedCardCounter ())
24- .withTricks (turn .getTrick ())
25- .build ();
51+ return sb .toString ();
52+ }
53+
54+ private static Player otherPlayer (Player player ) {
55+ return player == Player .PLAYER_A ? Player .PLAYER_B : Player .PLAYER_A ;
56+ }
57+
58+ private static Deque <String > deckOf (Player player , Deque <String > deckA , Deque <String > deckB ) {
59+ return player == Player .PLAYER_A ? deckA : deckB ;
60+ }
61+
62+ public void simulateGame (List <String > playerA , List <String > playerB ) {
63+ Deque <String > deckA = new ArrayDeque <>(playerA );
64+ Deque <String > deckB = new ArrayDeque <>(playerB );
65+
66+ int cardsPlayed = 0 ;
67+ int tricksCount = 0 ;
68+ Player current = Player .PLAYER_A ;
69+
70+ Set <String > seenStates = new HashSet <>();
71+
72+ while (true ) {
73+ String key = stateKey (deckA , deckB );
74+ // Key already exists, which means this is a loop
75+ if (!seenStates .add (key )) {
76+ finishGame (Status .LOOP , cardsPlayed , tricksCount );
77+ return ;
78+ }
79+
80+ // Otherwise, play next round
81+ RoundResult result = playRound (deckA , deckB , current );
82+
83+ cardsPlayed += result .pileSize ();
84+ tricksCount ++;
85+
86+ // Check if someone wins and finish the game
87+ if (hasWinner (deckA , deckB )) {
88+ finishGame (Status .FINISHED , cardsPlayed , tricksCount );
89+ return ;
90+ }
91+
92+ // Otherwise, play next round
93+ current = result .nextStarter ();
94+ }
95+ }
96+
97+ private RoundResult playRound (Deque <String > deckA , Deque <String > deckB , Player startingPlayer ) {
98+ Deque <String > pile = new ArrayDeque <>(); // cards played in this round
99+ Player currentPlayer = startingPlayer ;
100+ int pendingPenalty = 0 ;
101+
102+ while (true ) {
103+ Deque <String > currentPlayerDeck = deckOf (currentPlayer , deckA , deckB );
104+ Player opponent = otherPlayer (currentPlayer );
105+ Deque <String > opponentDeck = deckOf (opponent , deckA , deckB );
106+
107+ // Current player deck is empty, opponent collects all pile, end this round
108+ if (currentPlayerDeck .isEmpty ()) {
109+ opponentDeck .addAll (pile );
110+ return new RoundResult (opponent , pile .size ());
111+ }
112+
113+ // Otherwise, current player plays 1 card, add to pile
114+ String card = currentPlayerDeck .poll ();
115+ pile .addLast (card );
116+
117+ // Current player must pay off pending penalty
118+ if (pendingPenalty > 0 ) {
119+ // And player reveals a payment card
120+ if (isPaymentCard (card )) {
121+ // reset penalty based on new card, switch turn
122+ pendingPenalty = penaltyOf (card );
123+ currentPlayer = opponent ;
124+ } else {
125+ // Otherwise, deduct penalty
126+ pendingPenalty --;
127+ // No pending penalty
128+ if (pendingPenalty == 0 ) {
129+ // Opponent collects all pile and win the round
130+ deckOf (opponent , deckA , deckB ).addAll (pile );
131+ return new RoundResult (opponent , pile .size ());
132+ }
133+ }
134+ } else {
135+ // Normal gameplay, without pending penalty
136+ // If player reveals a payment card, update penalty
137+ if (isPaymentCard (card )) {
138+ pendingPenalty = penaltyOf (card );
139+ }
140+ currentPlayer = opponent ;
141+ }
142+ }
143+ }
144+
145+ private void finishGame (Status status , int cardsPlayed , int tricksCount ) {
146+ this .status = status ;
147+ this .cards = cardsPlayed ;
148+ this .tricks = tricksCount ;
149+ }
150+
151+ private boolean hasWinner (Deque <String > deckA , Deque <String > deckB ) {
152+ return deckA .isEmpty () || deckB .isEmpty ();
153+ }
154+
155+ public String getStatus () {
156+ return status != null ? status .name ().toLowerCase () : "" ;
26157 }
27158
28- String getStatus () {
29- return matchResult . getStatus (). name (). toLowerCase () ;
159+ public int getCards () {
160+ return cards ;
30161 }
31162
32- int getCards () {
33- return matchResult . getCards () ;
163+ public int getTricks () {
164+ return tricks ;
34165 }
35166
36- int getTricks () {
37- return matchResult .getTricks ();
167+ /**
168+ * Immutable round result
169+ */
170+ private record RoundResult (Player nextStarter , int pileSize ) {
38171 }
39172}
0 commit comments