GBAlatro
A Demake of Balatro for the GBA
Loading...
Searching...
No Matches
hand.c
Go to the documentation of this file.
1
7#include "hand.h"
8
9#include "audio_utils.h"
10#include "card.h"
11#include "game.h"
12#include "game/round.h"
13#include "game_variables.h"
14#include "graphic_utils.h"
15#include "soundbank.h"
16#include "util.h"
17
18#include <tonc.h>
19
20typedef struct
21{
22 u32 chips;
23 u32 mult;
24 char* display_name;
26
27static const HandValues HAND_BASE_VALUES[] = {
28 {.chips = 0, .mult = 0, .display_name = NULL }, // NONE
29 {.chips = 5, .mult = 1, .display_name = "Hi-Card"}, // HIGH_CARD
30 {.chips = 10, .mult = 2, .display_name = "Pair" }, // PAIR
31 {.chips = 20, .mult = 2, .display_name = "2 Pair" }, // TWO_PAIR
32 {.chips = 30, .mult = 3, .display_name = "3 OAK" }, // THREE_OF_A_KIND
33 {.chips = 30, .mult = 4, .display_name = "Strt" }, // STRAIGHT
34 {.chips = 35, .mult = 4, .display_name = "Flush" }, // FLUSH
35 {.chips = 40, .mult = 4, .display_name = "Full H" }, // FULL_HOUSE
36 {.chips = 60, .mult = 7, .display_name = "4 OAK" }, // FOUR_OF_A_KIND
37 {.chips = 100, .mult = 8, .display_name = "Strt F" }, // STRAIGHT_FLUSH
38 {.chips = 100, .mult = 8, .display_name = "Royal F"}, // ROYAL_FLUSH
39 {.chips = 120, .mult = 12, .display_name = "5 OAK" }, // FIVE_OF_A_KIND
40 {.chips = 140, .mult = 14, .display_name = "Flush H"}, // FLUSH_HOUSE
41 {.chips = 160, .mult = 16, .display_name = "Flush 5"} // FLUSH_FIVE
42};
43
44// clang-format off
45// Rects for TTE (in pixels) left top right bottom
46static const Rect HAND_TYPE_RECT = {8, 64, 64, 72};
47// clang-format on
48
49typedef struct Hand
50{
51 // Hand stack
52 CardObject* cards[MAX_HAND_SIZE];
53 s32 hand_top; // Position of the last card in hand array, -1 when no card in hand
54 s32 hand_selections; // Number of selected Cards.
55
56 // Hand Type
57 enum HandType hand_type;
58 ContainedHandTypes contained_hands;
59
60 enum HandState state;
61 bool sort_by_suit;
62} Hand;
63
64static Hand s_hand = {
65 .cards = {NULL},
66 .hand_top = -1,
67 .hand_selections = 0,
68 .hand_type = NONE,
69 .contained_hands = {{{0}}},
70 .state = HAND_DRAW,
71 .sort_by_suit = false
72};
73
74// Forward declarations
75static ContainedHandTypes compute_contained_hand_types(void);
76static enum HandType compute_hand_type(struct ContainedHandTypes contained_types);
77
78// Misc Hand Functions
79
80const char* get_hand_type_name(enum HandType hand_type)
81{
82 if (hand_type <= NONE || hand_type > FLUSH_FIVE)
83 return NULL;
84
85 return HAND_BASE_VALUES[hand_type].display_name;
86}
87
88// Hand Struct Manipulation
89
90enum HandState get_hand_state(void)
91{
92 return s_hand.state;
93}
94
95void set_hand_state(enum HandState new_hand_state)
96{
97 s_hand.state = new_hand_state;
98}
99
101{
102 return s_hand.cards;
103}
104
106{
107 return s_hand.hand_top;
108}
109
110void set_hand_top(int new_hand_top)
111{
112 s_hand.hand_top = new_hand_top;
113}
114
116{
117 return s_hand.hand_top + 1;
118}
119
121{
122 return s_hand.hand_selections;
123}
124
125void hand_set_nb_selected_cards(int new_selections)
126{
127 s_hand.hand_selections = new_selections;
128}
129
130enum HandType get_hand_type(void)
131{
132 return s_hand.hand_type;
133}
134
136{
137 return &s_hand.contained_hands;
138}
139
140static void print_hand_type(const char* hand_type_str)
141{
142 if (hand_type_str == NULL)
143 return; // NULL-checking paranoia
144
145 Rect hand_type_rect = HAND_TYPE_RECT;
146 update_text_rect_to_center_str(&hand_type_rect, hand_type_str, SCREEN_LEFT);
147 tte_printf(
148 "#{P:%d,%d; cx:0x%X000}%s",
149 hand_type_rect.left,
150 hand_type_rect.top,
151 TTE_WHITE_PB,
152 hand_type_str
153 );
154}
155
157{
158 tte_erase_rect_wrapper(HAND_TYPE_RECT);
159 s_hand.contained_hands = compute_contained_hand_types();
160 s_hand.hand_type = compute_hand_type(s_hand.contained_hands);
161
162 HandValues hand_values = HAND_BASE_VALUES[s_hand.hand_type];
163
164 g_game_vars.chips = hand_values.chips;
165 g_game_vars.mult = hand_values.mult;
166
167 print_hand_type(hand_values.display_name);
168 display_chips();
169 display_mult();
170}
171
172// idx_a and idx_b are assumed to be valid indexes within the hand array
173// no checks will be performed here for performance's sake
174void swap_cards_in_hand(int idx_a, int idx_b)
175{
176 CardObject* temp = s_hand.cards[idx_a];
177 s_hand.cards[idx_a] = s_hand.cards[idx_b];
178 s_hand.cards[idx_b] = temp;
179}
180
181static inline void sort_hand_by_suit(void)
182{
183 for (int idx_a = 0; idx_a < s_hand.hand_top; idx_a++)
184 {
185 for (int idx_b = idx_a + 1; idx_b <= s_hand.hand_top; idx_b++)
186 {
187 if (s_hand.cards[idx_a] == NULL ||
188 (s_hand.cards[idx_b] != NULL &&
189 (s_hand.cards[idx_a]->card->suit > s_hand.cards[idx_b]->card->suit ||
190 (s_hand.cards[idx_a]->card->suit == s_hand.cards[idx_b]->card->suit &&
191 s_hand.cards[idx_a]->card->rank > s_hand.cards[idx_b]->card->rank))))
192 {
193 swap_cards_in_hand(idx_a, idx_b);
194 }
195 }
196 }
197}
198
199static inline void sort_hand_by_rank(void)
200{
201 for (int idx_a = 0; idx_a < s_hand.hand_top; idx_a++)
202 {
203 for (int idx_b = idx_a + 1; idx_b <= s_hand.hand_top; idx_b++)
204 {
205 if (s_hand.cards[idx_a] == NULL ||
206 (s_hand.cards[idx_b] != NULL &&
207 s_hand.cards[idx_a]->card->rank > s_hand.cards[idx_b]->card->rank))
208 {
209 swap_cards_in_hand(idx_a, idx_b);
210 }
211 }
212 }
213}
214
215static inline bool shift_null_card_to_end(int null_card_idx)
216{
217 // Start by searching any non NULL cards after the NULL one
218 // don't start at null_card_idx+1 to avoid potential illegal array access
219 int non_null_card_idx = null_card_idx;
220 for (; non_null_card_idx <= s_hand.hand_top; non_null_card_idx++)
221 {
222 if (s_hand.cards[non_null_card_idx] != NULL)
223 {
224 break;
225 }
226 }
227
228 // return false if there are no non-NULL cards left/there are no more sprites to destroy
229 if (non_null_card_idx > s_hand.hand_top)
230 {
231 return false;
232 }
233
234 // If there is one, shift it and all the cards that follow forward
235 // This way we close the gap and ensure the next card is not NULL
236 for (int j = 0; j <= s_hand.hand_top - non_null_card_idx; j++)
237 {
238 s_hand.cards[null_card_idx + j] = s_hand.cards[non_null_card_idx + j];
239 }
240
241 return true;
242}
243
245{
246 // Update the sprites in the hand by destroying them and creating new ones in the correct order
247 // (This feels like a diabolical solution but like literally how else would you do this)
248 for (int i = 0; i <= s_hand.hand_top; i++)
249 {
250 // a NULL card will only happen if we rearrange the sprites without having sorted them
251 // before. Any NULL CardObject will be sent to the end by shifting all elements forward
252 if (s_hand.cards[i] == NULL)
253 {
254 if (!shift_null_card_to_end(i))
255 {
256 break;
257 }
258 }
259
260 // card_object_get_sprite() will not work here since we need the address
261 sprite_destroy(&(s_hand.cards[i]->sprite));
262 }
263
264 // Recreate the sprites for the remaining non NULL cards, in order
265 for (int i = 0; i <= s_hand.hand_top; i++)
266 {
267 if (s_hand.cards[i] != NULL)
268 {
269 // Set the sprite for the card object
270 card_object_set_sprite(s_hand.cards[i], i);
272 card_object_get_sprite(s_hand.cards[i]),
273 fx2int(s_hand.cards[i]->x),
274 fx2int(s_hand.cards[i]->y)
275 );
276 }
277 }
278}
279
280void sort_cards(void)
281{
282 if (s_hand.sort_by_suit)
283 {
284 sort_hand_by_suit();
285 }
286 else
287 {
288 sort_hand_by_rank();
289 }
290
292}
293
294void hand_change_sort(bool to_sort_by_suit)
295{
296 if (to_sort_by_suit != s_hand.sort_by_suit)
297 {
298 s_hand.sort_by_suit = to_sort_by_suit;
299 sort_cards();
300 }
301}
302
303void hand_select_card(int index)
304{
305 if (index < 0 || index >= hand_nb_held_cards() || s_hand.state != HAND_SELECT ||
306 s_hand.cards[index] == NULL)
307 return;
308
309 if (card_object_is_selected(s_hand.cards[index]))
310 {
311 card_object_set_selected(s_hand.cards[index], false);
312 s_hand.hand_selections--;
314 }
315 else if (s_hand.hand_selections < MAX_SELECTION_SIZE)
316 {
317 card_object_set_selected(s_hand.cards[index], true);
318 s_hand.hand_selections++;
320 }
322}
323
325{
326 bool any_cards_deselected = false;
327 for (int i = 0; i <= s_hand.hand_top; i++)
328 {
329 if (card_object_is_selected(s_hand.cards[i]))
330 {
331 card_object_set_selected(s_hand.cards[i], false);
332 s_hand.hand_selections--;
333 any_cards_deselected = true;
334 }
335 }
336
337 if (any_cards_deselected)
338 {
340 }
341}
342
343// Hand Analysis
344
352static void get_hand_distribution(u8 ranks_out[NUM_RANKS], u8 suits_out[NUM_SUITS])
353{
354 for (int i = 0; i < NUM_RANKS; i++)
355 ranks_out[i] = 0;
356 for (int i = 0; i < NUM_SUITS; i++)
357 suits_out[i] = 0;
358
359 int top = s_hand.hand_top;
360 for (int i = 0; i <= top; i++)
361 {
362 if (s_hand.cards[i] && card_object_is_selected(s_hand.cards[i]))
363 {
364 ranks_out[s_hand.cards[i]->card->rank]++;
365 suits_out[s_hand.cards[i]->card->suit]++;
366 }
367 }
368}
369
370// Returns the highest N of a kind. So a full-house would return 3.
371static u8 hand_contains_n_of_a_kind(u8* ranks)
372{
373 u8 highest_n = 0;
374 for (int i = 0; i < NUM_RANKS; i++)
375 {
376 if (ranks[i] > highest_n)
377 highest_n = ranks[i];
378 }
379 return highest_n;
380}
381
382static bool hand_contains_two_pair(u8* ranks)
383{
384 bool contains_other_pair = false;
385 for (int i = 0; i < NUM_RANKS; i++)
386 {
387 if (ranks[i] >= 2)
388 {
389 if (contains_other_pair)
390 return true;
391 contains_other_pair = true;
392 }
393 }
394 return false;
395}
396
397static bool hand_contains_full_house(u8* ranks)
398{
399 int count_three = 0;
400 int count_pair = 0;
401 for (int i = 0; i < NUM_RANKS; i++)
402 {
403 if (ranks[i] >= 3)
404 {
405 count_three++;
406 }
407 else if (ranks[i] >= 2)
408 {
409 count_pair++;
410 }
411 }
412 // Full house if there is:
413 // - at least one three-of-a-kind and at least one other pair,
414 // - OR at least two three-of-a-kinds (second "three" acts as pair).
415 // This accounts for hands with 6 or more cards even though
416 // they are currently not possible and probably never will be.
417 return (count_three >= 2 || (count_three && count_pair));
418}
419
420// This is mostly from Google Gemini
421static bool hand_contains_straight(u8* ranks)
422{
423 if (!is_shortcut_joker_active())
424 {
425 int straight_size = get_straight_and_flush_size();
426 // This is the regular case of detecting straights
427 int run = 0;
428 for (int i = 0; i < NUM_RANKS; ++i)
429 {
430 if (ranks[i])
431 {
432 if (++run >= straight_size)
433 return true;
434 }
435 else
436 {
437 run = 0;
438 }
439 }
440
441 // Check for ace low straight
442 if (straight_size >= 2 && ranks[ACE])
443 {
444 // With A as low, the highest rank you can use is FIVE.
445 // -1 for inclusive integer distance and another -1 for the Ace e.g. need=5 -> need 2..5
446 int last_needed = TWO + (straight_size - 2);
447 if (last_needed <= FIVE)
448 {
449 bool ok = true;
450 for (int r = TWO; r <= last_needed; ++r)
451 {
452 if (!ranks[r])
453 {
454 ok = false;
455 break;
456 }
457 }
458 if (ok)
459 return true;
460 }
461 }
462
463 return false;
464 }
465 else
466 {
467 // Shortcut Joker is active, we have to detect straights where any card may "skip" 1 rank
468 // We do this with a dynamic programming algorithm that calculates
469 // the longest possible straight that can end on each rank
470 // and stopping when we find one that is {straight-size} cards long
471 u8 longest_short_cut_at[NUM_RANKS] = {0};
472
473 // A low ace can start a sequence. 'ace_low_len' is 1 if an ace is present,
474 // acting as a potential predecessor for TWO and THREE.
475 int ace_low_len = ranks[ACE] ? 1 : 0;
476
477 // Iterate through all ranks from TWO up to ACE.
478 for (int i = 0; i < NUM_RANKS; i++)
479 {
480 // No cards in this rank, no straight can end here, continue
481 if (ranks[i] == 0)
482 {
483 longest_short_cut_at[i] = 0;
484 continue;
485 }
486
487 int prev_len1 = 0;
488 int prev_len2 = 0;
489
490 // This logic handles the special connections for ace-low straights.
491 if (i == TWO)
492 {
493 // A TWO can be preceded by a low ACE (no skip).
494 prev_len1 = ace_low_len;
495 }
496 else if (i == THREE)
497 {
498 // A THREE can be preceded by a TWO (no skip) or a low ACE (skip).
499 prev_len1 = longest_short_cut_at[TWO];
500 prev_len2 = ace_low_len;
501 }
502 else if (i == ACE)
503 {
504 // An ACE (as the highest card) can be preceded by a KING or a QUEEN.
505 prev_len1 = longest_short_cut_at[KING];
506 prev_len2 = longest_short_cut_at[QUEEN];
507 }
508 else // For all other cards (FOUR through KING).
509 {
510 // A card can be preceded by the rank directly below or two ranks below.
511 prev_len1 = longest_short_cut_at[i - 1];
512 prev_len2 = longest_short_cut_at[i - 2];
513 }
514
515 // The length of the straight ending at rank 'i' is 1 (for the card itself)
516 // plus the length of the longest valid preceding straight.
517 longest_short_cut_at[i] = 1 + max(prev_len1, prev_len2);
518
519 // If we've formed a sequence of {straight-size} or more cards, we have a straight.
520 if (longest_short_cut_at[i] >= get_straight_and_flush_size())
521 {
522 return true;
523 }
524 }
525 }
526
527 return false;
528}
529
530static bool hand_contains_flush(u8* suits)
531{
532 for (int i = 0; i < NUM_SUITS; i++)
533 {
534 if (suits[i] >= get_straight_and_flush_size())
535 {
536 return true;
537 }
538 }
539 return false;
540}
541
542// Returns the number of cards in the best flush found
543// or 0 if no flush of min_len is found, and marks them in out_selection.
544int find_flush_in_played_cards(CardObject** played, int top, int min_len, bool* out_selection)
545{
546 if (top < 0)
547 return 0;
548 for (int i = 0; i <= top; i++)
549 out_selection[i] = false;
550
551 int suit_counts[NUM_SUITS] = {0};
552 for (int i = 0; i <= top; i++)
553 {
554 if (played[i] && played[i]->card)
555 {
556 suit_counts[played[i]->card->suit]++;
557 }
558 }
559
560 int best_suit = -1;
561 int best_count = 0;
562 for (int i = 0; i < NUM_SUITS; i++)
563 {
564 if (suit_counts[i] > best_count)
565 {
566 best_count = suit_counts[i];
567 best_suit = i;
568 }
569 }
570
571 if (best_count >= min_len)
572 {
573 for (int i = 0; i <= top; i++)
574 {
575 if (played[i] && played[i]->card && played[i]->card->suit == best_suit)
576 {
577 out_selection[i] = true;
578 }
579 }
580 return best_count;
581 }
582 return 0;
583}
584
585// Returns the number of cards in the best straight or 0 if no straight of min_len is found, marks
586// them as true in out_selection[]. This is mostly from Google Gemini
587int find_straight_in_played_cards(CardObject** played, int top, int min_len, bool* out_selection)
588{
589 if (top < 0)
590 return 0;
591 for (int i = 0; i <= top; i++)
592 out_selection[i] = false;
593
594 // --- Setup for Backtracking DP ---
595 u8 longest_straight_at[NUM_RANKS] = {0};
596 int parent[NUM_RANKS];
597 for (int i = 0; i < NUM_RANKS; i++)
598 parent[i] = -1;
599
600 u8 ranks[NUM_RANKS] = {0};
601 for (int i = 0; i <= top; i++)
602 {
603 if (played[i] && played[i]->card)
604 {
605 ranks[played[i]->card->rank]++;
606 }
607 }
608
609 // --- Run DP to find longest straight ---
610 // This is nearly identical to hand_contains_straight() logic
611 // TODO: Consolidate functions to avoid code duplication?
612 // Might cost performance because this does a little more
613 int ace_low_len = ranks[ACE] ? 1 : 0;
614 bool is_shortcut_active = is_shortcut_joker_active();
615 for (int i = 0; i < NUM_RANKS; i++)
616 {
617 if (ranks[i] > 0)
618 {
619 int prev1 = 0, prev2 = 0;
620 int parent1 = -1, parent2 = -1;
621
622 if (is_shortcut_active)
623 {
624 if (i == TWO)
625 {
626 prev1 = ace_low_len;
627 parent1 = ACE;
628 }
629 else if (i == THREE)
630 {
631 prev1 = longest_straight_at[TWO];
632 parent1 = TWO;
633 prev2 = ace_low_len;
634 parent2 = ACE;
635 }
636 else if (i == ACE)
637 {
638 prev1 = longest_straight_at[KING];
639 parent1 = KING;
640 prev2 = longest_straight_at[QUEEN];
641 parent2 = QUEEN;
642 }
643 else
644 {
645 prev1 = longest_straight_at[i - 1];
646 parent1 = i - 1;
647 if (i > 1)
648 {
649 prev2 = longest_straight_at[i - 2];
650 parent2 = i - 2;
651 }
652 }
653 }
654 else
655 {
656 if (i == TWO)
657 {
658 prev1 = ace_low_len;
659 parent1 = ACE;
660 }
661 else if (i == ACE)
662 {
663 prev1 = longest_straight_at[KING];
664 parent1 = KING;
665 }
666 else
667 {
668 prev1 = longest_straight_at[i - 1];
669 parent1 = i - 1;
670 }
671 }
672
673 // Parallels longest_short_cut_at[i] = 1 + max(prev_len1, prev_len2);
674 // in hand_contains_straight()
675 if (prev1 >= prev2)
676 {
677 longest_straight_at[i] = 1 + prev1;
678 parent[i] = parent1;
679 }
680 else
681 {
682 longest_straight_at[i] = 1 + prev2;
683 parent[i] = parent2;
684 }
685 }
686 }
687
688 // --- Find best straight and backtrack ---
689 int best_len = 0;
690 int end_rank = -1;
691 for (int i = 0; i < NUM_RANKS; i++)
692 {
693 if (longest_straight_at[i] >= best_len)
694 {
695 best_len = longest_straight_at[i];
696 end_rank = i;
697 }
698 }
699
700 if (best_len >= min_len)
701 {
702 u8 needed_ranks[NUM_RANKS] = {0};
703 int current_rank = end_rank;
704 while (current_rank != -1 && best_len > 0)
705 {
706 needed_ranks[current_rank]++;
707 current_rank = parent[current_rank];
708 best_len--;
709 }
710
711 for (int i = 0; i <= top; i++)
712 {
713 if (played[i] && played[i]->card && needed_ranks[played[i]->card->rank] > 0)
714 {
715 out_selection[i] = true;
716 needed_ranks[played[i]->card->rank]--;
717 }
718 }
719
720 int final_card_count = 0;
721 for (int i = 0; i <= top; i++)
722 {
723 if (out_selection[i])
724 final_card_count++;
725 }
726 return final_card_count;
727 }
728 return 0;
729}
730
731void select_paired_cards_in_hand(CardObject** played, int played_top, bool* selection)
732{
733 // Build a set of ranks that are already selected
734 bool rank_selected[NUM_RANKS] = {0};
735 bool any_selected_rank = false;
736
737 for (int i = 0; i <= played_top; i++)
738 {
739 if (selection[i] && played[i] && played[i]->card)
740 {
741 rank_selected[played[i]->card->rank] = true;
742 any_selected_rank = true;
743 }
744 }
745
746 // If no ranks were selected initially, nothing to do
747 if (!any_selected_rank)
748 return;
749
750 // Add any unselected card to the selection if if shares a rank with the selected ranks
751 for (int i = 0; i <= played_top; i++)
752 {
753 if (played[i] && played[i]->card && !selection[i])
754 {
755 if (rank_selected[played[i]->card->rank])
756 {
757 selection[i] = true;
758 }
759 }
760 }
761}
762
763static ContainedHandTypes compute_contained_hand_types(void)
764{
765 ContainedHandTypes hand_types = {0};
766
767 // Idk if this is how Balatro does it but this is how I'm doing it
768 if (s_hand.hand_selections == 0 || s_hand.state == HAND_DISCARD)
769 {
770 return hand_types;
771 }
772
773 hand_types.HIGH_CARD = 1;
774
775 u8 suits[NUM_SUITS];
776 u8 ranks[NUM_RANKS];
777 get_hand_distribution(ranks, suits);
778
779 // The following can be optimized better but not sure how much it matters
780 u8 n_of_a_kind = hand_contains_n_of_a_kind(ranks);
781
782 // Pair and 2 Pair
783 if (n_of_a_kind >= 2)
784 {
785 hand_types.PAIR = 1;
786
787 if (hand_contains_two_pair(ranks))
788 {
789 hand_types.TWO_PAIR = 1;
790 }
791 }
792
793 // 3 OAK
794 if (n_of_a_kind >= 3)
795 {
796 hand_types.THREE_OF_A_KIND = 1;
797 }
798
799 // Straight
800 if (hand_contains_straight(ranks))
801 {
802 hand_types.STRAIGHT = 1;
803 }
804
805 // Flush
806 if (hand_contains_flush(suits))
807 {
808 hand_types.FLUSH = 1;
809 }
810
811 // Full House
812 if (n_of_a_kind >= 3 && hand_contains_full_house(ranks))
813 {
814 hand_types.FULL_HOUSE = 1;
815 }
816
817 // 4 OAK
818 if (n_of_a_kind >= 4)
819 {
820 hand_types.FOUR_OF_A_KIND = 1;
821 }
822
823 // Straight Flush
824 if (hand_types.STRAIGHT && hand_types.FLUSH)
825 {
826 hand_types.STRAIGHT_FLUSH = 1;
827 }
828
829 // Royal Flush
830 if (hand_types.STRAIGHT_FLUSH)
831 {
832 if (ranks[TEN] && ranks[JACK] && ranks[QUEEN] && ranks[KING] && ranks[ACE])
833 {
834 hand_types.ROYAL_FLUSH = 1;
835 }
836 }
837
838 // 5 OAK
839 if (n_of_a_kind >= 5)
840 {
841 hand_types.FIVE_OF_A_KIND = 1;
842 }
843
844 // Flush House and Five
845 if (hand_types.FLUSH)
846 {
847 if (hand_types.FULL_HOUSE)
848 {
849 hand_types.FLUSH_HOUSE = 1;
850 }
851
852 if (hand_types.FIVE_OF_A_KIND)
853 {
854 hand_types.FLUSH_FIVE = 1;
855 }
856 }
857
858 return hand_types;
859}
860
861static enum HandType compute_hand_type(struct ContainedHandTypes contained_types)
862{
863 enum HandType ret;
864
865 // test each pit see if it's set to 1, and return the first one
866 for (ret = FLUSH_FIVE; ret > NONE; ret--)
867 {
868 // Shift the bit we want to check to the front and mask it with 1 to keep only that
869 // Since the ContainedHandTypes is ordered the same way as the HandType enum, we
870 // can shift right by ret-1 to have the bit we want at the front
871 if ((contained_types.value >> (ret - 1)) & 0x1)
872 {
873 break;
874 }
875 }
876
877 // If we broke early, ret contains the value of the HandType enum corresponding to
878 // the position of the highest bit set to 1 in contained_types.value, which is the
879 // most powerful poker hand contained in the current Hand
880 // If not, then it contains NONE, which is what we're supposed to return when there
881 // are no Hands contained in what we played
882 return ret;
883}
Utilities for using maxmod to play sound effects.
#define MM_BASE_PITCH_RATE
The default pitch rate for the sound effect played.
Definition audio_utils.h:37
void play_sfx(mm_word id, mm_word rate, mm_byte volume)
Play a sound effect, wrapper for mmEffectEx() See https://maxmod.org/ref/functions/mm_sound_effect....
#define SFX_DEFAULT_VOLUME
Default volume for sound effects.
Definition audio_utils.h:43
Game global game variables struct definition.
Graphic utility functions.
void update_text_rect_to_center_str(Rect *rect, const char *str, enum ScreenHorzDir bias_direction)
Updates a rect so a string is centered within it.
void tte_erase_rect_wrapper(Rect rect)
A wrapper for tte_erase_rect that would use the rect struct.
void hand_set_nb_selected_cards(int new_selections)
Set the current number of selected Cards.
Definition hand.c:125
ContainedHandTypes * get_contained_hands(void)
Get the contained hands within the selected hand.
Definition hand.c:135
int find_flush_in_played_cards(CardObject **played, int top, int min_len, bool *out_selection)
Finds the best flush (set of cards with the same suit) in the given array of played cards.
Definition hand.c:544
enum HandType get_hand_type(void)
Get the current hand type.
Definition hand.c:130
int get_hand_top(void)
Get the position in hand array of the last card obtained.
Definition hand.c:105
enum HandState get_hand_state(void)
Get the hand state.
Definition hand.c:90
void swap_cards_in_hand(int idx_a, int idx_b)
Swaps the order of two cards in hand.
Definition hand.c:174
int find_straight_in_played_cards(CardObject **played, int top, int min_len, bool *out_selection)
Finds the best straight in the given array of played cards.
Definition hand.c:587
static void get_hand_distribution(u8 ranks_out[NUM_RANKS], u8 suits_out[NUM_SUITS])
Outputs the distribution of ranks and suits in the hand.
Definition hand.c:352
CardObject ** get_hand_array(void)
Get the hand array of Cards currently held in hand.
Definition hand.c:100
int hand_get_nb_selected_cards(void)
Get the current number of selected Cards.
Definition hand.c:120
void set_hand_state(enum HandState new_hand_state)
Set the hand state. Primarily used by the GAME_PLAYING game state.
Definition hand.c:95
void reorder_card_sprites_layers(void)
Destroy the sprites of the Cards held in hand and recreate then in the same order as the Cards in the...
Definition hand.c:244
void set_hand_top(int new_hand_top)
Set the position in hand array of the last card obtained.
Definition hand.c:110
void hand_select_card(int index)
Set the card at a given index in Hand as selected.
Definition hand.c:303
void hand_change_sort(bool to_sort_by_suit)
Switch to the given sort method. Can sort playing cards in two ways: by rank and suit....
Definition hand.c:294
void compute_hand_value_info(void)
Determine the HandType and ContainedHandTypes of the currently selected Cards, then print the Hand's ...
Definition hand.c:156
void sort_cards(void)
Sort the hand array according to the selected method to do that.
Definition hand.c:280
void hand_deselect_all_cards(void)
Deselect all cards in hand.
Definition hand.c:324
int hand_nb_held_cards(void)
Get the current number of Cards in hand.
Definition hand.c:115
void select_paired_cards_in_hand(CardObject **played, int played_top, bool *selection)
This is used for the special case in "Four Fingers" where you can add a pair into a straight (e....
Definition hand.c:731
Functions relative to manipulating and analyzing the contents of the Hand, a.k.a. the Cards we hold a...
API relative to the Rounds we play.
INLINE void sprite_position(Sprite *sprite, int x, int y)
Set sprite position. Inlined for efficiency.
Definition sprite.h:400
void sprite_destroy(Sprite **sprite)
Destroy Sprite.
Definition sprite.c:93
Definition hand.c:50
Utilities relating around number string representation and protected arithmatic helper functions.