Skip to content

Extracting holdem game logic into separate package #20

@santiweight

Description

@santiweight

We've talked in the past (dunno if you remember - 3 years ago maybe?).

Summary

Thanks for the great project :) Incoming wall of text, but I hope it is all to the point. I would like to migrate poker-maison to use a shared library for core poker type declarations (in src/Poker.Types) and for holdem game logic (src/Poker.Game.*). In general there is no need for each programmer to reimplement poker types and holdem (or any poker game) logic, and so if we get one solid and collaborated-upon set of libraries going, we'll have a better haskell-poker ecosystem! :)

My goal for these libraries is to be a good example of Haskell code. Nothing too fancy, but also nothing too simple, using the most common haskell features such as lens and mtl, so that an intermediate haskeller would feel comfortable and a beginner could come in without being overwhelmed.

I have already implemented the lion's share of the library, so I am here to check if you would be okay with migrating poker-maison to use my code. I have tested poker-maison with some of the migration, and it all looks good as far as I can tell.

What it would look like for poker-maison

poker-maison would use the new library for stuff like:

  • core poker types
  • next available actions for main street actions (preflop thru river)
  • stack sizes
  • basic poker types

Some things are not yet implemented not yet support (but would be added at a later date, likely by using your logic, if that's okay with you):

  • blind posting
  • showdown

Some notable differences:

  • I, as a rule, don't override Show or Read instances, since the libraries are aimed for all to use. Instead I implement pretty-simple's Pretty class, which I see you already use anyway.
  • I don't override Eq/Ord anywhere, since these instances can be used sometimes for Map and occasionally for serialisation. This only caused one minor issue with migrating poker-maison (see Poker.Game.Hands.sortByLength), which is easily fixed.
  • My goal is to have hand phase separation, so the game state type will be unique for each of blind-posting, post-card-deal (preflop thru river), and showdown (3 game state types in total).
  • My library is polymorphic in the bet type, but this would hidden from your code almost completely.
  • My library code uses State monad lens combinators extensively, but no other fanciness, and isn't too long (around 1000 lines excluding tests).
  • My test coverage is not yet a superset of your tests. Before merging, I would translate all your tests into my codebase (I already started the process locally). My code is tested and was tested by running around 10,000ish hands from my Bovada database through my holdem engine. The tests test both that all actions in the DB were accepted by my holdem engine, and that test-runner-generated invalid actions returned an error for all acts for all hands. I will also be augmenting these tests with a 1,000,000 hand database (including heads-up hands) from PokerStars once I have a parser for PokerStars hand histories.
  • I don't account for table actions such as Timeouts and LeaveSeats, which would be translated to my "BetAction", which are the in-a-vacuum available actions to a player. For example, your "Action" type might look something like the following:
data Action
  = SitDown Player -- doesnt progress the game
  | LeaveSeat' -- doesnt progress the game
  | PostBlind Blind
  | ShowHand
  | MuckHand
  | SitOut
  | SitIn
  | Timeout
  | FromBetAction BetAction

The migration process

I would be happy to do this migration all myself, and do so incrementally to make it more reviewable for you! I don't expect the patches to be too involved/arduous.

If you're open to the process, the only thing that I would ask from you would be whether I could make changes to the build system for poker-maison. In particular I was having trouble with having a good IDE setup, likely because of the following github issue haskell/haskell-language-server#1822. Would you be okay with a cabal based build instead of stack? The trade off would be slightly more verbose builds (you have to add each file to the .cabal file) which is definitely annoying, but as a reward we don't get an infuriating bug that means you have to persistently restart the IDE each time you edit library code while working on a test.

The process would overall look like this:

  • integrate with poker-base, which is here. This code will definitely experience more cleanups, and I'd like to get to 90+% test coverage (up from ~70%)
  • integrate with poker-game which lives here. This would be the harder part, but I can show in a PR maybe this weekend?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions