Adds Shortcut and Four Fingers (#95)

* adds shortcut joker

* adds Four Fingers joker, and combination support for Four Fingers + Shortcut

---------

Co-authored-by: MeirGavish <meir.gavish@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
theturtlemafia
2025-11-01 10:28:40 -04:00
committed by GitHub
parent eb815b7820
commit 03273de829
11 changed files with 553 additions and 153 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

+1
View File
@@ -0,0 +1 @@
-gB4 -Mw4 -Mh4 -m! -pn 16
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

+2 -1
View File
@@ -21,4 +21,5 @@ DEF_JOKER_GFX(19)
DEF_JOKER_GFX(20)
DEF_JOKER_GFX(21)
DEF_JOKER_GFX(22)
DEF_JOKER_GFX(23)
DEF_JOKER_GFX(23)
DEF_JOKER_GFX(24)
+5 -1
View File
@@ -118,4 +118,8 @@ int get_money(void);
int get_game_speed(void);
void set_game_speed(int new_game_speed);
#endif // GAME_H
// joker specific functions
bool is_shortcut_joker_active(void);
int get_straight_and_flush_size(void);
#endif // GAME_H
+4
View File
@@ -13,4 +13,8 @@ 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_straight_in_played_cards(CardObject** played, int top, bool shortcut_active, int min_len, bool* out_selection);
void select_paired_cards_in_hand(CardObject** played, int top, bool* selection);
#endif
+2
View File
@@ -65,6 +65,8 @@ enum JokerEvent
#define JOKER_STENCIL_ID 16
#define PAREIDOLIA_JOKER_ID 30
#define JOKER_BRAINSTORM_ID 40
#define SHORTCUT_JOKER_ID 26
#define FOUR_FINGERS_JOKER_ID 48
typedef struct
+1
View File
@@ -25,5 +25,6 @@
#include "joker_gfx21.h"
#include "joker_gfx22.h"
#include "joker_gfx23.h"
#include "joker_gfx24.h"
#endif
+276 -137
View File
@@ -245,6 +245,23 @@ static int deck_top = -1;
static Card *discard_pile[MAX_DECK_SIZE] = {NULL};
static int discard_top = -1;
// Joker Special Variables
static int shortcut_joker_count = 0;
bool is_shortcut_joker_active(void)
{
return shortcut_joker_count > 0;
}
#define STRAIGHT_AND_FLUSH_SIZE_FOUR_FINGERS 4
#define STRAIGHT_AND_FLUSH_SIZE_DEFAULT 5
static int four_fingers_joker_count = 0;
static int straight_and_flush_size = STRAIGHT_AND_FLUSH_SIZE_DEFAULT;
int get_straight_and_flush_size(void)
{
return straight_and_flush_size;
}
// Played stack
static inline void played_push(CardObject *card_object)
{
@@ -350,11 +367,46 @@ bool is_joker_owned(int joker_id)
void add_joker(JokerObject *joker_object)
{
list_append(jokers, joker_object);
// TODO: Extract to on_joker_added() callback
// In case the player gets multiple Four Fingers Jokers,
// only change size when the first one is added
if (joker_object->joker->id == FOUR_FINGERS_JOKER_ID)
{
if (four_fingers_joker_count == 0)
{
straight_and_flush_size = STRAIGHT_AND_FLUSH_SIZE_FOUR_FINGERS;
}
four_fingers_joker_count++;
}
if (joker_object->joker->id == SHORTCUT_JOKER_ID)
{
shortcut_joker_count++;
}
}
void remove_held_joker(int joker_idx)
{
list_remove_by_idx(jokers, joker_idx);
// TODO: Extract to on_joker_removed() callback
JokerObject* joker_object = list_get(jokers, joker_idx);
// In case the player gets multiple Four Fingers Jokers,
// and only reset the size when all of them have been removed
if (joker_object->joker->id == FOUR_FINGERS_JOKER_ID)
{
four_fingers_joker_count--;
if (four_fingers_joker_count == 0)
{
straight_and_flush_size = STRAIGHT_AND_FLUSH_SIZE_DEFAULT;
}
}
if (joker_object->joker->id == SHORTCUT_JOKER_ID)
{
shortcut_joker_count--;
}
list_remove_by_idx(jokers, joker_idx);
}
int get_deck_top(void)
@@ -662,28 +714,24 @@ enum HandType hand_get_type()
res_hand_type = STRAIGHT;
}
// Check for royal flush vs regular straight flush
if (res_hand_type == STRAIGHT_FLUSH)
{
if (ranks[TEN] && ranks[JACK] && ranks[QUEEN] && ranks[KING] && ranks[ACE])
return ROYAL_FLUSH;
return STRAIGHT_FLUSH;
}
// The following can be optimized better but not sure how much it matters
u8 n_of_a_kind = hand_contains_n_of_a_kind(ranks);
if (n_of_a_kind >= 5)
{
if (res_hand_type == FLUSH)
{
if (n_of_a_kind >= 5) {
if (res_hand_type == FLUSH) {
return FLUSH_FIVE;
}
return FIVE_OF_A_KIND;
}
if (n_of_a_kind == 4)
{
// Check for royal flush vs regular straight flush
if (res_hand_type == STRAIGHT_FLUSH) {
if (ranks[TEN] && ranks[JACK] && ranks[QUEEN] && ranks[KING] && ranks[ACE])
return ROYAL_FLUSH;
return STRAIGHT_FLUSH;
}
if (n_of_a_kind == 4) {
return FOUR_OF_A_KIND;
}
@@ -692,11 +740,16 @@ enum HandType hand_get_type()
return FULL_HOUSE;
}
// Flush is more valuable than the remaining hand types, so return now
if (res_hand_type == FLUSH)
{
// Flush and Straight are more valuable than the remaining hand types, so return them now
if (res_hand_type == FLUSH) {
if (n_of_a_kind >= 5) {
return FLUSH_HOUSE;
}
return FLUSH;
}
if (res_hand_type == STRAIGHT) {
return STRAIGHT;
}
if (n_of_a_kind == 3)
{
@@ -1724,8 +1777,195 @@ void card_in_hand_loop_handle_discard_and_shuffling(int card_idx, bool* discarde
};
}
static void select_flush_and_straight_cards_in_played_hand()
{
// Special handling because Four Fingers might be active
bool final_selection[MAX_SELECTION_SIZE] = {false};
int min_len = get_straight_and_flush_size(); // Will be 4 if Four Fingers is in effect, otherwise 5
// if we have a flush in our hand
if (hand_type == FLUSH || hand_type == STRAIGHT_FLUSH || hand_type == ROYAL_FLUSH)
{
bool flush_selection[MAX_HAND_SIZE] = {false};
find_flush_in_played_cards(played, played_top, min_len, flush_selection);
// Add the results into the final selection
for (int i = 0; i <= played_top; i++)
{
final_selection[i] = flush_selection[i];
}
}
// If we have a straight in our hand
if (hand_type == STRAIGHT || hand_type == STRAIGHT_FLUSH || hand_type == ROYAL_FLUSH)
{
bool straight_selection[MAX_HAND_SIZE] = {false};
find_straight_in_played_cards(played, played_top, is_shortcut_joker_active(), min_len, straight_selection);
// Add the results into the final selection
for (int i = 0; i <= played_top; i++)
{
final_selection[i] = final_selection[i] || straight_selection[i];
}
// If Four Fingers is active, pairs can happen in a valid straight
// If Four Fingers is not active, pairs are impossible so this will not affect things
select_paired_cards_in_hand(played, played_top, final_selection);
}
// Finally, set mark the cards as selected based final_selection
for (int i = 0; i <= played_top; i++)
{
if (final_selection[i])
{
card_object_set_selected(played[i], true);
}
}
}
static void select_all_five_cards_in_played_hand()
{
for (int i = 0; i <= played_top; i++)
{
card_object_set_selected(played[i], true);
}
}
static void select_four_of_a_kind_cards_in_played_hand()
{
// find four cards with the same rank
if (played_top >= 3) // If there are 5 cards selected we just need to find the one card that doesn't match, and select the others
{
int unmatched_index = -1;
for (int i = 0; i <= played_top; i++)
{
if (played[i]->card->rank != played[(i + 1) % played_top]->card->rank && played[i]->card->rank != played[(i + 2) % played_top]->card->rank)
{
unmatched_index = i;
break;
}
}
for (int i = 0; i <= played_top; i++)
{
if (i != unmatched_index)
{
card_object_set_selected(played[i], true);
}
}
}
else // If there are only 4 cards selected we know they match
{
for (int i = 0; i <= played_top; i++)
{
card_object_set_selected(played[i], true);
}
}
}
static void select_three_of_a_kind_cards_in_played_hand()
{
// find three cards with the same rank
for (int i = 0; i <= played_top - 1; i++)
{
for (int j = i + 1; j <= played_top; j++)
{
if (played[i]->card->rank == played[j]->card->rank)
{
card_object_set_selected(played[i], true);
card_object_set_selected(played[j], true);
for (int k = j + 1; k <= played_top; k++)
{
if (played[i]->card->rank == played[k]->card->rank && !card_object_is_selected(played[k]))
{
card_object_set_selected(played[k], true);
break;
}
}
break;
}
}
if (card_object_is_selected(played[i]))
break;
}
}
static void select_two_pair_cards_in_played_hand()
{
// find two pairs of cards with the same rank
int i;
for (i = 0; i <= played_top - 1; i++)
{
for (int j = i + 1; j <= played_top; j++)
{
if (played[i]->card->rank == played[j]->card->rank)
{
card_object_set_selected(played[i], true);
card_object_set_selected(played[j], true);
break;
}
}
if (card_object_is_selected(played[i]))
break;
}
for (; i <= played_top - 1; i++) // Find second pair
{
for (int j = i + 1; j <= played_top; j++)
{
if (played[i]->card->rank == played[j]->card->rank && !card_object_is_selected(played[i]) && !card_object_is_selected(played[j]))
{
card_object_set_selected(played[i], true);
card_object_set_selected(played[j], true);
break;
}
}
}
}
static void select_pair_cards_in_played_hand()
{
// find two cards with the same rank
for (int i = 0; i <= played_top - 1; i++)
{
for (int j = i + 1; j <= played_top; j++)
{
if (played[i]->card->rank == played[j]->card->rank)
{
card_object_set_selected(played[i], true);
card_object_set_selected(played[j], true);
break;
}
}
if (card_object_is_selected(played[i]))
break;
}
}
static void select_highcard_cards_in_played_hand()
{
// find the card with the highest rank in the hand
int highest_rank_index = 0;
for (int i = 0; i <= played_top; i++)
{
if (played[i]->card->rank > played[highest_rank_index]->card->rank)
{
highest_rank_index = i;
}
}
card_object_set_selected(played[highest_rank_index], true);
}
static void cards_in_hand_update_loop(bool* discarded_card, int* played_selections, bool* sound_played)
{
// TODO: Break this function up into smaller ones, Gods be good
for (int i = hand_top + 1; i >= 0; i--) // Start from the end of the hand and work backwards because that's how Balatro does it
{
if (hand[i] != NULL)
@@ -1803,142 +2043,39 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio
{
case NONE:
break;
case HIGH_CARD: // find the card with the highest rank in the hand
int highest_rank_index = 0;
for (int i = 0; i <= played_top; i++)
{
if (played[i]->card->rank > played[highest_rank_index]->card->rank)
{
highest_rank_index = i;
}
}
card_object_set_selected(played[highest_rank_index], true);
case HIGH_CARD:
select_highcard_cards_in_played_hand();
break;
case PAIR: // find two cards with the same rank (Requires recursion)
for (int i = 0; i <= played_top - 1; i++)
{
for (int j = i + 1; j <= played_top; j++)
{
if (played[i]->card->rank == played[j]->card->rank)
{
card_object_set_selected(played[i], true);
card_object_set_selected(played[j], true);
break;
}
}
if (card_object_is_selected(played[i])) break;
}
case PAIR:
select_pair_cards_in_played_hand();
break;
case TWO_PAIR: // find two pairs of cards with the same rank (Requires recursion)
int i;
for (i = 0; i <= played_top - 1; i++)
{
for (int j = i + 1; j <= played_top; j++)
{
if (played[i]->card->rank == played[j]->card->rank)
{
card_object_set_selected(played[i], true);
card_object_set_selected(played[j], true);
break;
}
}
if (card_object_is_selected(played[i])) break;
}
for (; i <= played_top - 1; i++) // Find second pair
{
for (int j = i + 1; j <= played_top; j++)
{
if (played[i]->card->rank == played[j]->card->rank && !card_object_is_selected(played[i]) && !card_object_is_selected(played[j]))
{
card_object_set_selected(played[i], true);
card_object_set_selected(played[j], true);
break;
}
}
}
case TWO_PAIR:
select_two_pair_cards_in_played_hand();
break;
case THREE_OF_A_KIND: // find three cards with the same rank (requires recursion)
for (int i = 0; i <= played_top - 1; i++)
{
for (int j = i + 1; j <= played_top; j++)
{
if (played[i]->card->rank == played[j]->card->rank)
{
card_object_set_selected(played[i], true);
card_object_set_selected(played[j], true);
for (int k = j + 1; k <= played_top; k++)
{
if (played[i]->card->rank == played[k]->card->rank && !card_object_is_selected(played[k]))
{
card_object_set_selected(played[k], true);
break;
}
}
break;
}
}
if (card_object_is_selected(played[i])) break;
}
case THREE_OF_A_KIND:
select_three_of_a_kind_cards_in_played_hand();
break;
case FOUR_OF_A_KIND: // find four cards with the same rank (requires recursion)
if (played_top >= 3) // If there are 5 cards selected we just need to find the one card that doesn't match, and select the others
{
int unmatched_index = -1;
for (int i = 0; i <= played_top; i++)
{
if (played[i]->card->rank != played[(i + 1) % played_top]->card->rank && played[i]->card->rank != played[(i + 2) % played_top]->card->rank)
{
unmatched_index = i;
break;
}
}
for (int i = 0; i <= played_top; i++)
{
if (i != unmatched_index)
{
card_object_set_selected(played[i], true);
}
}
}
else // If there are only 4 cards selected we know they match
{
for (int i = 0; i <= played_top; i++)
{
card_object_set_selected(played[i], true);
}
}
case FOUR_OF_A_KIND:
select_four_of_a_kind_cards_in_played_hand();
break;
case STRAIGHT:
/* FALL THROUGH */
case FLUSH:
/* FALL THROUGH */
case FULL_HOUSE:
/* FALL THROUGH */
case STRAIGHT_FLUSH:
/* FALL THROUGH */
case ROYAL_FLUSH:
select_flush_and_straight_cards_in_played_hand();
break;
// ELSE FALL THROUGH
case FULL_HOUSE:
/* FALL THROUGH */
case FIVE_OF_A_KIND:
/* FALL THROUGH */
case FLUSH_HOUSE:
/* FALL THROUGH */
case FLUSH_FIVE: // Select all played cards in the hand (This is functionally identical as the above hand types)
for (int i = 0; i <= played_top; i++)
{
card_object_set_selected(played[i], true);
}
case FLUSH_FIVE: // Select all played cards in the hand
select_all_five_cards_in_played_hand();
break;
}
}
@@ -1978,6 +2115,8 @@ static bool check_and_score_joker_for_event(int* iteration_start, Card* played_c
static void played_cards_update_loop(bool* discarded_card, int* played_selections, bool* sound_played)
{
// TODO: Break this function up into smaller ones.
// So this one is a bit fucking weird because I have to work kinda backwards for everything because of the order of the pushed cards from the hand to the play stack
// (also crazy that the company that published Balatro is called "Playstack" and this is a play stack, but I digress)
for (int i = 0; i <= played_top; i++)
+259 -13
View File
@@ -2,10 +2,13 @@
#include "card.h"
#include "game.h"
static void get_distribution(CardObject **cards, int top, u8 *ranks_out, u8 *suits_out) {
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;
CardObject **cards = get_hand_array();
int top = get_hand_top();
for (int i = 0; i <= top; i++) {
if (cards[i] && card_object_is_selected(cards[i])) {
ranks_out[cards[i]->card->rank]++;
@@ -14,12 +17,17 @@ static void get_distribution(CardObject **cards, int top, u8 *ranks_out, u8 *sui
}
}
void get_hand_distribution(u8 *ranks_out, u8 *suits_out) {
get_distribution(get_hand_array(), get_hand_top(), ranks_out, suits_out);
}
void get_played_distribution(u8 *ranks_out, u8 *suits_out) {
get_distribution(get_played_array(), get_played_top(), ranks_out, 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;
CardObject **played = get_played_array();
int top = get_played_top();
for (int i = 0; i <= top; i++) {
if (!played[i]) continue;
ranks_out[played[i]->card->rank]++;
suits_out[played[i]->card->suit]++;
}
}
// Returns the highest N of a kind. So a full-house would return 3.
@@ -63,15 +71,103 @@ bool hand_contains_full_house(u8* ranks) {
return (count_three >= 2 || (count_three && count_pair));
}
// This is mostly from Google Gemini
bool hand_contains_straight(u8 *ranks) {
for (int i = 0; i < NUM_RANKS - 4; i++)
if (!is_shortcut_joker_active())
{
if (ranks[i] && ranks[i + 1] && ranks[i + 2] && ranks[i + 3] && ranks[i + 4])
return true;
int straight_size = get_straight_and_flush_size();
// This is the regular case of detecting straights
int run = 0;
for (int i = 0; i < NUM_RANKS; ++i)
{
if (ranks[i]) {
if (++run >= straight_size)
return true;
} else {
run = 0;
}
}
// Check for ace low straight
if (straight_size >= 2 && ranks[ACE]) {
// 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
if (last_needed <= FIVE) {
bool ok = true;
for (int r = TWO; r <= last_needed; ++r)
{
if (!ranks[r]) {
ok = false;
break;
}
}
if (ok)
return true;
}
}
return false;
} else
{
// 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
// the longest possible straight that can end on each rank
// and stopping when we find one that is {straight-size} cards long
u8 longest_short_cut_at[NUM_RANKS] = {0};
// A low ace can start a sequence. 'ace_low_len' is 1 if an ace is present,
// acting as a potential predecessor for TWO and THREE.
int ace_low_len = ranks[ACE] ? 1 : 0;
// Iterate through all ranks from TWO up to ACE.
for (int i = 0; i < NUM_RANKS; i++)
{
// No cards in this rank, no straight can end here, continue
if (ranks[i] == 0)
{
longest_short_cut_at[i] = 0;
continue;
}
int prev_len1 = 0;
int prev_len2 = 0;
// This logic handles the special connections for ace-low straights.
if (i == TWO)
{
// A TWO can be preceded by a low ACE (no skip).
prev_len1 = ace_low_len;
}
else if (i == THREE)
{
// A THREE can be preceded by a TWO (no skip) or a low ACE (skip).
prev_len1 = longest_short_cut_at[TWO];
prev_len2 = ace_low_len;
}
else if (i == ACE)
{
// An ACE (as the highest card) can be preceded by a KING or a QUEEN.
prev_len1 = longest_short_cut_at[KING];
prev_len2 = longest_short_cut_at[QUEEN];
}
else // For all other cards (FOUR through KING).
{
// A card can be preceded by the rank directly below or two ranks below.
prev_len1 = longest_short_cut_at[i - 1];
prev_len2 = longest_short_cut_at[i - 2];
}
// The length of the straight ending at rank 'i' is 1 (for the card itself)
// plus the length of the longest valid preceding straight.
longest_short_cut_at[i] = 1 + max(prev_len1, prev_len2);
// If we've formed a sequence of {straight-size} or more cards, we have a straight.
if (longest_short_cut_at[i] >= get_straight_and_flush_size())
{
return true;
}
}
}
// Check for ace low straight
if (ranks[ACE] && ranks[TWO] && ranks[THREE] && ranks[FOUR] && ranks[FIVE])
return true;
return false;
}
@@ -79,10 +175,160 @@ bool hand_contains_straight(u8 *ranks) {
bool hand_contains_flush(u8 *suits) {
for (int i = 0; i < NUM_SUITS; i++)
{
if (suits[i] >= MAX_SELECTION_SIZE) // this allows MAX_SELECTION_SIZE - 1 for four fingers joker
if (suits[i] >= get_straight_and_flush_size())
{
return true;
}
}
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.
/**
* 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.
*
* @param played Array of pointers to CardObject representing played cards.
* @param top Index of the top of the played stack.
* @param min_len Minimum number of cards required for a flush.
* @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.
*/
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;
int suit_counts[NUM_SUITS] = {0};
for (int i = 0; i <= top; i++) {
if (played[i] && played[i]->card) {
suit_counts[played[i]->card->suit]++;
}
}
int best_suit = -1;
int best_count = 0;
for (int i = 0; i < NUM_SUITS; i++) {
if (suit_counts[i] > best_count) {
best_count = suit_counts[i];
best_suit = i;
}
}
if (best_count >= min_len) {
for (int i = 0; i <= top; i++) {
if (played[i] && played[i]->card && played[i]->card->suit == best_suit) {
out_selection[i] = true;
}
}
return best_count;
}
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[].
// 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) {
if (top < 0) return 0;
for (int i = 0; i <= top; i++) out_selection[i] = false;
// --- Setup for Backtracking DP ---
u8 longest_straight_at[NUM_RANKS] = {0};
int parent[NUM_RANKS];
for(int i=0; i<NUM_RANKS; i++) parent[i] = -1;
u8 ranks[NUM_RANKS] = {0};
for (int i = 0; i <= top; i++) {
if (played[i] && played[i]->card) {
ranks[played[i]->card->rank]++;
}
}
// --- Run DP to find longest straight ---
// This is nearly identical to hand_contains_straight() logic
// TODO: Consolidate functions to avoid code duplication?
// Might cost performance because this does a little more
int ace_low_len = ranks[ACE] ? 1 : 0;
for (int i = 0; i < NUM_RANKS; i++) {
if (ranks[i] > 0) {
int prev1 = 0, prev2 = 0;
int parent1 = -1, parent2 = -1;
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; }
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);
// in hand_contains_straight()
if(prev1 >= prev2) { longest_straight_at[i] = 1 + prev1; parent[i] = parent1; }
else { longest_straight_at[i] = 1 + prev2; parent[i] = parent2; }
}
}
// --- Find best straight and backtrack ---
int best_len = 0;
int end_rank = -1;
for (int i = 0; i < NUM_RANKS; i++) {
if (longest_straight_at[i] >= best_len) {
best_len = longest_straight_at[i];
end_rank = i;
}
}
if (best_len >= min_len) {
u8 needed_ranks[NUM_RANKS] = {0};
int current_rank = end_rank;
while (current_rank != -1 && best_len > 0) {
needed_ranks[current_rank]++;
current_rank = parent[current_rank];
best_len--;
}
for (int i = 0; i <= top; i++) {
if (played[i] && played[i]->card && needed_ranks[played[i]->card->rank] > 0) {
out_selection[i] = true;
needed_ranks[played[i]->card->rank]--;
}
}
int final_card_count = 0;
for(int i=0; i<=top; i++) {
if(out_selection[i]) final_card_count++;
}
return final_card_count;
}
return 0;
}
// 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)
void select_paired_cards_in_hand(CardObject** played, int played_top, bool* selection) {
// Build a set of ranks that are already selected
bool rank_selected[NUM_RANKS] = {0};
bool any_selected_rank = false;
for (int i = 0; i <= played_top; i++) {
if (selection[i] && played[i] && played[i]->card) {
rank_selected[played[i]->card->rank] = true;
any_selected_rank = true;
}
}
// If no ranks were selected initially, nothing to do
if (!any_selected_rank) return;
// Add any unselected card to the selection if if shares a rank with the selected ranks
for (int i = 0; i <= played_top; i++) {
if (played[i] && played[i]->card && !selection[i]) {
if (rank_selected[played[i]->card->rank]) {
selection[i] = true;
}
}
}
}
+3 -1
View File
@@ -1057,7 +1057,7 @@ const JokerInfo joker_registry[] = {
{ COMMON_JOKER, 4, even_steven_joker_effect, on_joker_created_noop }, // 23
{ COMMON_JOKER, 5, blue_joker_effect, on_joker_created_noop }, // 24
{ COMMON_JOKER, 4, odd_todd_joker_effect, on_joker_created_noop }, // 25
{ COMMON_JOKER, 4, scholar_joker_effect, on_joker_created_noop }, // 26
{ UNCOMMON_JOKER, 7, NULL, /* Shortcut */ on_joker_created_noop }, // 26
{ COMMON_JOKER, 4, business_card_joker_effect, on_joker_created_noop }, // 27
// Business card should be paired with Shortcut for palette optimization when it's added
{ COMMON_JOKER, 4, scary_face_joker_effect, on_joker_created_noop }, // 28
@@ -1080,6 +1080,8 @@ const JokerInfo joker_registry[] = {
{ UNCOMMON_JOKER, 6, sock_and_buskin_joker_effect, sock_and_buskin_on_joker_created }, // 45
{ UNCOMMON_JOKER, 6, hack_joker_effect, hack_on_joker_created }, // 46
{ COMMON_JOKER, 4, hanging_chad_joker_effect, hanging_chad_on_joker_created }, // 47
{ UNCOMMON_JOKER, 7, NULL,/* Four Fingers */ on_joker_created_noop }, // 48
{ COMMON_JOKER, 4, scholar_joker_effect, on_joker_created_noop }, // 49
// The following jokers don't have sprites yet,
// uncomment them when their sprites are added.