{"id":19360986,"url":"https://github.com/gawdl3y/tyche-rs","last_synced_at":"2025-04-23T04:44:26.692Z","repository":{"id":224094953,"uuid":"761483607","full_name":"Gawdl3y/tyche-rs","owner":"Gawdl3y","description":"Rust library for dice rolling and parsing of dice expressions (with a syntax similar to FoundryVTT)","archived":false,"fork":false,"pushed_at":"2025-04-08T18:45:18.000Z","size":255,"stargazers_count":3,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-16T05:30:08.331Z","etag":null,"topics":["dice","dnd","tabletop"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/tyche","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Gawdl3y.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","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},"funding":{"github":"Gawdl3y","ko_fi":"gawdl3y"}},"created_at":"2024-02-21T23:07:50.000Z","updated_at":"2025-04-08T18:45:22.000Z","dependencies_parsed_at":"2024-03-15T08:26:03.237Z","dependency_job_id":"c9e00708-777e-4c4d-9fa6-0e2ddadeb9dd","html_url":"https://github.com/Gawdl3y/tyche-rs","commit_stats":null,"previous_names":["gawdl3y/dicey-rs","gawdl3y/tyche-rs"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gawdl3y%2Ftyche-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gawdl3y%2Ftyche-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gawdl3y%2Ftyche-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gawdl3y%2Ftyche-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Gawdl3y","download_url":"https://codeload.github.com/Gawdl3y/tyche-rs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250372919,"owners_count":21419722,"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","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":["dice","dnd","tabletop"],"created_at":"2024-11-10T07:20:02.576Z","updated_at":"2025-04-23T04:44:26.686Z","avatar_url":"https://github.com/Gawdl3y.png","language":"Rust","funding_links":["https://github.com/sponsors/Gawdl3y","https://ko-fi.com/gawdl3y"],"categories":[],"sub_categories":[],"readme":"# Tyche\n\n[![Crates.io Version](https://img.shields.io/crates/v/tyche)](https://crates.io/crates/tyche)\n[![docs.rs](https://img.shields.io/docsrs/tyche)](https://docs.rs/tyche)\n\nTyche is a library for parsing, rolling, and explaining the results of tabletop dice.  \nIt also has a simple CLI app binary that evaluates a given expression.\n\nThe eventual goal is full compatibility with [FoundryVTT's dice syntax](https://foundryvtt.com/article/dice/) with some\nconvenient extensions.\n\n## Features\n\n- Parsing dice expressions, simple or complex\n- Arithmetic\n  - Addition: `2d6 + 2`\n  - Subtraction: `2d6 - 2`\n  - Multiplication: `2d6 * 2`\n  - Division (integer, rounded down): `2d6 / 2`\n  - Division (integer, rounded up): `2d6 \\ 2`\n  - Standard mathematical order of operations\n  - Parenthetical grouping: `(2d6 + 2) * 2`\n- Dice modifiers\n  - Keep highest (advantage): `2d20kh`, `2d20k`\n    - With specific amount to keep: `3d20kh2`\n  - Keep lowest (disadvantage): `2d20kl`\n    - With specific amount to keep: `3d20kl2`\n  - Reroll (once): `4d6r`\n    - With specific condition: `4d6r\u003e4`, `4d6r\u003e=5`, `4d6r\u003c3`, `4d6r\u003c=2`, `4d6r4`\n  - Reroll (recursive): `4d6rr`\n    - With specific condition: `4d6rr\u003e4`, `4d6rr\u003e=5`, `4d6rr\u003c3`, `4d6rr\u003c=2`, `4d6rr4`\n  - Explode (recursive): `4d6x`\n    - With specific condition: `4d6x\u003e4`, `4d6x\u003e=5`, `4d6x\u003c3`, `4d6x\u003c=2`, `4d6x4`\n  - Explode (once): `4d6xo`\n    - With specific condition: `4d6xo\u003e4`, `4d6xo\u003e=5`, `4d6xo\u003c3`, `4d6xo\u003c=2`, `4d6xo4`\n  - Minimum: `4d8min3`\n  - Maximum: `4d8max6`\n- Dice modifier chaining (applied in the order they're specified): `4d6rr1x\u003e4`, `8d8min2kh4xo`\n- Roller abstractions, allowing custom die rolling behavior\n- Support for `no_std` (`alloc` is required)\n\n## Installation\n\n### Library\n\nRun `cargo add tyche` or add the following to your project's Cargo.toml file:\n\n```toml\n[dependencies]\ntyche = \"0.3.1\"\n```\n\n### Binary (CLI app)\n\nRun `cargo install tyche --features build-binary`.  \nAssuming Cargo's bin directory is in your `$PATH`, use the app with `tyche` or `tyche \u003cdice expression\u003e`.\n\n## Library usage\n\nThere are three main types that you'll start with while using Tyche:\n\n- [`Dice`]: a struct containing a dice count, number of sides for each die, and modifiers that should be applied to any\n  resulting rolls, representing a set of dice, e.g. `4d8x`.\n- [`Expr`]: an AST-like tree structure enum of individual components of a dice expression, capable of representing\n  complex sets of mathematical operations including dice, e.g. `(2d6 + 2) * 2`.\n- [`Roller`]: a trait for rolling individual die values and entire sets of `Dice`. Sometimes referred to as simply \"RNG\"\n  for the sake of brevity. There are a few `Roller` implementations available out of the box:\n  - [`FastRand`]: uses the [fastrand] crate for random number generation\n  - [`Max`]: always rolls the max possible value for each die\n  - [`Val`]: always rolls one specific value, ignoring dice sides\n  - [`Iter`]: rolls a specific sequence of die values from an iterator, ignoring dice sides\n    (useful for testing purposes)\n\n### Parsing\n\nAll parsing requires the `parse` feature of the crate to be enabled (which it is by default).\n\nTyche uses the [chumsky] parser generator to parse all strings in a _nearly_ zero-copy and wicked-fast fashion.\n\nMost conveniently, parsing can be done by utilizing the standard [`FromStr`] implementations for the relevant types\n([`Dice`], [`Expr`], [`Modifier`], and [`Condition`]):\n\n```rust\nuse tyche::{\n\tdice::modifier::{Condition, Modifier},\n\tDice, Expr,\n};\n\nlet expr: Expr = \"4d6rr\u003c3 + 2d8 - 4\".parse()?;\nlet dice: Dice = \"4d6rr\u003c3\".parse()?;\nlet modifier: Modifier = \"rr\u003c3\".parse()?;\nlet cond: Condition = \"\u003c3\".parse()?;\n```\n\nAlternatively, you can directly use the parsers for each type via its associated [`GenParser`] implementation\nor the functions in the [`tyche::parse`] module.\n\n### Manually creating Dice\n\nProgrammatically constructing [`Dice`] to roll is painless, even with lots of chained modifiers,\nthanks to its use of the builder pattern.\n\n```rust\nuse tyche::{dice::modifier::Condition, Dice};\n\n// Simple set of dice, no modifiers: 2d20\nlet d2d20 = Dice::new(2, 20);\n\n// Exploding dice: 4d6x\nlet d4d6x = Dice::builder()\n\t.count(4)\n\t.sides(6)\n\t.explode(None, true)\n\t.build();\n\n// Chained modifiers: 6d6rr1x\nlet d6d6rr1x = Dice::builder()\n\t.count(6)\n\t.sides(6)\n\t.reroll(Condition::Eq(1), true)\n\t.explode(None, true)\n\t.build();\n```\n\n### Rolling Dice\n\nAll rolling of dice is performed by a [`Roller`] implementation.  \nThe most suitable \"default\" roller implementation is [`FastRand`], which generates random numbers for die values using a\n[`fastrand::Rng`] instance.\n\n```rust\nuse tyche::dice::roller::FastRand as FastRandRoller;\n\n// Create a FastRand roller with the default thread-local fastrand::Rng\n// This is only possible with the \"std\" feature enabled (which it is by default)\nlet mut roller = FastRandRoller::default();\n\n// Create a FastRand roller with a manual seed\nlet mut roller = FastRandRoller::with_seed(0x750c38d574400);\n\n// Create a FastRand roller with a custom fastrand::Rng\nlet rng = fastrand::Rng::with_seed(0x750c38d574400);\nlet mut roller = FastRandRoller::new(rng);\n```\n\nOnce you have a roller, you can roll a single die at a time or a whole set of [`Dice`] with it:\n\n```rust\nuse tyche::dice::{roller::FastRand as FastRandRoller, Dice, Roller};\n\nlet mut roller = FastRandRoller::default();\n\n// Roll a single 20-sided die\nlet die = roller.roll_die(20);\n\n// Roll two six-sided dice\nlet dice = Dice::new(2, 6);\nlet rolled = roller.roll(\u0026dice, true)?;\n```\n\nRolling a single die results in a [`DieRoll`] instance, whereas rolling a set of `Dice` returns a [`Rolled`] instance.\n\n#### Working with rolled dice sets\n\n[`Rolled`] is a struct that ties multiple [`DieRoll`]s together with the [`Dice`] that were rolled to generate them so\nit can accurately describe what happened during the roll and application of any modifiers that were on the dice.\n\nUsing a `Rolled` result, you can easily total the results of all rolled dice and/or build a string that contains the\noriginal dice set along with a list of each individual die roll.\n\n```rust\nuse tyche::{\n\tdice::{roller::FastRand as FastRandRoller, Dice, Roller},\n\texpr::Describe,\n};\n\n// Build and roll 4d6kh2\nlet mut roller = FastRandRoller::default();\nlet dice = Dice::builder()\n\t.count(4)\n\t.sides(6)\n\t.keep_high(2)\n\t.build();\nlet rolled = roller.roll(\u0026dice, true)?;\n\n// Total the results\nlet total = rolled.total()?;\nassert!((2..=12).contains(\u0026total));\n\n// Build a detailed string about the rolls\n// The resulting string will look like \"4d6kh2[2 (d), 5, 6, 4 (d)]\"\nlet described = rolled.to_string();\n\n// This is the same as the to_string() call above, except only two die rolls will be listed.\n// The resulting string will look like \"4d6kh2[2 (d), 5, 2 more...]\"\nlet limited = rolled.describe(Some(2));\n```\n\n#### Working with individual die rolls\n\nA [`DieRoll`] contains the final value of the die alongside information about any changes that were made to it and the\nsource of said changes.\n\n##### Added rolls\n\nWhen a modifier (rather than the original dice set) causes the addition of a new [`DieRoll`] to a [`Rolled`] set,\nthe roll's `added_by` field is set to `Some(source_modifier)`. An added roll will be marked as such in strings.  \nModifiers that can result in additional die rolls are:\n\n- Explode\n- Reroll\n\n##### Dropped rolls\n\nWhen a modifier causes the removal of a [`DieRoll`] from a [`Rolled`] set,\nthe roll's `dropped_by` field is set to `Some(source_modifier)`. A dropped roll will not be affected by any further\nmodifiers, is not included when totaling the rolled set, and will be marked as dropped in strings.  \nModifiers that can result in dropped die rolls are:\n\n- Reroll\n- Keep highest\n- Keep lowest\n\n##### Changed rolls\n\nWhen a modifier directly manipulates the value of a [`DieRoll`] in a [`Rolled`] set, the roll's `changes` field has a\n[`ValChange`] item added to it that describes the change made and the modifier that caused it.  \nModifiers that can result in changed die rolls are:\n\n- Minimum\n- Maximum\n\n### Working with expressions\n\n[`Expr`] trees are essentially always obtained from parsing an expression string, as manually creating them would be\nquite cumbersome.\n\nOnce you have an `Expr` variant, it can be evaluated to produce an [`Evaled`] expression tree.\n`Evaled` expression trees are nearly identical to their originating `Expr` tree, except any Dice variants have their\ndice rolled. This separation allows for describing an expression in a detailed way both before and after rolling dice it\ncontains, in addition to a few other utilities.\n\n```rust\nuse tyche::{\n\tdice::roller::FastRand as FastRandRoller,\n\texpr::{Describe, Expr},\n};\n\n// Parse a nice dice expression\nlet expr: Expr = \"4d6 + 2d8 - 2\".parse()?;\n\n// This expression is definitely not deterministic (it contains dice sets)\nassert!(!expr.is_deterministic());\n\n// Build a string for the expression - in this case, it'll be identical to the original parsed string\nassert_eq!(expr.to_string(), \"4d6 + 2d8 - 2\");\n\n// Evaluate the expression, rolling any dice sets it contains\nlet mut roller = FastRandRoller::default();\nlet evaled = expr.eval(\u0026mut roller)?;\n\n// Build a detailed string about the evaluated expression\n// The resulting string will look like \"4d6[3, 2, 6, 2] + 2d8[5, 4] - 2\"\nlet described = evaled.to_string();\n\n// This is the same as the to_string() call above, except only two die rolls in each set will be listed.\n// The resulting string will look like \"4d6[3, 2, 2 more...] + 2d8[5, 4] - 2\"\nlet limited = evaled.describe(Some(2));\n\n// Calculate the final result of the expression\nlet total = evaled.calc()?;\nassert!((4..=38).contains(\u0026total));\n```\n\n## Contributing\n\nAll contributions are welcome! Try to keep PRs relatively small in scope (single feature/fix/refactor at a time)\nand word your commits descriptively.\n\n## License\n\nTyche is licensed under the [LGPLv3](https://www.gnu.org/licenses/lgpl-3.0) license.\n\n[`Dice`]: https://docs.rs/tyche/latest/tyche/dice/struct.Dice.html\n[`Expr`]: https://docs.rs/tyche/latest/tyche/expr/enum.Expr.html\n[`Evaled`]: https://docs.rs/tyche/latest/tyche/expr/enum.Evaled.html\n[`Roller`]: https://docs.rs/tyche/latest/tyche/dice/roller/trait.Roller.html\n[`FastRand`]: https://docs.rs/tyche/latest/tyche/dice/roller/struct.FastRand.html\n[`Max`]: https://docs.rs/tyche/latest/tyche/dice/roller/struct.Max.html\n[`Val`]: https://docs.rs/tyche/latest/tyche/dice/roller/struct.Val.html\n[`Iter`]: https://docs.rs/tyche/latest/tyche/dice/roller/struct.Iter.html\n[`Modifier`]: https://docs.rs/tyche/latest/tyche/dice/modifier/enum.Modifier.html\n[`Condition`]: https://docs.rs/tyche/latest/tyche/dice/modifier/enum.Condition.html\n[`DieRoll`]: https://docs.rs/tyche/latest/tyche/dice/struct.DieRoll.html\n[`Rolled`]: https://docs.rs/tyche/latest/tyche/dice/struct.Rolled.html\n[`ValChange`]: https://docs.rs/tyche/latest/tyche/dice/struct.ValChange.html\n[`GenParser`]: https://docs.rs/tyche/latest/tyche/parse/trait.GenParser.html\n[`tyche::parse`]: https://docs.rs/tyche/latest/tyche/parse/index.html\n[`FromStr`]: https://doc.rust-lang.org/std/str/trait.FromStr.html\n[fastrand]: https://github.com/smol-rs/fastrand\n[`fastrand::Rng`]: https://docs.rs/fastrand/latest/fastrand/struct.Rng.html\n[chumsky]: https://github.com/zesterer/chumsky\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgawdl3y%2Ftyche-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgawdl3y%2Ftyche-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgawdl3y%2Ftyche-rs/lists"}