From 341231b0439fe29a3cbf9197fd53d3a9cf291790 Mon Sep 17 00:00:00 2001 From: Mykhailo Shevchuk Date: Thu, 23 Jul 2026 23:32:25 +0300 Subject: [PATCH] Video Poker: saturate the payout so a big win cannot overflow the bank (closes #239) score += bet * paytable[] was computed entirely in int, so both the payout and the resulting bank could overflow. At the reported bet that corrupted the score, and the score <= 0 check right below then dropped the player on the "You have run out of money!" screen - a jackpot reported as a bankruptcy. Depending on the bet it could instead wrap positive and silently pay out the wrong amount. The new bank total is now worked out in 64 bits and saturated. This is the only place the score grows, so the bank - and highscore, which just copies it - can no longer overflow. The ceiling is MAX_BANK rather than INT_MAX on purpose: parking the bank on INT_MAX would have made a pre-existing overflow reachable, since half-bank then double leaves the bet at INT_MAX-1 and the +10 step then overflows. Capping at roughly half of INT_MAX keeps the whole bet domain away from the type limit instead of needing a per-site guard, and since clamp_bet already binds bet to score, bounding score covers bet too. Also hoist the doubled recognize() call into hand_rank; it is idempotent, so this is behaviour-preserving. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps_source_code/videopoker/CHANGELOG.md | 5 +++++ apps_source_code/videopoker/application.fam | 2 +- apps_source_code/videopoker/poker.c | 17 ++++++++++++++--- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/apps_source_code/videopoker/CHANGELOG.md b/apps_source_code/videopoker/CHANGELOG.md index 75afced6..5095307f 100644 --- a/apps_source_code/videopoker/CHANGELOG.md +++ b/apps_source_code/videopoker/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 1.5 + +- Fix a winning hand at a very large bet overflowing the bank, which could pay out the wrong amount or end the game on the "You have run out of money!" screen +- Cap the bank at $1,000,000,000 so the bet controls cannot overflow either + ## 1.4 - Add Up/Down press to double or halve the bet (replaces Up = all-in; all-in is still reachable by doubling past half the bank or wrapping Left at the minimum) diff --git a/apps_source_code/videopoker/application.fam b/apps_source_code/videopoker/application.fam index 4908c6b9..fde0ac80 100644 --- a/apps_source_code/videopoker/application.fam +++ b/apps_source_code/videopoker/application.fam @@ -11,6 +11,6 @@ App( fap_category="Games", fap_author="@PixlEmly", fap_weburl="https://github.com/PixlEmly", - fap_version="1.4", + fap_version="1.5", fap_description="Video poker is a casino game based on five-card draw poker", ) diff --git a/apps_source_code/videopoker/poker.c b/apps_source_code/videopoker/poker.c index 7e1e8f7e..ef722808 100644 --- a/apps_source_code/videopoker/poker.c +++ b/apps_source_code/videopoker/poker.c @@ -23,6 +23,10 @@ Sometimes duplicate cards will show up. there is a function to test this. I shou #define TAG "Video Poker" +/* Bank ceiling, at roughly half of INT_MAX so the bet arithmetic (+/-10 steps +and doubling) stays inside int - a larger multiplier would need a lower cap */ +#define MAX_BANK 1000000000 + static void Shake(void) { NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); notification_message(notification, &sequence_single_vibro); @@ -785,9 +789,16 @@ int32_t video_poker_app(void* p) { ->held[poker_player->selected]; //cursed and bad pls replace } else if(poker_player->GameState == 3) { /* accept your fate */ - if(recognize(poker_player) != 9) { - poker_player->score += - poker_player->bet * paytable[recognize(poker_player)]; + int hand_rank = recognize(poker_player); + if(hand_rank != 9) { + /* Both the payout and the new bank total overflow int at very + large bets - as reported that corrupted the score and dropped a + winning hand on the game-over screen. Work the total out in 64 + bits and saturate: a bank this large is already meaningless, and + widening score would ripple through every %d */ + int64_t total = (int64_t)poker_player->score + + (int64_t)poker_player->bet * paytable[hand_rank]; + poker_player->score = total > MAX_BANK ? MAX_BANK : (int)total; } poker_player->GameState = 1; clamp_bet(poker_player);