TL;DR:
I decompiled Gamble With Your Friends to work out the odds of every minigame in the casino. The table below shows the games by approximate edge, sorted by “Return To Player” (RTP). Bigger RTP means the player has better odds. 100% here means essentially “perfectly fair 50/50 odds”.
| Game | Bet | RTP | Favors |
|---|---|---|---|
| Craps | N/A | 137.75% | Player |
| Money Wheel | Orange | 135.14% | Player |
| Ducks | Faruk | ~134.80% | Player |
| Video Poker | Strategy Table | 128.00% | Player |
| Slots | N/A | ~115.70% | Player |
| Ducks | Erlaf | ~109.60% | Player |
| Money Wheel | Red | 108.11% | Player |
| Blackjack | Strategy Table | ~102.00% | Player |
| Coin Flip | Heads / Tails | 100.00% | Fair |
| Penguins | Any step | 100.00% | Fair |
| Prize Wheel | Any segment | 100.00% | Fair |
| Hi-Lo | Any threshold | 100.00% | Fair |
| Dragon Tower | Any difficulty | 100.00% | Fair |
| Keno | Any pick count | 100.00% | Fair |
| Minesweeper | Any mine count | 100.00% | Fair |
| Roulette | Any bet | 97.30% | House |
| Baccarat | Player / Banker | 90.10% | House |
| Crash | Any cashout | 85.00% | House |
| Ducks | Yarl | ~81.60% | House |
| Money Wheel | Blue | 81.08% | House |
| Baccarat | Tie | 79.20% | House |
| Plinko | Single Ball | 78.00% | House |
| Money Wheel | Green | 75.68% | House |
| Ducks | Jabin | ~74.40% | House |
Why I did this
Gamble with your Friends is the latest entry in a sub-genre of indie titles that has become known as Friendslop. While I don’t agree with how pejorative this name is, I won’t deny that it is a product of our current moment. Gamble with your Friends (GWYF) pits you, and up to five of your degen friends, against violent loan sharks to whom you owe an immense debt.
We had a lot of fun playing this game, and it made us wonder: are these games fair? While playing we got the distinct feeling that some of the games seemed to favor the player, such as the slots and the ducks, while other games seemed unwinnable, like Crash. I was curious if the developers had juiced the odds one way or another. Fortunately, GWYF is a Unity game, and it does not ship itself AOT compiled. In simple terms, that means we can use reverse engineering tools like dnSpy to recover the game logic.
What this document is not intending to do is show you exploits. This post is only about statistics and odds. For example there is a known exploit for Craps by crouching in a specific location before throwing the dice. Likewise, it is possible to exploit Plinko by shoving baseball bats into the machine to form “ramps” for the balls to ride. I’m sure there are more exploits like this that are possible, but we are not interested in them here.
Caveats
I did all of this analysis based on game build 1.0.11. If the game has updated since then it is possible that the developers have retuned the games to change their RTP entirely. In my analysis I found that there is a single tunable “profitability” slider that the devs can easily change at a later date in response to player feedback. Take these results with a grain of salt.
Just like we are not covering game exploits, we are also not covering the use of items. There are a number of items that directly affect EV, by negating losses or increasing profit. These items are very interesting, and a key part of a successful strategy to actually beat the game. However, discussing them is out of scope for this already very-very-long blog post.
AI disclaimer
I used AI assistance to aid in reverse engineering the game and to write various helper scripts. The analysis itself, and the prose you are reading are human made, however many of the diagrams, charts, and tables in this post were constructed with AI assistance.
Ducks
Everyone loves the ducks so let’s start there. On first glance, this game appears to be fair. When the race starts, time starts ticking forward, and on every tick all of the ducks advance forward by a uniformly sampled random amount.
private IEnumerator DuckRaceRoutine(Random rng)
{
this.RpcSetRunningAnimation(true);
while (!this.duckRace.hasEnded)
{
float num = Mathf.Lerp(this.minStepDistance, this.maxStepDistance, (float)rng.NextDouble());
float targetZ = Mathf.Min(base.transform.localPosition.z + num, this.duckRace.endPoint.localPosition.z);
this.RpcStep(targetZ);
yield return new WaitForSeconds(this.stepTweenDuration);
if (Mathf.Approximately(targetZ, this.duckRace.endPoint.localPosition.z))
{
if (this.duckRace.OnDuckFinish(this))
{
this.RpcWinFeedback();
}
this.RpcSetRunningAnimation(false);
yield break;
}
float seconds = Mathf.Lerp(this.minStepDelay, this.maxStepDelay, (float)rng.NextDouble());
yield return new WaitForSeconds(seconds);
}
this.RpcSetRunningAnimation(false);
yield break;
}
At first glance this looks perfectly symmetric. Every duck is identical and independently seeded, so you would expect each one to cross first exactly one quarter of the time, paying a fair 4x:
\[E[\text{net}] = \tfrac{1}{4}(+3) + \tfrac{3}{4}(-1) = 0 \implies \text{RTP} = 100\%\]This is what the math tells us, but when we dig deeper we find a big flaw: It turns out that multiple ducks can cross the finish line on the same tick. When this happens, the winner is determined by which Duck coroutine (which is basically like a thread) fires first. It turns out that Unity schedules these coroutines in creation order. The coroutines are started in index order, meaning that the ducks on the left side of the board are more likely to win.
I have to come clean here, I did not figure out this bug on my own. I was assisted by AI, which suggested this was possible. I didn’t believe the AI, so I had it write a simple Unity program to simulate these dynamics as close as possible to what the game logic does. I simulated 4,000 duck races, so random variation should be minimal:
| Duck | Lane | Win % | RTP |
|---|---|---|---|
| Faruk | 1 | 33.7% | 135% |
| Erlaf | 2 | 27.4% | 110% |
| Yarl | 3 | 20.4% | 81% |
| Jabin | 4 | 18.6% | 74% |
Faruk (lane 1) wins nearly twice as often as Jabin (lane 4). The chi-square against a uniform 25% split is 231, where anything above 7.815 already rejects “all ducks equally likely” at the 95% confidence level. This is largely because the track they race on is relatively short. If they were on a longer track, ties would be less likely, and this effect becomes less relevant:
| Track (steps) | Faruk | Erlaf | Yarl | Jabin | Chi-square |
|---|---|---|---|---|---|
| ~21 (in game) | 33.7% | 27.4% | 20.4% | 18.6% | 231 |
| 80 | 30.2% | 26.2% | 23.0% | 20.6% | 84 |
| 160 | 28.4% | 26.2% | 24.1% | 21.3% | 45 |
With this bug, the RTP of betting on Faruk is an eye watering 135%. I suspect that the developers did not realize the implications of their tie breaking behavior. I hardly blame them, on first glance I also was ready to just write this off as “perfectly fair.” Of course the correct way to fix this problem would be to use a fair tie-breaker, instead of biasing to certain ducks.
One caveat of this result is that while I did simulate the races inside of a toy Unity game, I did not run this same experiment in the original game itself. It is possible that would change the results. Future work here would require writing a GWYF mod to run the experiments in the game directly.
Slots
In real life, slot machines are tuned to be very close to 100% RTP. Some machines advertise 99% RTP, for example. Since modern digital slots need less maintenance than their mechanical predecessors, and don’t require a dealer, these are the money printers for casinos. At 99% RTP most players will not be able to discern that they are losing money on net, even though they slowly are being drained.
What we had noticed while playing the game was that playing the slots felt like it was slightly positive EV. We had managed to rescue more than one run by spamming slot machines. Unlike the duck race above, this game is not very interesting from a technical perspective. On each cell of the 3x3 grid, there is a “roller” which can display one of four symbols. Looking at the game code, these rollers seem unbiased, so all of the rollers are equally likely. It basically does this:
def spinSlotMachine(playfield):
for roller in playfield:
roller.symbol = random.randint(1, 4)
You win when these rollers all contain the same symbol. The payout for various patterns is listed below, as extracted from game assets:
| Payline | Payout | Win Probability | EV |
|---|---|---|---|
| Top row | 2x | 6.25% | 0.125 |
| Middle row | 2x | 6.25% | 0.125 |
| Bottom row | 2x | 6.25% | 0.125 |
| Left column | 2x | 6.25% | 0.125 |
| Middle column | 2x | 6.25% | 0.125 |
| Right column | 2x | 6.25% | 0.125 |
| Diagonal \ | 2x | 6.25% | 0.125 |
| Diagonal / | 2x | 6.25% | 0.125 |
| Diamond | 5x | 1.5625% | 0.078 |
| X (corners + center) | 20x | 0.390625% | 0.078 |
| Full board | 59x | 0.001526% | 0.0009 |
Summing that column gives the base RTP:
\[E[\text{return}] = 8(0.125) + 0.078 + 0.078 + 0.0009 \approx \mathbf{1.157}\]Additionally, the win is scaled by which of the four symbols you matched with.
| Symbol | Index | Factor |
|---|---|---|
| Lotus | 0 | 1.75x |
| Beetle | 1 | 1.25x |
| Staff | 2 | 0.75x |
| Eye | 3 | 0.25x |
Since these scaling factors average to 1.0 our EV is still 1.157.
Craps
As we mentioned before, craps is exploitable but let’s focus instead on playing the game correctly. When you toss the dice to scramble it, that action is random, and not weighted. When you throw the dice, they become physics objects and are simulated using Unity’s physics engine. While we could in principle simulate these rolls, there is extra randomness introduced by player input since the starting velocity of the die is influenced by the players position and mouse cursor when they roll.
Instead, let’s just do some classic statistics here. The first, “come-out”, roll works like real craps: a 7 or 11 wins immediately paying out 2x. A 2, 3, or 12 loses immediately. Any other roll sets a target score, called a “point”, that you need to attempt to roll for. If you hit your point you get paid out 4x, but if you instead roll a 7 you lose. Where this game differs from traditional casino craps is that after 3 attempts to hit your point, you get your ante refunded. In a normal casino you lose at this point, and this difference is why the player has an edge in GWYF’s variant.
It is worth briefly reminding ourselves of the probabilities of the outcomes of rolling two dice:
Each “tower” of dice in this diagram represents all of the possible rolls of two dice, and what value they sum to. This makes it a bit more intuitive that 7 is the most likely dice roll, and why. From here, we just need to consider the game as a sort of flow chart of dice rolls.
In the graph below we show what the rules say you do in the case of every dice roll, and that nodes contribution to EV. To make it explicit, EV is just “probability x reward”.
This diagram makes the math much more intuitive to me, since to calculate the EV we need to just traverse this whole decision tree. We can then construct a simplified table like this to see the overall EV contribution of each node:
| Outcome | Probability | Multiplier | EV contribution |
|---|---|---|---|
| Come-out 7 / 11 | 22.22% | 2x | 0.4444 |
| Come-out 2 / 3 / 12 | 11.11% | 0x | 0.0000 |
| Point hit (within 3 rolls) | 17.15% | 4x | 0.6860 |
| Seven-out | 24.81% | 0x | 0.0000 |
| 3-roll refund | 24.71% | 1x | 0.2471 |
| Total | 100% | 1.3775 |
This result was not intuitive to my group, since we felt like we were always losing at Craps, but the overall EV of 1.3775 is one of the best player edges in the casino.
Prize wheels
There are three wheel based games in the casino, that are fundamentally the same game.
Wheel of Fortune
The simplest of the three, this wheel has 20 segments that are equally likely. What is interesting is that the “spin” of the wheel is just for show. The result of the spin is predetermined, and then the wheel is simply animated towards the predetermined result:
public virtual void SpinTheWheel(Random rng)
{
if (this._isSpinning) return;
this._isSpinning = true;
// 1. Pick the resting position FIRST: a uniform angle in [0, 360).
float angle = (float)(rng.NextDouble() * 360.0);
// Add a few whole turns purely for show, so it looks like a real spin.
float finalAngle = this.minTurnAmount * 360f + angle;
if (this.spinDirection) finalAngle *= -1f;
// 2. Animate toward that predetermined angle (DOTween, fixed easing).
this.RpcSpinWheel(finalAngle, this.spinDuration);
base.StartCoroutine(this.WaitAndStop());
}
Since the segments are all equal width, they are therefore equally likely. By adding up the multipliers on the wheel segments we can straightforwardly get the EV:
| Payout | Wedges | Probability | EV contribution |
|---|---|---|---|
| 5x | 1 | 5% | 0.250 |
| 3x | 2 | 10% | 0.300 |
| 2x | 2 | 10% | 0.200 |
| 0.5x | 3 | 15% | 0.075 |
| 0.25x | 4 | 20% | 0.050 |
| 0.1x | 5 | 25% | 0.025 |
| 0x | 1 | 5% | 0.000 |
| Spin Again | 2 | 10% | 0.100 |
| Total | 20 | 100% | 1.000 |
The only subtle row is Spin Again. Since they spin the wheel for us a second time, it is equivalent to refunding us and us playing again, so we can count it as the same as a 1x payout. Summing the EV contributions we find that means this game has an EV of 1.0, making it perfectly fair.
Money Wheel
The Money Wheel is governed by the same logic as the Wheel of Fortune, in that it also predetermines the result and then drives the animation towards it. What makes this game different is that it asks you to predict the outcome of the spin, Green, Blue, Red, or Orange. These colors payout at different rates, 2x, 3x, 5x, and 10x respectively. You may be tempted to think that surely this game will also be fair, but it emphatically is not.
Adding up the number of squares per color we get this:
| Color | Segments | Share of wheel | Payout | RTP (share × payout) |
|---|---|---|---|---|
| Green | 14 | 14/37 = 37.84% | 2x | 75.68% |
| Blue | 10 | 10/37 = 27.03% | 3x | 81.08% |
| Red | 8 | 8/37 = 21.62% | 5x | 108.11% |
| Orange | 5 | 5/37 = 13.51% | 10x | 135.14% |
If you are playing this game, you should ONLY play Red or Orange bets, never Green or Blue.
Roulette
Roulette is a textbook casino-style roulette wheel, with a single green square. This game is very similar to the other two wheel games, and as such the ball landing on a square is simply animated towards a predetermined target. For this game we don’t even need to do our own math, Wikipedia has done it for us. Regardless of your bet or betting patterns your RTP will be 97.30%, just like at a real casino.
The Martingales
Hi-Lo, Penguins, Minesweeper, Dragon Tower, Keno, and Crash look like six different games, but they are really the same game mathematically. Each one secretly picks a survival probability \(P\), lets you climb for a rising multiplier, and pays out exactly \(1/P\) if you make it to where you stop. In short, the more risk you take on, the higher the payout, but weighted such that the EV is always 1.
\[E[\text{return}] = P \times \frac{1}{P} = 1 \implies \text{RTP} = 100\%\]I think to make this more intuitive, it’s helpful to start with Hi-Lo. If you set the slider in Hi-Lo to the 50% position, your odds of winning are exactly 50% regardless of if you pick “Hi” or “Low”. Because your odds are 50% or \(1/2\) the game pays out the reciprocal of your odds, which is 2x. Likewise, if you set the slider to the 90% position and bet “Hi” your odds of winning are \(1/10\), so the game will payout 10x if you win.
// HiLoGame.cs -- roll is uniform on [0, 1); hiLoSlider.currentValue is the threshold t
float roll = (float)base.GetSeededRandom(0).NextDouble();
bool win = this._isOver
? roll >= this.hiLoSlider.currentValue // "Hi": win when the roll clears t
: roll <= this.hiLoSlider.currentValue; // "Low": win when the roll is under t
// P(win) is exactly the slider position, and the multiplier is E / P(win)
double num = this._isOver
? (1.0 - (double)this.hiLoSlider.currentValue) // P(win) for "Hi"
: ((double)this.hiLoSlider.currentValue); // P(win) for "Low"
double multiplier = 1 / num;
The other five games follow the same pattern. For example in Minesweeper your payout is calculated based on the number of mines you added to the board, and the number of tiles you have revealed so far. The math looks like this:
\[P(\text{survive } r) = \prod_{i=0}^{r-1} \frac{N - m - i}{N - i}\]Where \(N\) is the total tiles, \(m\) is the number of mines, and \(r\) is the number of tiles safely revealed.
// Minesweeper.cs
private double CalculateCurrentMultiplier()
{
if (this._revealedTiles.Count == 0) return 1.0;
int count = this.tiles.Count; // N: total tiles (25)
double num = 1.0;
int num2 = count - this._currentMineCount; // S: safe tiles = N - m
for (int i = 0; i < this._revealedTiles.Count; i++)
{
// (S - i)/(N - i) is P(the i-th reveal is safe), sampling without replacement
double num3 = (double)(num2 - i) / (double)(count - i);
num *= 1.0 / num3; // accumulate 1 / P(survive so far)
}
return num;
}
The payout that the game gives you for winning is therefore \(1/P(\text{survive})\) just like Hi-Lo. It turns out that games of this flavor are called “martingales”. I won’t pretend to understand all of the math here, but the optional stopping theorem proves that for games like this there is no cash-out strategy that improves your expected value. I won’t bore you with going through these games one by one, but they all have this flavor to them, and are tuned to be exactly fair in the code with one notable exception.
Crash
Crash is the only one of these martingale games that is not tuned to be fair. From the game code we see the following:
Random seededRandom = base.GetSeededRandom(0);
float num = (float)seededRandom.NextDouble();
float crashPoint = 1.01f;
if (num > this.instantCrashChance)
{
crashPoint = this.GetRandomCrashPoint((float)seededRandom.NextDouble());
}
...
private float GetRandomCrashPoint(float r)
{
r = Mathf.Max(r, 0.001f);
return Mathf.Clamp(1f / r, 1.001f, this.maxPoint);
}
Note the variable “instantCrashChance.” Unlike the other games, Crash has a tunable parameter for how likely the player should be to lose instantly. “instantCrashChance” is set to 15% in the shipping version of the game. I presume this is to tune down the likelihood of the potentially enormous wins that are possible here. It’s hard to say exactly why this was done, but nonetheless, this caps the EV for this game at 0.85.
Physics-driven games
Two of the games in the casino are actually driven by the Unity physics engine. While we can reason about them analytically, that analysis might not match the real game logic for a number of reasons.
Coin Flip
Let’s start with the very first game you are presented with when you start the game: the Coin Flip. If you aren’t familiar with GWYF, if you win the coinflip you are allowed to play the game. If you fail, the game exits and you have to try again. It’s a good teaser for the vibe of the rest of the game.
In the Unity scene that drives the coin flip, the coin starts face down. The game then applies a random upward force, and a torque along a random axis to the coin. The coin is simulated with the default Unity physics engine, but the physics constants have been tuned for a more dramatic looking coin flip. For example, the rest of the game uses 15 m/s² of gravity, but the coin only experiences 2 m/s² of gravity.
Now every middle schooler can tell you that a coin flip is roughly 50-50, but even in real life it is possible for coins to not be fair. To this end, we will need to do the same trick we did with the duck game and simulate a couple thousand runs by recreating the game code as faithfully as we can in our own Unity scene.
| Flips | Win | Lose | P(win) | RTP |
|---|---|---|---|---|
| 10,000 | 4,995 | 5,005 | 0.4995 | 99.9% |
This is a pretty good result, and shows that this coin is damn near fair. One fun caveat of these results is that “tails” and “heads” are not the only possibilities. It turns out that the coin is being flipped inside an invisible “cup” to prevent it from flying off screen. In some rare circumstances, it is possible for the coin to come to rest leaning on the walls of this cup. Likewise, under extreme circumstances it is possible for the coin to land precisely on its edge. These quirks don’t influence the outcome of the experiments much, they are just fun.
Plinko
Plinko is an interesting case, because on first glance it might appear impossible to model the probability directly. However, the Plinko machine is a straightforward application of the binomial distribution. In a simplified view of the Plinko machine, imagine that at each vertical level of the board the ball asks a question “should I go right, or should I go left?” and flips a coin to determine its path. Assuming that the coin is fair, you should see probabilities like this:
This type of model is also called a Galton board. Notice on the third row that the probabilities for each node goes, from left to right, 25%, 50%, 25%. That is because there are 2 paths to reach the middle node, but there is only one path to reach the left and right nodes. This simple idea, iterated out for all 11 rows, gets us our expected probabilities for each of the bottom nodes. Now reminding ourselves of the \(EV = probability * reward\) math we can use this table to compute the “theoretical” EV of this game.
| Bin | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Mult | 24x | 6x | 2.8x | 1.2x | 0.5x | 0.2x | 0.2x | 0.5x | 1.2x | 2.8x | 6x | 24x |
| Probability | 0.05% | 0.54% | 2.69% | 8.06% | 16.11% | 22.56% | 22.56% | 16.11% | 8.06% | 2.69% | 0.54% | 0.05% |
| EV | 0.012 | 0.032 | 0.075 | 0.097 | 0.081 | 0.045 | 0.045 | 0.081 | 0.097 | 0.075 | 0.032 | 0.012 |
You can also compute this directly with the binomial distribution formula like such, where \(m_k\) is the payout multiplier for bucket \(k\):
\[\text{EV} = \sum_{k=0}^{11}\frac{\binom{11}{k}}{2^{11}}\,m_k = \frac{1398.8}{2048} = 0.683\]The naive model says 68.3% RTP which is a good starting point, but is ultimately wrong. The balls in this game do not behave like an idealized Galton board. For one they are bouncy, which the idealized balls are not. Also, the game code has some tunings that manually changes the speed of any ball after a collision, presumably just for visual appeal. To really make sure that we capture the real odds, we need to rebuild the in-game Plinko board in Unity and simulate it. I ran 100,000 balls through the simulated board and got these results:
| Bin mult | 24x | 6x | 2.8x | 1.2x | 0.5x | 0.2x | 0.2x | 0.5x | 1.2x | 2.8x | 6x | 24x |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Measured P | 0.59% | 0.97% | 1.53% | 2.94% | 8.82% | 34.44% | 34.70% | 8.99% | 2.98% | 1.43% | 0.92% | 0.62% |
The twelve pockets account for 98.92% of drops. The remaining 1.08% are balls that got stuck on one of the pegs. If the ball gets stuck, the game has a 20s timeout, after which the ball is deleted and the player loses their ante. Doing the same math we did above, we get an EV of 0.78, which is 10 points higher than our theoretical guess. Looking at a histogram of where the balls land, this becomes a bit more obvious:
Even though the center 0.2x bins are way more likely than the math predicted, which should drag our EV down, the 24x buckets on the edges are 10 times more likely than we predicted. These edge buckets account for almost the entire EV improvement above the theoretical model.
Card games
The card-based games (Blackjack, Video Poker, Baccarat) all deal cards from a single shuffled deck. The deck is not shuffled in-between rounds until the deck is empty. This means card counting is trivial. I also think that card counting is way too high effort for most players. With that said, we will be analyzing these games without counting for simplicity.
Blackjack
Blackjack in this casino bends the rules in the player’s favor. Blackjack pays 2:1 instead of the usual 3:2, and a player natural beats a dealer natural instead of pushing. Also unlike many casinos you cannot split. The game actually has splitting implemented in the code, but there is no button for it on the Blackjack table. Presumably they intended on adding this, but removed it late during development for some reason. Due to these rule changes, the normal house edge of ~2% is in question. (NB: that is the rough house edge for tables with 6 decks in shoe, like most real casinos)
| Result | Return | Net |
|---|---|---|
| Player blackjack | 3x | +2 |
| Player win | 2x | +1 |
| Push | 1x (refund) | 0 |
| Player lose | 0x | -1 |
Computing the exact RTP
To determine both the house edge and the optimal play charts, we used Eric Farmer’s blackjack analyzer, an engine that computes the optimal blackjack play for any given rule set.
We ran his code two ways: once with the GWYF ruleset and once with the same rules, but with the normal 3:2 payout scheme that casinos normally use as a smoketest.
| Rules | Blackjack payoff | Overall EV | RTP |
|---|---|---|---|
| Single deck, S17, double any two, no split, no surrender | 3:2 | -0.333% | 99.667% |
| Single deck, S17, double any two, no split, no surrender | 2:1 | +1.991% | 101.991% |
So EV is 1.02 which means we are just barely positive under perfect play.
One quirk of the game falls outside what the engine can express: the dealer never peeks for blackjack. This rule shouldn’t have a big influence on EV, so we are choosing to ignore it here.
Optimal strategy
The analyzer also outputs the optimal strategy chart given our ruleset. This chart is very similar to the canonical chart, so no big surprises here.
To use the chart, find the row that matches your hand and read across to the column for the dealer’s card. The cell where they meet is the action you should take. Hard totals (no ace, or an ace that can only count as 1) are the “H” rows, and soft totals that contain a flexible ace are the “A,” rows. Check the soft rows first. If you hold an ace counted as 11, use the matching soft row; otherwise fall back to the hard total.
Video Poker
Like Blackjack, Poker is also very different from most casino incarnations of the game. In this game the Ace is always low, and pairs pay. Normal video poker machines will require face card pairs to pay out.
I’m going to be honest: for this game I surrendered entirely to our AI overlords. All of the other analysis was driven by me, with AI doing the boring stuff. In this case I could not find a ready-made tool to simulate the odds of this game, since it uses a custom rule set. “No worries”, said the LLM du jour, “I can just spit that out for you”. It generated some of the densest C++ code I have ever seen. I tried to understand it, and failed. To that end, take these results with a grain of salt: the poker solver that the AI wrote says that with perfect play this game has an EV of 1.28.
The solver enumerated all 2,598,960 possible five-card deals. Scan your dealt hand from the top of the table down and take the first action that matches what you are holding. The “avg return” column is the average payout (in multiples of your bet) you can expect from playing that situation optimally.
| Dealt hand | Frequency | Best action | Avg return |
|---|---|---|---|
| Straight flush | 0.0014% | Keep all five | 50.00x |
| Four of a kind | 0.0240% | Hold the four, draw 1 | 25.00x |
| Full house | 0.1441% | Keep all five | 9.00x |
| Flush | 0.1967% | Keep all five | 6.00x |
| Three of a kind | 2.1128% | Hold the three, draw 2 | 4.30x |
| Straight | 0.3532% | Keep all five | 4.00x |
| Two pair | 4.7539% | Hold both pairs, draw 1 | 2.60x |
| One pair | 42.26% | Hold the pair, draw 3 | 1.54x |
| Four to a flush (no pair) | 2.95% | Hold the four suited, draw 1 | 1.47x |
| Four to a straight (no pair) | 9.20% | Hold any 2 suited, else a high card, draw 3 | 0.75x |
| Nothing | 38.01% | Hold one high card (or 2-3 suited), draw the rest | 0.68x |
Baccarat
Baccarat in GWYF is a stripped-down version of the game: two cards are dealt to each side, the totals are compared mod 10, and the higher one wins. Because a single deck only has 1,624,350 possible two-card-each deals, we can just enumerate all of them and tally the results.
\[\binom{52}{2}\binom{50}{2} = 1{,}624{,}350\]| Outcome | Probability | Bet payout | EV |
|---|---|---|---|
| Player wins | 45.05% | 2x | 0.901 |
| Banker wins | 45.05% | 2x | 0.901 |
| Tie | 9.90% | 8x | 0.792 |
The Player and Banker win counts are identical, so the house edge on those bets is from ties pushing to a loss. If ties were refunded, this game would be perfectly fair.
Conclusion
Thanks for reading this far! Not much else to say other than go buy Gamble With Your Friends if you haven’t tried it yet. It’s great fun :)
Corrections
- 2026-06-03: The duck names in the Ducks section were originally listed in reverse order, influencing the recommendation of which duck to bet on.