{"id":30676001,"url":"https://github.com/kreibaum/pacosako","last_synced_at":"2025-09-01T09:13:38.194Z","repository":{"id":38977386,"uuid":"252795561","full_name":"kreibaum/pacosako","owner":"kreibaum","description":"This code drives the pacoplay.com website where you can play Paco Ŝako.","archived":false,"fork":false,"pushed_at":"2025-08-06T20:00:37.000Z","size":77611,"stargazers_count":25,"open_issues_count":30,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-08-06T21:08:24.887Z","etag":null,"topics":["elm","game-development","rust"],"latest_commit_sha":null,"homepage":"http://pacoplay.com","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kreibaum.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-04-03T17:21:22.000Z","updated_at":"2025-08-06T20:00:41.000Z","dependencies_parsed_at":"2023-12-12T23:27:03.953Z","dependency_job_id":"5ec9ef31-35d2-4c5c-bc0c-24f450ac130b","html_url":"https://github.com/kreibaum/pacosako","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/kreibaum/pacosako","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kreibaum%2Fpacosako","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kreibaum%2Fpacosako/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kreibaum%2Fpacosako/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kreibaum%2Fpacosako/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kreibaum","download_url":"https://codeload.github.com/kreibaum/pacosako/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kreibaum%2Fpacosako/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273099317,"owners_count":25045697,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-01T02:00:09.058Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["elm","game-development","rust"],"created_at":"2025-09-01T09:13:34.515Z","updated_at":"2025-09-01T09:13:38.187Z","avatar_url":"https://github.com/kreibaum.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pacosako\n\n[![CI](https://github.com/kreibaum/pacosako/actions/workflows/main.yml/badge.svg)](https://github.com/kreibaum/pacosako/actions/workflows/main.yml)\n\u003ca href=\"https://hosted.weblate.org/engage/pacoplay/\"\u003e\n\u003cimg src=\"https://hosted.weblate.org/widgets/pacoplay/-/main-website/svg-badge.svg\" alt=\"Translation status\" /\u003e\n\u003c/a\u003e\n\nThis is the codebase for the [pacoplay.com website](http://pacoplay.com). It has a frontend written in\n[Elm](https://elm-lang.org) and a backend written in [Rust](https://rust-lang.org) based on the\nAxum server framework.\n\nIf you want to help with the translation, you can do so on our [Weblate](https://hosted.weblate.org/engage/pacoplay/)\nproject:\n\n\u003ca href=\"https://hosted.weblate.org/engage/pacoplay/\"\u003e\n\u003cimg src=\"https://hosted.weblate.org/widgets/pacoplay/-/main-website/multi-auto.svg\" alt=\"Translation status\" /\u003e\n\u003c/a\u003e\n\n# Running the Project for Development\n\nSorry, this is a bit of a mess.\n\n## Install Required Software\n\nIf you want to run the development environment locally, you will need to have installed:\n\n- Rust, rustup, [wasm-pack](https://github.com/rustwasm/wasm-pack)\n    - `cargo binstall wasm-pack`\n- Elm and [elm-watch](https://github.com/lydell/elm-watch) and [elm-spa](https://www.elm-spa.dev/) (via npm)\n- terser (via npm)\n- brotli\n- sqlx-cli (must be 0.5 :-/), e.g. via `cargo install sqlx-cli --version ^0.5`\n- typescript (via npm)\n\nFor the npm packages, consider\n\n- `sudo npm install -g elm elm-spa@latest typescript terser --unsafe-perm=true --allow-root`\n\nIf you want to work on the AI, you also need\n\n- julia\n\nAnd there is some work towards a python package as well\n\n- python3\n\n## Set up Database\n\nThe project uses SQLx for database access. You need to create a database before\nit can compile the rust code.\n\n```sh\nscripts/setup-fresh-database.sh\n```\n\nIf you are getting `error: error returned from database: disk I/O error` when\nrunning this, check if maybe you deleted `database.sqlite` but kept around\nthe `database.sqlite-wal` and `database.sqlite-shm` files. If so, delete them.\n\n## Fake a Secrets File\n\nYou can't get the real discord client secret, so you need to create a fake one.\nWe use Discord to manage logins so we don't have to deal with emails and account\nrecoveries.\n\n```sh\necho 'discord_client_secret = \"\"' \u003e\u003e dev-secrets.toml\n```\n\n## Build everything\n\nThe project has a few internal development tools that you'll need to build.\n\n```sh\ncargo build --all --release\n```\n\n## Compile the Frontend\n\n```sh\nscripts/compile-frontend.sh\n```\n\n## Run the Project\n\n### Run the backend\n\n```sh\ncd backend\ncargo run\n```\n\n### Run the frontend\n\n```sh\nscripts/compile-frontend.sh\ncd frontend\nelm-watch hot\n```\n\n## Useful Software\n\nBuilding WASM artifacts is faster, if you have wasm-bindgen installed:\n\n    cargo install wasm-bindgen-cli\n\n## Rules for Paco Ŝako (Rust Library)\n\nBesides the server frontend in Elm and the backend in Rust, we also have a Rust\nlibrary which implements the rules of the game and provides some analysis\nfunctions. Eventually, this library will also be included in the frontend via\nwebassembly and Elm ports.\n\nTo run an example, just execute `cargo run`.\n\nTo build the webassembly file from the library run the `scripts/compile-wasm.sh` script.\nYou also get this as part of `scripts/compile-frontend.sh` as well.\n\nSee https://rustwasm.github.io/docs/book/game-of-life/hello-world.html for details on wasm.\n\n## Working on translations\n\nRemember: if you just want to help with translations, use\n[Weblate](https://hosted.weblate.org/engage/pacoplay/).\n\nThis part is for development. It is switching the used language when programming.\n\nIf you want to merge translations that were done with weblate, use\n\n    git remote add weblate https://hosted.weblate.org/git/pacoplay/main-website/\n    git remote update weblate\n    git merge weblate/master\n\nCurrently, the translations are not integrated into the live reloading\ndevelopment server. You can set the language you see the UI in by going into\nthe `frontend` folder and copying the right language version into position:\n\n    # English\n    ./backend/target/release/i18n_gen English\n    # Dutch\n    ./backend/target/release/i18n_gen Dutch\n    # Esperanto\n    ./backend/target/release/i18n_gen Esperanto\n    # List all Languages\n    ./backend/target/release/i18n_gen --list\n    # Once you have chosen a language it is remembered any you can rebuild using\n    ./backend/target/release/i18n_gen\n\nOnce you copy this, the dev server `elm-watch hot` should pick up the change and recompile the\nfrontend for you.\n\n### Adding a new language\n\nYou first need a new language file in `frontend/i18n`. This can be\nan empty dictionary `{}`. Once you sync that to weblate, weblate will allow\ntranslators to translate the file.\n\nWhen translations are available, merge changes from weblate into the main branch\nas described above.\n\nAdd your language to `frontend/i18n-config.json` to make it available to the Elm code.\nThen use `i18n_gen` in the frontend directory to switch to it.\n\nAfterwards, you also need to update the `languageChoiceV2` function in `Header.elm`.\n\nNote that locally switching language doesn't work with the buttons, and instead you\nneed to run `i18n_gen Esperanto` or the equivalent for your language. This is\nbecause the language is set at compile time, so the app only has one language at a time.\n\nIn the backend you need to adapt the `get_static_language_file` function to make\nsure the productive server is also able to deliver the compiled Elm in the right\nlanguage.\n\n## Creating a username-password user\n\nWhen developing the server without access to Discord secrets, you can create a\nusername-password user by inserting it directly into the database with\n\n```sql\n-- First, insert the user into the user table\nINSERT INTO user (name, avatar)\nVALUES ('Rolf Kreibaum', 'identicon:204bedcd9a44b3e1db26e7619bca694d');\n\n-- Then, retrieve the ID of the newly inserted user and use it to insert into the login table\nINSERT INTO login (user_id, type, identifier, hashed_password)\nVALUES (last_insert_rowid(), 'password', 'rolf',\n        '$argon2id$v=19$m=19456,t=2,p=1$OsG1y7Fvnq1FW8gKvlK4gQ$ryEgps/NG93d/Nyp8ri0GMR+LHymyb7ivnw5vnE4Q7U');\n```\n\nTo get the `argon2` hash of a password, run the server locally\nand try logging in with the password you want to use. The server will print\nthe hash to the console. (This happens only with `dev_mode = true`.)\n\nRemember to commit the changes to the database.\n\n# Working with Julia\n\nTo load JtacPacoSako, do the following:\n\n```julia\n# Load CUDA and cuDNN\njulia\u003e # using Revise (Only for development)\njulia\u003e using cuDNN\njulia\u003e using JtacPacoSako\n```\n\nTo compile the shared library run `cargo build --release` in ./lib\n\nThis assumes you have installed Jtac.jl and JtacPacoSako as a development package using\n\n    ]dev {..}/Jtac.jl\n    ]dev {..}/pacosako/julia\n\nTest if everything works\n\n```\njulia\u003e G = PacoSako;\njulia\u003e model = Model.NeuralModel(G, Model.@chain G Dense(50, \"relu\"));\njulia\u003e player = Player.MCTSPlayer(model, power = 50, temperature=0.1);\njulia\u003e dataset = Training.record(player, 10)\nDataSet{PacoSako} with 1258 elements and 0 features\n```\n\n## Loading Models\n\nModels can be used from various sources. If you just want to play with an\nexisting model, you can get it by its name from our artifact storage:\n\nThe artifact system is hosted at https://static.kreibaum.dev/\n\n```julia\n# Get the Default model for Ludwig:\nmodel = Ludwig()\n# Get a specific version of Ludwig, make it run on the GPU:\nmodel = Ludwig(\"1.0-human\", async=false, backend=:cuda)\n```\n\nIf you already have a specific model downloaded, you can load it from a file:\n\n```julia\nmodel = Model.load(\"models/ludwig-1.0.jtm\", async=false, backend=:cuda)\n```\n\n## Using models\n\nTo apply a model to a single game state, use `Model.apply`:\n\n```julia\nmodel = Ludwig()\nstate = PacoSako()\nModel.apply(model, state)\n```\n\nTurn the model into a player:\n\n```julia\nplayer = Player.MCTSPlayer(model, power = 3000, temperature=0.01)\n```\n\nPlay on the website\n\n```julia\nPacoPlay.play(player, color = :white, domain = :dev)\n```\n\nOr to connect to the official server with a username and password\n\n```julia\nPacoPlay.play(player, color = :white, domain = :official, username = \"ludwig_ai\", password = \"hunter2\")\n```\n\n## Errors you may encounter\n\n```plaintext\njulia\u003e model = Ludwig(\"1.0-human\", async=false, backend=:cuda)\nERROR: CUDA initialization failed: CUDA error (code 999, CUDA_ERROR_UNKNOWN)\n```\n\nThis may happen when you suspend your computer while the CUDA driver is still\nloaded. To fix this, you must restart your computer.\n\n# Working with Python\n\nWe have a Python package that can be used to interact with the Rust library.\nIt is there to support some statistical analysis Bas has been doing.\n\nYou'll need `maturin` from the pyo3 ecosystem: https://pyo3.rs/\n\nThen you need to got to `/python` to build the package:\n\n```bash\nmaturin build --release\n```\n\nAfterward, you can install the package with pip:\n\n```bash\npip install target/wheels/pacosako-$LOOK_IT_UP.whl\n```\n\nWhen you are just developing the package, you can use the `develop` command:\n\n```bash\nmaturin develop --release\n```\n\n# Replay Meta Data\n\nIt is possible to attach arbitrary json metadata to a replay.\nThis requires you to have access to an AI users api credentials.\n\nEvery piece of metadata is associated with a game and an action index. It is\nalso sorted into a category. The category is just an arbitrary string\nshown in the frontend to group metadata together and to control what is shown.\n\nOf course, the frontend has no way to render arbitrary metadata. So you need to\nconform to an implemented schema. Here is what we currently support:\n\n```jsonc\n{\n  \"type\": \"arrow\",\n  \"tail\": 11,\n  \"head\": 27,\n  \"color\": \"#ffc80080\", // Optional, default #ffc80080\n  \"width\": 20 // Optional, default 10\n  // \"width\" may also be replaced by \"weight\" which scales arrows proportionally\n}\n```\n\nAdditionally, we are also planning to implement\n\n```jsonc\n{\n  \"type\": \"value\",\n  \"value\": 0.38,\n  \"impact\": -0.09, // Optional\n  \"best\": 0.02, // Optional\n  \"rank\": 3, // Optional\n  \"rank_of\": 10 // Optional\n}\n```\n\n# Architecture\n\n![A schematic drawing of the architecture when deployed.](/doc/architecture.png)\n\n# Deployment and Server Management\n\nThis application is set up to run using two systemd services, one for the staging environment and one for\nthe production environment. The configuration for these services is available in the `/scripts` directory.\n\n## Systemd Services\n\nThe systemd service files are:\n\n- `stage.service`: This service runs the staging server.\n- `prod.service`: This service runs the production server.\n\nThese service files should be placed in the `/etc/systemd/system/` directory on your server.\n\nTo control the services, you can use the following commands:\n\n- Start the service: `sudo systemctl start servicename`\n- Stop the service: `sudo systemctl stop servicename`\n- Enable the service to start on boot: `sudo systemctl enable servicename`\n- Disable the service from starting on boot: `sudo systemctl disable servicename`\n- Check the status of the service: `sudo systemctl status servicename`\n\nReplace `servicename` with either `stage` or `prod` depending on which service you want to control.\n\n## Update Scripts\n\nThere are two scripts used to update the staging and production servers:\n\n- `update-stage.sh`: This script is used to deploy a new version to the staging server.\n  It first stops the staging service, removes the existing deployment, installs the new deployment,\n  and then restarts the staging service.\n- `update-prod.sh`: This script is used to promote the staging version to production.\n  It first stops the production service, backs up the current production server and database,\n  removes the existing deployment, installs the new deployment from staging,\n  and then restarts the production service.\n\nEach server will update its own database schema when it starts up.\n\n## Caddy Configuration\n\nThe application uses caddy to reverse proxy `dev.pacoplay.com` to the staging system and `pacoplay.com` to the\nproduction system.\nThe `Caddyfile` is available in the `/scripts` directory. This configuration file should be placed in the `/etc/caddy/`\ndirectory.\n\n## Database Backups\n\nThe application automatically creates daily backups of the production SQLite database. These backups are created by a\nscript named `create-backup.sh` which is run as a nightly cron job at 2 AM.\n\nThe `create-backup.sh` script performs the following actions:\n\n1. Creates a backup of the `prod.sqlite` database located in `/home/pacosako/db/`.\n2. Compresses the backup using `gzip`.\n3. Deletes all but the five most recent backups.\n\nThe backups are saved in the `/home/pacosako/db/daily-backup/` directory, with each backup named\nas `prod-YYYYMMDDHHMM.sqlite.gz`, where `YYYYMMDDHHMM` is the date and time when the backup was created.\n\n### Set Up The Backup Cron Job\n\nTo set up the backup cron job, run `crontab -e` and add the following line:\n\n```cron\n0 2 * * * /home/pacosako/create-backup.sh\n```\n\nThis will run the `create-backup.sh` script every day at 2 AM.\n\n# Papers and other references\n\nThis isn't a complete overview, but I wanted a central place to collect the references that I know about.\n\n## Auxiliary Policy Targets\n\n[Accelerating Self-Play Learning in Go](https://arxiv.org/abs/1902.10565)\n\n\u003e As another generalizable improvement over AlphaZero, we add an auxiliary policy target that predicts the\n\u003e opponent’s reply on the following turn to improve regularization.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkreibaum%2Fpacosako","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkreibaum%2Fpacosako","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkreibaum%2Fpacosako/lists"}