Show HN: Allocate poker chips optimally with mixed-integer nonlinear programming (github.com)
Try it out here (the submitted link goes to the GitHub repo): https://jstrieb.github.io/poker-chipper/
It turns out that picking chip denominations optimally—such that as many chips are distributed as possible, and such that the denominations are nice—is hard (in the computational complexity sense). Upon reflection, the problem seemed to be a perfect fit for constrained optimization.
I first got a CLI prototype working with Z3 (an SMT solver with optimization capabilities https://github.com/Z3Prover/z3) in Python. Then, I cross-compiled SCIP (https://www.scipopt.org/) to web assembly, and ported my code to use SCIP instead of Z3 so it could run in the browser.
The web interface is designed to be fast and easy to use on desktop and mobile.
I would love to answer questions and discuss design choices. I'm also open to feedback and bug reports. Thanks for taking a look!
74 comments
[ 1901 ms ] story [ 2980 ms ] threadThe denominations are $1, $5, $25, $100, or $5, $25, $100, $500, or $5, $25, $100, $1000.
In Vegas, the $5s are almost always red, the $25s are almost always green, and the $100s are almost always black.
Even the $10 chips in use for the 5/10 games at the Wynn and Resorts World throw off a lot of players.
Casinos frequently have $2 chips simply because it makes dropping the rake take less volume and allows them to change the boxes less frequently. You don’t need them in a home game.
This might be an interesting math problem, but let’s not pretend that it has any applications in actual poker games.
It’s just not done. It also makes no sense: it’s confusing and slows down the game for no reason.
It’s a fun mental exercise and programming problem, but that’s it IMO.
makes sense to me
This is something that most amateurs want, it's OK if pros don't want it.
I play with low stakes amateurs all of the time, as often as not in home games, and the smallest games people play are 1/1 or 1/2. You can’t even buy a cheeseburger in America for $1 anymore, anything less than that and you might as well not be playing for money at all; just use points and don’t worry about denominations.
If you’re not playing for stakes where you care if you win or lose, then just don’t play for money at all.
I know lots of hobby poker players, firmly nonprofessionals, who play in non-serious 2/5 and 5/5 home games where the buyin is $500-3000.
Penny poker is totally pointless for anyone not below the literal/actual poverty line where $40 is actually lifechanging money. If you’re not homeless and want to play $0.25/$0.50, just play for points/chips then instead of cosplaying gambling.
Time You Enjoy Wasting Is Not Wasted Time.
https://quoteinvestigator.com/2010/06/11/time-you-enjoy/
> don’t pretend it’s a “friendly game”, it’s just a distraction, not a game.
A bit of friendly competition is still a game. Games can be fun.
> If you’re not playing for stakes where you care if you win or lose, then just don’t play for money at all.
You can still care you win, but not mind if you lose. That’s a sweet spot. Even a small pot (which is still a multiplier of all players) can get people engaged just that extra bit to take it a bit more seriously. That’s more enjoyable for everyone: you get more investment and interesting plays. The winner gets a some pocket money but no one else is bummed by the loss, it becomes a pittance relative to the enjoyment of playing with friends.
> just play for points/chips then instead of cosplaying gambling.
Why does this make you (sound/read) so irate? Let people enjoy themselves, no one is being harmed.
I'm really glad for you to know and hopefully be able to make a living off of non-professionals that can afford to lose several thousand dollars in one evening of non-serious playing. That's a great clientèle to have as a professional gambler, and I see nothing wrong with taking advantage of it. But I'll go out on a limb and say it's probably far from the lived experience of most people, and neither should it be.
Obviously for poker to make sense you need to have a minimum of skin in the game. Which is exactly why you basically telling people here to rather go and play with peas comes across as pretty tone deaf, not to say entitled.
You need to understand that you are projecting your standards on people who have no aspirations to play professionally.
I've seen $2, $2.50, $3, $10 and $20 chips as well as the normal denominations. This site mentions $8s:
https://wizardofvegas.com/forum/gambling/poker/5999-2-3-and-...
Few of the above needs apply to casual unraked homes game, unless you are playing limit as noted. (I’ve never encountered a limit home game; more and more these are strictly hold ’em or PLO/PLO8, unless the players are over 70. Once in a while if it gets really late and shorthanded someone might suggest a round of stud, I guess)
And even then you need larger numbers of fewer types of chips for limit, as a rule.
I feel like 1,3,9,27,81 could be an optimal set; but I am not sure how to prove it.
So if the list of primes is 2, 3, 5, 7, 11, etc, you could have weights of 5, 25, 35, 55, 75, 11*5, etc. for 5, 10, 15, 25, 35, 55, etc.
Fixed the formatting for you. You need to put a \ before * symbols to have them reliably render in HN
Alternatively, write two sequential *, like so: **
If you want to get within x% using at most k weights the some kind of logarithmic distribution is also a good bet.
Both you and OP could benefit from being a bit clearer about what you find optimal.
https://news.ycombinator.com/item?id=40576198
What This Country Needs is an 18¢ Piece (2002) [pdf] - https://news.ycombinator.com/item?id=38665334 - Dec 2023 (272 comments)
What This Country Needs Is an 18¢ Piece [pdf] - https://news.ycombinator.com/item?id=14579635 - June 2017 (45 comments)
Could you expand on what the optimisation criteria are? you want each person to have the maximum number of chips for some set of "nice" denominations where the total number of chips and players are constrained?
What does a "nice" set of denominations mean in this case? Why do you want to distribute a maximal number of chips?
https://github.com/jstrieb/poker-chipper/blob/fab41bbe8821d0...
Something like a dive-table planner (which is actually really cool if you know how to read it!):
https://www.baliocean.com/blog/what-is-a-dive-table/
http://www.scubadiverinfo.com/images/Dive_tables_NAUI.jpg
It is indeed nice and easy to read the constraints. Nice work!
This is amazing. I wanted to do something similar for a web-based optimization. I ended up just putting a C++ Lambda up to serve the requests.
I wonder how the performance compares? I feel like optimization frameworks make use of a lot of CPU-based heuristics.
Specifically, I haven't been able to find a reasonable set of parameters that cause the optimization to take longer than ~20 seconds on the slowest device I tried.
To get things to be smooth, though, I had to do debouncing and also run optimization in a web worker (separate, non-blocking thread) so the UI doesn't hang. Doing that was its own challenge since web workers don't use the HTTP cache for security reasons (they're supposed to be an isolated context), and it was re-requesting the ~5MB WASM files every time any of the numbers was adjusted. To solve this I used a service worker with a local cache.
Slow performance of web-based Z3 for optimization was one of the main reasons I switched to SCIP. I originally had a version working using a WASM port of Z3, but it just wasn't fast enough to be usable.
Did you do some local-vs-phone tests? I'm really curious how 20s on android compares to laptop.
If you end up wanting to use the version of SCIP compiled to WASM, I have it pre-compiled in the repo here: https://github.com/jstrieb/poker-chipper/tree/fab41bbe8821d0...
To interact with WASM SCIP, I use the CLI with an Emscripten virtual filesystem (as opposed to the C API/FFI): https://github.com/jstrieb/poker-chipper/blob/fab41bbe8821d0...
If you want to compile it yourself, the code and compiler flags I used to do that are documented in a Dockerfile in the repo: https://github.com/jstrieb/poker-chipper/blob/fab41bbe8821d0...
FWIW, I belong to a forum comprised mostly of game hosts who collect playable sets for their games. This group has gotten optimal breakdowns for various games (from 5¢/10¢ up to nosebleed stakes) down to a science, through actual hosting experience. The collective number of hours hosted by these members is astronomical.
A standard notion is that you rarely need more than 4-6 denoms for most games, with really only 3 of those in any quantity of 100 or more for a one-table cash game.
Also, that the jump between chip denominations functionally should be 4-5x the next lowest denom.
So for example, a typical cash set meant to work for games ranging from 50¢/$1, $1/$2, and $2/$5 could make do with denoms of 50/1/5/25/100.
For any given stakes, there is a “workhorse” denomination, and that’s the chip type you need the most of (e.g. $5 chips on 1/2 games).
The only real divide among this group is those who like to use the fewest possible chips which is still comfortable, without change having to be made too often, vs. those who think poker is more fun with lots and lots of chips on the table, even if many are unnecessary.
The idea of assigning non-denoms (i.e. chips with no value printed on them) all sorts of unstandard but “optimal” amounts is I suppose an interesting intellectual exercise, but in practice seems nuts.
… Especially considering that chips are typically sold with existing amounts printed on them. To host with non-standard denoms requires undenominated chips used with some sort of printed or displayed key to remind players of the weird values.
Oh, overthinking can be fun!
One fix would be changing the minimum number of chips per color under "advanced options > requirements" to 0.
If you have a set with 100x of white, blue, red, green, etc... your total number of chips used for 8 players is: 96x white (25c) 96x blue ($1) 8x red ($5)
If players lose all of their chips... they can re-buy with $5 (red) chips and make change from the players that have all of the lower denomination chips.
What I have proposed above is a proper solution if you want to play poker.
Ideally you want to use common casino chip colors (though these are somewhat fungible) and just shift orders of magnitudes.
If 1BI is 20 bucks, you can make it equivalent to a $200 BI at the casino, which really only uses 2 colors 95% of the time: whites for 1 dollar, reds for $5. Deeper games might need a $25 (green) or even $100 (black chip). So whites in your home game are 10c, reds are 50c, greens are $2.50, blacks $10.
For a $1/$2 cash game, I'm going to ALWAYS use:
And that's it. Since again, standard chip racks hold stacks of 20 chips, these can be set up in seconds. Later in the game, we can break out the $25 chips.Also +1 to using "standard" casino chip colors. In the US, $1 is almost always white, $5 is almost always red, $25 is almost always green, $100 is almost always black, and so on. Don't confuse people.
Buy your chip set based on how you allocate your chips when playing, don't allocate your chips based on whatever chip set you happen to have.
Other, less important usability consideration is that you would typically want "round" amounts of chips, ideally stacks of 10 or 20, though having a few big chips in odd amounts is fine. Not as big a deal, but again, very much diverging from typical poker ux expectations.
Neat project technically, but highly impractical for actually playing poker.
edit: to be fair, just saw the advanced options, which would allow it to produce a more useful result, so that's cool. maybe just update the defaults :)
At first glance, seems like you could do this with linear programming instead of mixed-integer if you're ok shaving off the fractions at the end and handing them out naively. Nobody will mind getting 1-2 more chips than theoretically necessary.
You're almost certainly totally right for this problem, but in general that's one of the pitfalls in beginner mixed-integer programming implementations. Rounding a reasonable real-number solution has almost no guarantees in terms of approximate optimality or error bounds compared to the optimal integer solution.
It seems to me that the underlying problem is that cheap pre-mixed chip cases usually have terrible denominations (if printed) and/or too few of each (sometimes only 50), which just doesn't make any sense. Nobody ever needs denominations that differ by a factor of 2, and you need way more than 50 of any one type to play with more than 6 people.
For our 0.05/0.1 10$ cash games with 8-10 people, one day about 15 years ago we just went and filled a 500 chip case with nice ceramics (Old Havana Poker Club, they're still around): 150x 5 200x 25 120x 100 30x 500 (just for good measure)
Obviously we take the numbers printed on the chips to be cents. This system has worked perfectly ever since and still offers sufficient flexibility for the occasional tournament; I can only recommend solving the problem this way once and for all. (Of course you need to adjust for the kind of game you play.)
Covers all of our use cases. We usually do a 20 dollar buyin and do 12 .25, 12 1.00, and 1 5.00
Works well and we eventually move rebuys to 4 5.00, with players making change off of someone with lots of chips.
Sometimes we flood the game with as many .25 as possible because it's fun to have a massive chip stack, but pain to count at the end.
This has worked well for us in games up to 14 people (2 tables)
Worth the initial investment in the chips, especially because they are thonky ceramic chips which just feel _so nice_
Once the optimization has happened, you can edit the colors, and the order will be preserved. So if you want to swap it so that white is the lowest valued chip, just edit the color of the lowest valued chip to white.
I also did some constraint programming to solve my poker problems. We play mostly cash games so I did a MiniZinc model for computing the least amount of transactions after the game: https://github.com/SRautila/poker-home-game-calculator