Skip to main content

spru_util/die/
mod.rs

1use std::mem;
2
3use derive_where::derive_where;
4
5use rand::{Rng, seq::IndexedRandom};
6use spru::common::error::AnyResult;
7use tagset::tagset;
8use telety::telety;
9
10use crate::cloned;
11
12#[derive(Debug)]
13pub struct Die<D: self::DieKind> {
14    die: D,
15    current_face: D::Face,
16}
17
18impl<D: self::DieKind> Die<D> {
19    pub fn current_face(&self) -> &D::Face {
20        &self.current_face
21    }
22}
23
24pub fn create<D: self::DieKind>(die: D) -> Create<D> {
25    let mut mock_rng = rand::rngs::mock::StepRng::new(0, 0);
26    let current_face = die.roll(&mut mock_rng);
27    cloned::create(Die { die, current_face })
28}
29
30/// Sets the die to a random face. Should not be used in interactions, as the interaction
31/// will be rejected if the outcome does not match on the server.
32pub fn roll<D: self::DieKind, R: rand::Rng>(state: &Die<D>, rng: &mut R) -> SetFace<D> {
33    let face = state.die.roll(rng);
34    set_face(face)
35}
36
37pub fn set_face<D: self::DieKind>(face: D::Face) -> SetFace<D> {
38    SetFace { face }
39}
40
41pub fn destroy<D: self::DieKind>() -> Destroy<D> {
42    cloned::destroy()
43}
44
45#[telety(crate::die)]
46#[tagset(Create<D>)]
47#[tagset(SetFace<D>)]
48#[tagset(Destroy<D>)]
49pub struct Actions<D: self::DieKind>;
50
51pub type Create<D> = cloned::Create<Die<D>>;
52
53#[derive_where(Debug, Clone; D::Face)]
54#[derive(serde::Serialize, serde::Deserialize, spru::action::Update)]
55pub struct SetFace<D: self::DieKind> {
56    face: D::Face,
57}
58
59impl<D: self::DieKind<Face: Clone>> spru::action::Update for SetFace<D> {
60    type T = Die<D>;
61    type Undo = Self;
62
63    fn update(&self, value: &mut Self::T) -> AnyResult<impl Into<Option<Self::Undo>>> {
64        let face = mem::replace(&mut value.current_face, self.face.clone());
65
66        Ok(Self { face })
67    }
68}
69
70pub type Destroy<D> = cloned::Destroy<Die<D>>;
71
72pub trait DieKind {
73    type Face;
74
75    fn roll<R: Rng>(&self, rng: &mut R) -> Self::Face;
76}
77
78/// A die with one of the first 'N' natural numbers as the faces,
79/// such as the standard 6-sided die, or a d20.
80#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
81pub struct DN<T>(T);
82
83impl<T> DN<T> {
84    pub fn new(n: T) -> Self {
85        Self(n)
86    }
87
88    pub fn n(&self) -> &T {
89        &self.0
90    }
91}
92
93impl<T> DieKind for DN<T>
94where
95    T: num_traits::Zero + rand::distr::uniform::SampleUniform + std::cmp::PartialOrd + Clone,
96{
97    type Face = T;
98
99    fn roll<R: Rng>(&self, rng: &mut R) -> Self::Face {
100        rng.random_range(T::zero()..self.0.clone())
101    }
102}
103
104/// A die with individually specified faces of type T
105#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
106pub struct Custom<T>(Vec<T>);
107
108impl<T> Custom<T> {
109    pub fn new(faces: Vec<T>) -> Self {
110        assert!(!faces.is_empty());
111        Self(faces)
112    }
113}
114
115impl<T: Clone> DieKind for Custom<T> {
116    type Face = T;
117
118    fn roll<R: Rng>(&self, rng: &mut R) -> Self::Face {
119        self.0.choose(rng).unwrap().clone()
120    }
121}