11package games .uno .backend ;
22
3- using Math ;
4-
5- import games .uno .backend .UnoRules .UnoGameState ;
63import games .uno .backend .UnoCard .UnoColor ;
4+ import games .uno .backend .UnoRules .UnoGameState ;
5+
6+ using Math ;
77
88/**
99 * AI player implementation for UNO with different difficulty levels
1010 */
1111class UnoCPU extends UnoPlayer {
1212 public var difficulty : UnoDifficulty ;
1313 public var thinkingTime : Float ; // Delay to simulate thinking
14-
14+
1515 public function new (id : String , name : String , difficulty : UnoDifficulty = NORMAL ) {
1616 super (id , name , false );
1717 this .difficulty = difficulty ;
1818 this .thinkingTime = getDifficultyThinkingTime (difficulty );
1919 }
20-
20+
2121 /**
2222 * Get thinking time based on difficulty
2323 */
@@ -29,33 +29,33 @@ class UnoCPU extends UnoPlayer {
2929 case EXPERT : 1.5 ;
3030 }
3131 }
32-
32+
3333 /**
3434 * CPU chooses a card to play
3535 */
3636 public function chooseCard (topCard : UnoCard , gameState : UnoGameState ): Int {
3737 var playableCards = getPlayableCards (topCard );
38-
38+
3939 if (playableCards .length == 0 ) {
4040 return - 1 ; // No playable cardsdr
4141 }
42-
42+
4343 return switch (difficulty ) {
4444 case EASY : chooseCardEasy (playableCards , topCard , gameState );
4545 case NORMAL : chooseCardNormal (playableCards , topCard , gameState );
4646 case HARD : chooseCardHard (playableCards , topCard , gameState );
4747 case EXPERT : chooseCardExpert (playableCards , topCard , gameState );
4848 }
4949 }
50-
50+
5151 /**
5252 * Easy AI - Random card selection
5353 */
5454 private function chooseCardEasy (playableCards : Array <UnoCard >, topCard : UnoCard , gameState : UnoGameState ): Int {
5555 var randomCard = playableCards [Math .floor (Math .random () * playableCards .length )];
5656 return hand .cards .indexOf (randomCard );
5757 }
58-
58+
5959 /**
6060 * Normal AI - Basic strategy
6161 */
@@ -66,25 +66,25 @@ class UnoCPU extends UnoPlayer {
6666 return hand .cards .indexOf (card );
6767 }
6868 }
69-
69+
7070 // Then number cards
7171 for (card in playableCards ) {
7272 if (card .type == NUMBER ) {
7373 return hand .cards .indexOf (card );
7474 }
7575 }
76-
76+
7777 // Finally wild cards
7878 return hand .cards .indexOf (playableCards [0 ]);
7979 }
80-
80+
8181 /**
8282 * Hard AI - Strategic play
8383 */
8484 private function chooseCardHard (playableCards : Array <UnoCard >, topCard : UnoCard , gameState : UnoGameState ): Int {
8585 var nextPlayer = gameState .getNextPlayer ();
8686 var isNextPlayerClose = nextPlayer .getHandSize () <= 3 ;
87-
87+
8888 // If next player is close to winning, try to disrupt them
8989 if (isNextPlayerClose ) {
9090 // Use action cards to disrupt
@@ -94,7 +94,7 @@ class UnoCPU extends UnoPlayer {
9494 }
9595 }
9696 }
97-
97+
9898 // Save wild cards for later unless necessary
9999 var nonWildCards = [];
100100 for (card in playableCards ) {
@@ -109,43 +109,43 @@ class UnoCPU extends UnoPlayer {
109109 return hand .cards .indexOf (card );
110110 }
111111 }
112-
112+
113113 // Then highest value number cards
114114 nonWildCards .sort (function (a , b ) return b .value - a .value );
115115 return hand .cards .indexOf (nonWildCards [0 ]);
116116 }
117-
117+
118118 return hand .cards .indexOf (playableCards [0 ]);
119119 }
120-
120+
121121 /**
122122 * Expert AI - Advanced strategy
123123 */
124124 private function chooseCardExpert (playableCards : Array <UnoCard >, topCard : UnoCard , gameState : UnoGameState ): Int {
125125 var cardScores = [];
126-
126+
127127 for (card in playableCards ) {
128128 var score = evaluateCardPlay (card , topCard , gameState );
129129 cardScores .push ({card : card , score : score });
130130 }
131-
131+
132132 // Sort by score (highest first)
133133 cardScores .sort (function (a , b ) return Math .round (b .score - a .score ));
134-
134+
135135 return hand .cards .indexOf (cardScores [0 ].card );
136136 }
137-
137+
138138 /**
139139 * Evaluate the strategic value of playing a card
140140 */
141141 private function evaluateCardPlay (card : UnoCard , topCard : UnoCard , gameState : UnoGameState ): Float {
142142 var score = 0.0 ;
143143 var nextPlayer = gameState .getNextPlayer ();
144144 var isNextPlayerClose = nextPlayer .getHandSize () <= 3 ;
145-
145+
146146 // Basic card value
147147 score + = card .getPointValue () * 0.1 ;
148-
148+
149149 // Action card bonuses
150150 switch (card .type ) {
151151 case SKIP :
@@ -165,37 +165,37 @@ class UnoCPU extends UnoPlayer {
165165 case CUSTOM (name , points , cpuImportance , action ):
166166 score + = cpuImportance ;
167167 }
168-
168+
169169 // Color strategy (including custom colors)
170170 var colorCount = hand .getCardsByColor (card .color ).length ;
171171 if (! card .isWildCard ()) {
172172 score + = colorCount * 2 ; // Prefer playing colors we have more of
173173 }
174-
174+
175175 // Custom color bonus - slightly prefer custom colors if we have many
176176 switch (card .color ) {
177177 case CUSTOM (color , name ):
178178 score + = colorCount > 2 ? 3 : 1 ; // Small bonus for custom colors
179179 case _ : // Standard colors get no bonus
180180 }
181-
181+
182182 // End game strategy
183183 if (hand .getSize () <= 2 ) {
184184 // Prioritize getting rid of high-value cards
185185 score + = card .getPointValue () * 0.5 ;
186186 }
187-
187+
188188 return score ;
189189 }
190-
190+
191191 /**
192192 * CPU chooses a color for wild cards (including custom colors)
193193 */
194194 public function chooseWildColor (? availableColors : Array <UnoColor >): UnoColor {
195195 // If no colors are specified, use standard colors plus any custom colors found in hand
196196 if (availableColors == null ) {
197197 availableColors = UnoCard .getStandardColors ();
198-
198+
199199 // Add any custom colors found in the hand
200200 for (card in hand .cards ) {
201201 switch (card .color ) {
@@ -214,7 +214,7 @@ class UnoCPU extends UnoPlayer {
214214 }
215215 }
216216 }
217-
217+
218218 var colorCounts = [];
219219 for (color in availableColors ) {
220220 if (color != WILD ) { // Don't count wild as a choosable color
@@ -224,45 +224,47 @@ class UnoCPU extends UnoPlayer {
224224 });
225225 }
226226 }
227-
227+
228228 // Sort by count (highest first)
229229 colorCounts .sort (function (a , b ) return b .count - a .count );
230-
230+
231+ colorCounts = colorCounts .filter (function (c ) return c .count > 0 );
232+
231233 // Add some randomness for lower difficulties
232234 var randomFactor = switch (difficulty ) {
233235 case EASY : Math .random () < 0.5 ;
234236 case NORMAL : Math .random () < 0.3 ;
235237 case HARD : Math .random () < 0.1 ;
236238 case EXPERT : false ;
237239 }
238-
240+
239241 if (randomFactor && colorCounts .length > 1 ) {
240242 return colorCounts [1 ].color ; // Choose second best sometimes
241243 }
242-
244+
243245 // Return the color with the most cards, or a random standard color if no cards
244246 return colorCounts .length > 0 ? colorCounts [0 ].color : UnoColor .RED ;
245247 }
246-
248+
247249 /**
248250 * CPU decides whether to challenge a Wild Draw Four
249251 */
250252 public function shouldChallenge (previousPlayer : UnoPlayer , topCard : UnoCard ): Bool {
251253 if (difficulty == EASY ) {
252254 return Math .random () < 0.1 ; // 10% chance
253255 }
254-
256+
255257 if (difficulty == NORMAL ) {
256258 return Math .random () < 0.3 ; // 30% chance
257259 }
258-
260+
259261 // Hard and Expert: Strategic decision based on game state
260262 var handSize = previousPlayer .getHandSize ();
261263 var challengeProbability = handSize <= 2 ? 0.7 : 0.4 ; // More likely to challenge if they're close to winning
262-
264+
263265 return Math .random () < challengeProbability ;
264266 }
265-
267+
266268 /**
267269 * CPU automatically calls UNO when appropriate
268270 */
@@ -274,7 +276,7 @@ class UnoCPU extends UnoPlayer {
274276 case HARD : 0.95 ;
275277 case EXPERT : 1.0 ; // Never forgets
276278 }
277-
279+
278280 if (Math .random () < callProbability ) {
279281 callUno ();
280282 }
0 commit comments