Clang format hand analysis (#261)

* Clang format hand analysis

* Update source/hand_analysis.c

Co-authored-by: MeirGavish <meir.gavish@gmail.com>

---------

Co-authored-by: MeirGavish <meir.gavish@gmail.com>
This commit is contained in:
Rickey
2025-11-25 13:45:07 -08:00
committed by GitHub
co-authored by MeirGavish
parent e910aee184
commit 2c32ff69e3
3 changed files with 204 additions and 91 deletions
@@ -20,6 +20,7 @@ jobs:
run: > run: >
clang-format --dry-run -Werror clang-format --dry-run -Werror
include/bitset.h source/bitset.c include/bitset.h source/bitset.c
include/hand_analysis.h source/hand_analysis.c
include/list.h source/list.c include/list.h source/list.c
include/sprite.h source/sprite.c include/sprite.h source/sprite.c
include/splash_screen.h source/splash_screen.c include/splash_screen.h source/splash_screen.c
+9 -8
View File
@@ -1,17 +1,18 @@
#ifndef HAND_ANALYSIS_H #ifndef HAND_ANALYSIS_H
#define HAND_ANALYSIS_H #define HAND_ANALYSIS_H
#include <tonc.h>
#include "card.h" #include "card.h"
void get_hand_distribution(u8 *ranks_out, u8 *suits_out); #include <tonc.h>
void get_played_distribution(u8 *ranks_out, u8 *suits_out);
u8 hand_contains_n_of_a_kind(u8 *ranks); void get_hand_distribution(u8* ranks_out, u8* suits_out);
bool hand_contains_two_pair(u8 *ranks); void get_played_distribution(u8* ranks_out, u8* suits_out);
bool hand_contains_full_house(u8 *ranks);
bool hand_contains_straight(u8 *ranks); u8 hand_contains_n_of_a_kind(u8* ranks);
bool hand_contains_flush(u8 *suits); bool hand_contains_two_pair(u8* ranks);
bool hand_contains_full_house(u8* ranks);
bool hand_contains_straight(u8* ranks);
bool hand_contains_flush(u8* suits);
int find_flush_in_played_cards(CardObject** played, int top, int min_len, bool* out_selection); int find_flush_in_played_cards(CardObject** played, int top, int min_len, bool* out_selection);
int find_straight_in_played_cards(CardObject** played, int top, bool shortcut_active, int min_len, bool* out_selection); int find_straight_in_played_cards(CardObject** played, int top, bool shortcut_active, int min_len, bool* out_selection);
+189 -78
View File
@@ -1,49 +1,64 @@
#include "hand_analysis.h" #include "hand_analysis.h"
#include "card.h" #include "card.h"
#include "game.h" #include "game.h"
void get_hand_distribution(u8* ranks_out, u8* suits_out)
{
for (int i = 0; i < NUM_RANKS; i++)
ranks_out[i] = 0;
for (int i = 0; i < NUM_SUITS; i++)
suits_out[i] = 0;
void get_hand_distribution(u8 *ranks_out, u8 *suits_out) { CardObject** cards = get_hand_array();
for (int i = 0; i < NUM_RANKS; i++) ranks_out[i] = 0;
for (int i = 0; i < NUM_SUITS; i++) suits_out[i] = 0;
CardObject **cards = get_hand_array();
int top = get_hand_top(); int top = get_hand_top();
for (int i = 0; i <= top; i++) { for (int i = 0; i <= top; i++)
if (cards[i] && card_object_is_selected(cards[i])) { {
if (cards[i] && card_object_is_selected(cards[i]))
{
ranks_out[cards[i]->card->rank]++; ranks_out[cards[i]->card->rank]++;
suits_out[cards[i]->card->suit]++; suits_out[cards[i]->card->suit]++;
} }
} }
} }
void get_played_distribution(u8 *ranks_out, u8 *suits_out) { void get_played_distribution(u8* ranks_out, u8* suits_out)
for (int i = 0; i < NUM_RANKS; i++) ranks_out[i] = 0; {
for (int i = 0; i < NUM_SUITS; i++) suits_out[i] = 0; for (int i = 0; i < NUM_RANKS; i++)
ranks_out[i] = 0;
for (int i = 0; i < NUM_SUITS; i++)
suits_out[i] = 0;
CardObject **played = get_played_array(); CardObject** played = get_played_array();
int top = get_played_top(); int top = get_played_top();
for (int i = 0; i <= top; i++) { for (int i = 0; i <= top; i++)
if (!played[i]) continue; {
if (!played[i])
continue;
ranks_out[played[i]->card->rank]++; ranks_out[played[i]->card->rank]++;
suits_out[played[i]->card->suit]++; suits_out[played[i]->card->suit]++;
} }
} }
// Returns the highest N of a kind. So a full-house would return 3. // Returns the highest N of a kind. So a full-house would return 3.
u8 hand_contains_n_of_a_kind(u8 *ranks) { u8 hand_contains_n_of_a_kind(u8* ranks)
{
u8 highest_n = 0; u8 highest_n = 0;
for (int i = 0; i < NUM_RANKS; i++) { for (int i = 0; i < NUM_RANKS; i++)
{
if (ranks[i] > highest_n) if (ranks[i] > highest_n)
highest_n = ranks[i]; highest_n = ranks[i];
} }
return highest_n; return highest_n;
} }
bool hand_contains_two_pair(u8 *ranks) { bool hand_contains_two_pair(u8* ranks)
{
bool contains_other_pair = false; bool contains_other_pair = false;
for (int i = 0; i < NUM_RANKS; i++) { for (int i = 0; i < NUM_RANKS; i++)
if (ranks[i] >= 2) { {
if (ranks[i] >= 2)
{
if (contains_other_pair) if (contains_other_pair)
return true; return true;
contains_other_pair = true; contains_other_pair = true;
@@ -52,14 +67,18 @@ bool hand_contains_two_pair(u8 *ranks) {
return false; return false;
} }
bool hand_contains_full_house(u8* ranks) { bool hand_contains_full_house(u8* ranks)
{
int count_three = 0; int count_three = 0;
int count_pair = 0; int count_pair = 0;
for (int i = 0; i < NUM_RANKS; i++) { for (int i = 0; i < NUM_RANKS; i++)
if (ranks[i] >= 3) { {
if (ranks[i] >= 3)
{
count_three++; count_three++;
} }
else if (ranks[i] >= 2) { else if (ranks[i] >= 2)
{
count_pair++; count_pair++;
} }
} }
@@ -72,7 +91,8 @@ bool hand_contains_full_house(u8* ranks) {
} }
// This is mostly from Google Gemini // This is mostly from Google Gemini
bool hand_contains_straight(u8 *ranks) { bool hand_contains_straight(u8* ranks)
{
if (!is_shortcut_joker_active()) if (!is_shortcut_joker_active())
{ {
int straight_size = get_straight_and_flush_size(); int straight_size = get_straight_and_flush_size();
@@ -80,23 +100,30 @@ bool hand_contains_straight(u8 *ranks) {
int run = 0; int run = 0;
for (int i = 0; i < NUM_RANKS; ++i) for (int i = 0; i < NUM_RANKS; ++i)
{ {
if (ranks[i]) { if (ranks[i])
{
if (++run >= straight_size) if (++run >= straight_size)
return true; return true;
} else { }
else
{
run = 0; run = 0;
} }
} }
// Check for ace low straight // Check for ace low straight
if (straight_size >= 2 && ranks[ACE]) { if (straight_size >= 2 && ranks[ACE])
{
// With A as low, the highest rank you can use is FIVE. // With A as low, the highest rank you can use is FIVE.
int last_needed = TWO + (straight_size - 2); // -1 for inclusive integer distance and another -1 for the Ace e.g. need=5 -> need 2..5 // -1 for inclusive integer distance and another -1 for the Ace e.g. need=5 -> need 2..5
if (last_needed <= FIVE) { int last_needed = TWO + (straight_size - 2);
if (last_needed <= FIVE)
{
bool ok = true; bool ok = true;
for (int r = TWO; r <= last_needed; ++r) for (int r = TWO; r <= last_needed; ++r)
{ {
if (!ranks[r]) { if (!ranks[r])
{
ok = false; ok = false;
break; break;
} }
@@ -107,7 +134,8 @@ bool hand_contains_straight(u8 *ranks) {
} }
return false; return false;
} else }
else
{ {
// Shortcut Joker is active, we have to detect straights where any card may "skip" 1 rank // Shortcut Joker is active, we have to detect straights where any card may "skip" 1 rank
// We do this with a dynamic programming algorithm that calculates // We do this with a dynamic programming algorithm that calculates
@@ -172,7 +200,8 @@ bool hand_contains_straight(u8 *ranks) {
return false; return false;
} }
bool hand_contains_flush(u8 *suits) { bool hand_contains_flush(u8* suits)
{
for (int i = 0; i < NUM_SUITS; i++) for (int i = 0; i < NUM_SUITS; i++)
{ {
if (suits[i] >= get_straight_and_flush_size()) if (suits[i] >= get_straight_and_flush_size())
@@ -183,7 +212,8 @@ bool hand_contains_flush(u8 *suits) {
return false; return false;
} }
// Returns the number of cards in the best flush found or 0 if no flush of min_len is found, and marks them in out_selection. // Returns the number of cards in the best flush found
// or 0 if no flush of min_len is found, and marks them in out_selection.
/** /**
* Finds the largest flush (set of cards with the same suit) in the given array of played cards. * Finds the largest flush (set of cards with the same suit) in the given array of played cards.
* Marks the cards belonging to the best flush in the out_selection array. * Marks the cards belonging to the best flush in the out_selection array.
@@ -194,29 +224,39 @@ bool hand_contains_flush(u8 *suits) {
* @param out_selection Output array of bools; set to true for cards in the best flush, false otherwise. * @param out_selection Output array of bools; set to true for cards in the best flush, false otherwise.
* @return The number of cards in the best flush found, or 0 if no flush meets min_len. * @return The number of cards in the best flush found, or 0 if no flush meets min_len.
*/ */
int find_flush_in_played_cards(CardObject** played, int top, int min_len, bool* out_selection) { int find_flush_in_played_cards(CardObject** played, int top, int min_len, bool* out_selection)
if (top < 0) return 0; {
for (int i = 0; i <= top; i++) out_selection[i] = false; if (top < 0)
return 0;
for (int i = 0; i <= top; i++)
out_selection[i] = false;
int suit_counts[NUM_SUITS] = {0}; int suit_counts[NUM_SUITS] = {0};
for (int i = 0; i <= top; i++) { for (int i = 0; i <= top; i++)
if (played[i] && played[i]->card) { {
if (played[i] && played[i]->card)
{
suit_counts[played[i]->card->suit]++; suit_counts[played[i]->card->suit]++;
} }
} }
int best_suit = -1; int best_suit = -1;
int best_count = 0; int best_count = 0;
for (int i = 0; i < NUM_SUITS; i++) { for (int i = 0; i < NUM_SUITS; i++)
if (suit_counts[i] > best_count) { {
if (suit_counts[i] > best_count)
{
best_count = suit_counts[i]; best_count = suit_counts[i];
best_suit = i; best_suit = i;
} }
} }
if (best_count >= min_len) { if (best_count >= min_len)
for (int i = 0; i <= top; i++) { {
if (played[i] && played[i]->card && played[i]->card->suit == best_suit) { for (int i = 0; i <= top; i++)
{
if (played[i] && played[i]->card && played[i]->card->suit == best_suit)
{
out_selection[i] = true; out_selection[i] = true;
} }
} }
@@ -225,20 +265,26 @@ int find_flush_in_played_cards(CardObject** played, int top, int min_len, bool*
return 0; return 0;
} }
// Returns the number of cards in the best straight or 0 if no straight of min_len is found, marks as true them in out_selection[]. // Returns the number of cards in the best straight or 0 if no straight of min_len is found, marks as true them in
// This is mostly from Google Gemini // out_selection[]. This is mostly from Google Gemini
int find_straight_in_played_cards(CardObject** played, int top, bool shortcut_active, int min_len, bool* out_selection) { int find_straight_in_played_cards(CardObject** played, int top, bool shortcut_active, int min_len, bool* out_selection)
if (top < 0) return 0; {
for (int i = 0; i <= top; i++) out_selection[i] = false; if (top < 0)
return 0;
for (int i = 0; i <= top; i++)
out_selection[i] = false;
// --- Setup for Backtracking DP --- // --- Setup for Backtracking DP ---
u8 longest_straight_at[NUM_RANKS] = {0}; u8 longest_straight_at[NUM_RANKS] = {0};
int parent[NUM_RANKS]; int parent[NUM_RANKS];
for(int i=0; i<NUM_RANKS; i++) parent[i] = -1; for (int i = 0; i < NUM_RANKS; i++)
parent[i] = -1;
u8 ranks[NUM_RANKS] = {0}; u8 ranks[NUM_RANKS] = {0};
for (int i = 0; i <= top; i++) { for (int i = 0; i <= top; i++)
if (played[i] && played[i]->card) { {
if (played[i] && played[i]->card)
{
ranks[played[i]->card->rank]++; ranks[played[i]->card->rank]++;
} }
} }
@@ -248,58 +294,116 @@ int find_straight_in_played_cards(CardObject** played, int top, bool shortcut_ac
// TODO: Consolidate functions to avoid code duplication? // TODO: Consolidate functions to avoid code duplication?
// Might cost performance because this does a little more // Might cost performance because this does a little more
int ace_low_len = ranks[ACE] ? 1 : 0; int ace_low_len = ranks[ACE] ? 1 : 0;
for (int i = 0; i < NUM_RANKS; i++) { for (int i = 0; i < NUM_RANKS; i++)
if (ranks[i] > 0) { {
if (ranks[i] > 0)
{
int prev1 = 0, prev2 = 0; int prev1 = 0, prev2 = 0;
int parent1 = -1, parent2 = -1; int parent1 = -1, parent2 = -1;
if (shortcut_active) { if (shortcut_active)
if (i == TWO) { prev1 = ace_low_len; parent1 = ACE; } {
else if (i == THREE) { prev1 = longest_straight_at[TWO]; parent1 = TWO; prev2 = ace_low_len; parent2 = ACE; } if (i == TWO)
else if (i == ACE) { prev1 = longest_straight_at[KING]; parent1 = KING; prev2 = longest_straight_at[QUEEN]; parent2 = QUEEN; } {
else { prev1 = longest_straight_at[i-1]; parent1 = i-1; if (i > 1) { prev2 = longest_straight_at[i-2]; parent2 = i-2; }} prev1 = ace_low_len;
} else { parent1 = ACE;
if (i == TWO) { prev1 = ace_low_len; parent1 = ACE; } }
else if (i == ACE) { prev1 = longest_straight_at[KING]; parent1 = KING; } else if (i == THREE)
else { prev1 = longest_straight_at[i-1]; parent1 = i-1; } {
prev1 = longest_straight_at[TWO];
parent1 = TWO;
prev2 = ace_low_len;
parent2 = ACE;
}
else if (i == ACE)
{
prev1 = longest_straight_at[KING];
parent1 = KING;
prev2 = longest_straight_at[QUEEN];
parent2 = QUEEN;
}
else
{
prev1 = longest_straight_at[i - 1];
parent1 = i - 1;
if (i > 1)
{
prev2 = longest_straight_at[i - 2];
parent2 = i - 2;
}
}
}
else
{
if (i == TWO)
{
prev1 = ace_low_len;
parent1 = ACE;
}
else if (i == ACE)
{
prev1 = longest_straight_at[KING];
parent1 = KING;
}
else
{
prev1 = longest_straight_at[i - 1];
parent1 = i - 1;
}
} }
// Parallels longest_short_cut_at[i] = 1 + max(prev_len1, prev_len2); // Parallels longest_short_cut_at[i] = 1 + max(prev_len1, prev_len2);
// in hand_contains_straight() // in hand_contains_straight()
if(prev1 >= prev2) { longest_straight_at[i] = 1 + prev1; parent[i] = parent1; } if (prev1 >= prev2)
else { longest_straight_at[i] = 1 + prev2; parent[i] = parent2; } {
longest_straight_at[i] = 1 + prev1;
parent[i] = parent1;
}
else
{
longest_straight_at[i] = 1 + prev2;
parent[i] = parent2;
}
} }
} }
// --- Find best straight and backtrack --- // --- Find best straight and backtrack ---
int best_len = 0; int best_len = 0;
int end_rank = -1; int end_rank = -1;
for (int i = 0; i < NUM_RANKS; i++) { for (int i = 0; i < NUM_RANKS; i++)
if (longest_straight_at[i] >= best_len) { {
if (longest_straight_at[i] >= best_len)
{
best_len = longest_straight_at[i]; best_len = longest_straight_at[i];
end_rank = i; end_rank = i;
} }
} }
if (best_len >= min_len) { if (best_len >= min_len)
{
u8 needed_ranks[NUM_RANKS] = {0}; u8 needed_ranks[NUM_RANKS] = {0};
int current_rank = end_rank; int current_rank = end_rank;
while (current_rank != -1 && best_len > 0) { while (current_rank != -1 && best_len > 0)
{
needed_ranks[current_rank]++; needed_ranks[current_rank]++;
current_rank = parent[current_rank]; current_rank = parent[current_rank];
best_len--; best_len--;
} }
for (int i = 0; i <= top; i++) { for (int i = 0; i <= top; i++)
if (played[i] && played[i]->card && needed_ranks[played[i]->card->rank] > 0) { {
if (played[i] && played[i]->card && needed_ranks[played[i]->card->rank] > 0)
{
out_selection[i] = true; out_selection[i] = true;
needed_ranks[played[i]->card->rank]--; needed_ranks[played[i]->card->rank]--;
} }
} }
int final_card_count = 0; int final_card_count = 0;
for(int i=0; i<=top; i++) { for (int i = 0; i <= top; i++)
if(out_selection[i]) final_card_count++; {
if (out_selection[i])
final_card_count++;
} }
return final_card_count; return final_card_count;
} }
@@ -308,25 +412,32 @@ int find_straight_in_played_cards(CardObject** played, int top, bool shortcut_ac
// This is used for the special case in "Four Fingers" where you can add a pair into a straight // This is used for the special case in "Four Fingers" where you can add a pair into a straight
// (e.g. AA234 should score all 5 cards) // (e.g. AA234 should score all 5 cards)
void select_paired_cards_in_hand(CardObject** played, int played_top, bool* selection) { void select_paired_cards_in_hand(CardObject** played, int played_top, bool* selection)
{
// Build a set of ranks that are already selected // Build a set of ranks that are already selected
bool rank_selected[NUM_RANKS] = {0}; bool rank_selected[NUM_RANKS] = {0};
bool any_selected_rank = false; bool any_selected_rank = false;
for (int i = 0; i <= played_top; i++) { for (int i = 0; i <= played_top; i++)
if (selection[i] && played[i] && played[i]->card) { {
if (selection[i] && played[i] && played[i]->card)
{
rank_selected[played[i]->card->rank] = true; rank_selected[played[i]->card->rank] = true;
any_selected_rank = true; any_selected_rank = true;
} }
} }
// If no ranks were selected initially, nothing to do // If no ranks were selected initially, nothing to do
if (!any_selected_rank) return; if (!any_selected_rank)
return;
// Add any unselected card to the selection if if shares a rank with the selected ranks // Add any unselected card to the selection if if shares a rank with the selected ranks
for (int i = 0; i <= played_top; i++) { for (int i = 0; i <= played_top; i++)
if (played[i] && played[i]->card && !selection[i]) { {
if (rank_selected[played[i]->card->rank]) { if (played[i] && played[i]->card && !selection[i])
{
if (rank_selected[played[i]->card->rank])
{
selection[i] = true; selection[i] = true;
} }
} }