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