{"id":37744768,"url":"https://github.com/fschlatt/clubs","last_synced_at":"2026-01-16T14:14:20.699Z","repository":{"id":45950492,"uuid":"248792358","full_name":"fschlatt/clubs","owner":"fschlatt","description":"python poker engine for arbitrary community card poker games","archived":false,"fork":false,"pushed_at":"2024-02-06T07:49:07.000Z","size":447,"stargazers_count":19,"open_issues_count":1,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-05T16:13:15.990Z","etag":null,"topics":["blinds","clubs","community-cards","machine-learning","poker","poker-game","python","raise-sizes","reinforcement-learning"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fschlatt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-03-20T15:41:44.000Z","updated_at":"2025-11-16T15:44:27.000Z","dependencies_parsed_at":"2022-09-05T16:00:41.554Z","dependency_job_id":null,"html_url":"https://github.com/fschlatt/clubs","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/fschlatt/clubs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschlatt%2Fclubs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschlatt%2Fclubs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschlatt%2Fclubs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschlatt%2Fclubs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fschlatt","download_url":"https://codeload.github.com/fschlatt/clubs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschlatt%2Fclubs/sbom","scorecard":{"id":412618,"data":{"date":"2025-08-11","repo":{"name":"github.com/fschlatt/clubs","commit":"c8ce301d68c5917bc9ba31fdbc429948bae8a608"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pythonpackage.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/fschlatt/clubs/pythonpackage.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pythonpackage.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/fschlatt/clubs/pythonpackage.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pythonpackage.yml:46: update your workflow using https://app.stepsecurity.io/secureworkflow/fschlatt/clubs/pythonpackage.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pythonpackage.yml:48: update your workflow using https://app.stepsecurity.io/secureworkflow/fschlatt/clubs/pythonpackage.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/pythonpackage.yml:61: update your workflow using https://app.stepsecurity.io/secureworkflow/fschlatt/clubs/pythonpackage.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pythonpublish.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/fschlatt/clubs/pythonpublish.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pythonpublish.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/fschlatt/clubs/pythonpublish.yml/main?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/pythonpackage.yml:28","Warn: pipCommand not pinned by hash: .github/workflows/pythonpackage.yml:29","Warn: pipCommand not pinned by hash: .github/workflows/pythonpackage.yml:30","Warn: pipCommand not pinned by hash: .github/workflows/pythonpackage.yml:31","Warn: pipCommand not pinned by hash: .github/workflows/pythonpackage.yml:53","Warn: pipCommand not pinned by hash: .github/workflows/pythonpackage.yml:54","Warn: pipCommand not pinned by hash: .github/workflows/pythonpackage.yml:55","Warn: pipCommand not pinned by hash: .github/workflows/pythonpackage.yml:56","Warn: pipCommand not pinned by hash: .github/workflows/pythonpublish.yml:22","Warn: pipCommand not pinned by hash: .github/workflows/pythonpublish.yml:23","Warn: pipCommand not pinned by hash: .github/workflows/pythonpublish.yml:24","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of  11 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/pythonpackage.yml:1","Warn: no topLevel permission defined: .github/workflows/pythonpublish.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: GNU General Public License v3.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":4,"reason":"6 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2023-62 / GHSA-m2qf-hxjv-5gpq","Warn: Project is vulnerable to: PYSEC-2023-177 / GHSA-x7m3-jprg-wc5g","Warn: Project is vulnerable to: PYSEC-2021-856 / GHSA-5545-2q6w-2gh6","Warn: Project is vulnerable to: GHSA-6p56-wp2h-9hxr","Warn: Project is vulnerable to: PYSEC-2021-857 / GHSA-f7c7-j99h-c22f","Warn: Project is vulnerable to: GHSA-fpfv-jqm9-f5jm"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-18T23:06:14.125Z","repository_id":45950492,"created_at":"2025-08-18T23:06:14.125Z","updated_at":"2025-08-18T23:06:14.125Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28479277,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T11:59:17.896Z","status":"ssl_error","status_checked_at":"2026-01-16T11:55:55.838Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blinds","clubs","community-cards","machine-learning","poker","poker-game","python","raise-sizes","reinforcement-learning"],"created_at":"2026-01-16T14:14:19.986Z","updated_at":"2026-01-16T14:14:20.693Z","avatar_url":"https://github.com/fschlatt.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"./clubs/render/resources/static/images/black_red_logo.svg\" alt=\"Logo\" width=200px\u003e\n\n\u003c/div\u003e\n\n# clubs\n\n[![PyPI Status](https://badge.fury.io/py/clubs.svg)](https://badge.fury.io/py/clubs)\n[![PyPI Status](https://pepy.tech/badge/clubs)](https://pepy.tech/project/clubs)\n[![codecov](https://codecov.io/gh/fschlatt/clubs/branch/main/graph/badge.svg)](https://codecov.io/gh/fschlatt/clubs)\n[![CodeFactor](https://www.codefactor.io/repository/github/fschlatt/clubs/badge)](https://www.codefactor.io/repository/github/fschlatt/clubs)\n[![Documentation Status](https://readthedocs.org/projects/clubs/badge/?version=latest)](https://clubs.readthedocs.io/en/latest/?badge=latest)\n\nclubs is a python library for running arbitrary configurations of community card poker games. This includes anything from simple Leduc or [Kuhn](https://en.wikipedia.org/wiki/Kuhn_poker) poker to full n-player [No Limit Texas Hold'em](https://en.wikipedia.org/wiki/Texas_hold_%27em) or [Pot Limit Omaha](https://en.wikipedia.org/wiki/Omaha_hold_%27em#Pot-limit_Omaha).\n\n## Install\n\nInstall using `pip install clubs`. To enable webserver rendering (see example below), install using `pip install clubs[render]`.\n\n## Example\n\n```python\nimport random\n\nimport clubs\n\nconfig = clubs.configs.NO_LIMIT_HOLDEM_SIX_PLAYER\ndealer = clubs.poker.Dealer(**config)\nobs = dealer.reset()\n\nwhile True:\n    call = obs['call']\n    min_raise = obs['min_raise']\n    max_raise = obs['max_raise']\n\n    rand = random.random()\n    if rand \u003c 0.1:\n        bet = 0\n    elif rand \u003c 0.80:\n        bet = call\n    else:\n        bet = random.randint(min_raise, max_raise)\n\n    obs, rewards, done = dealer.step(bet)\n    if all(done):\n        break\n\nprint(rewards)\n```\n\n## Configuration\n\nThe type of poker game is defined using a configuration dictionary. See [configs.py](./clubs/configs.py) for some example configurations. A configuration dictionary has to have the following key value structure:\n\n* num_players\n  * int: maximum number of players\n* num_streets\n  * int: number of streets\n* blinds\n  * int or list of ints: the blind distribution starting from the button e.g. [0, 1, 2, 0, 0, 0] for a 6 player 1-2 game\n  * a single int is expanded to the number of players, settings blinds=0 will result in no blinds [0, 0, 0, 0, 0, 0]\n* antes\n  * int or list of ints: the ante distribution starting from the button, analog to the blind distribution\n  * single ints are expanded to the number of players\n* raise_sizes\n  * float or str or list of floats or str: the maximum raise size as a list of values, one for each street\n  * options are ints (for fixed raise sizes), float('inf') (for no limit raise sizes) or 'pot' for pot limit raise sizes\n  * single values are expanded to the number of streets\n* num_raises\n  * float or list of floats: the maximum number of raises for each street\n  * options are ints (for a fixed number of bets per round) or float('inf') for unlimited number of raises\n  * single values are expanded to the number of streets\n* num_suits\n  * number of suits in the deck\n* num_ranks\n  * number of ranks in the deck\n* num_hole_cards\n  * number of hole cards for each player\n* num_community_cards\n  * number of community cards per street\n* num_cards_for_hand\n  * number of cards for a valid poker hand\n* mandatory_num_hole_cards\n  * number of hole cards which must be used for a poker hand\n* start_stack\n  * initial stack size\n\n## API\n\nclubs adopts the [Open AI gym](https://github.com/openai/gym) interface. See [clubs gym](https://github.com/fschlatt/clubs_gym.git) for a full clubs gym environment. To deal a new hand, call `dealer.reset()`, which returns a dictionary of observations for the current active player. To advance the game, call `dealer.step({bet})` with an integer bet size. Invalid bet sizes are always rounded to the nearest valid bet size. When the bet lies exactly between 2 valid bet sizes, it is always rounded down. For example, if the minimum raise size is 10 and the bet is 5, the bet is rounded down to 0, i.e. call or fold.\n\n## Universal Deuces\n\nThe hand evaluator is heavily inspired by the [deuces](https://github.com/worldveil/deuces/) library. The basic logic is identical, but the evaluator and lookup table are generalized to work for any deck configuration with number of ranks \u003c= 13 and number of suits \u003c= 4 and poker hands with 5 or less cards. See the poker [README](./clubs/poker/README.md) for further details.\n\nSome speed was sacrificed for the sake of better usability. Nonetheless, the evaluator is still quite fast on modern machines (Intel(R) Core(TM) i5-8250U):\n\n```python\n\u003e\u003e\u003e import clubs\n\u003e\u003e\u003e evaluator = clubs.poker.Evaluator(4, 13, 5)\n\u003e\u003e\u003e avg_time = evaluator.speed_test()\n\u003e\u003e\u003e print(f\"Average time per evaluation: {avg_time}\")\nAverage time per evaluation: 1.3986515504075214e-06\n\u003e\u003e\u003e print(f\"Evaluations per second = {1.0/avg_time}\")\nEvaluations per second = 714974.362062254\n```\n\n## Visualize\n\n3 different render modes are available via `dealer.render()`. The default render mode uses a web front which gets exposed on localhost on a specified or random port.\n\n\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"./clubs/render/resources/static/images/render_example.png\" alt=\"Render Example\" width=1000\u003e\n\n\u003c/div\u003e\n\n## Limitations\n\nEven though the library is designed to be as general as possible, it currently has a couple limitations:\n\n* Only integer chip denominations are supported\n* The evaluator only works for sub decks of the standard 52 card deck as well as a maximum of 5 card poker hands\n* Draw and stud poker games are not supported\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffschlatt%2Fclubs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffschlatt%2Fclubs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffschlatt%2Fclubs/lists"}