Skip to content

Commit 66b55e8

Browse files
committed
Add dice::tree and dice::TreeBuilder
1 parent ef58642 commit 66b55e8

File tree

2 files changed

+225
-0
lines changed

2 files changed

+225
-0
lines changed

src/dice.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ pub use fn_builder::*;
8484
mod index_of;
8585
pub use index_of::*;
8686

87+
mod tree;
88+
pub use tree::*;
89+
8790
#[cfg(any(feature = "rand_full", all(feature = "rand_core", feature = "rand")))]
8891
mod rand;
8992
#[cfg(any(feature = "rand_full", all(feature = "rand_core", feature = "rand")))]

src/dice/tree.rs

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
use crate::{dice, dice::LengthRange, Die, Fate};
2+
3+
pub trait TreeBuilder<T> {
4+
fn branch_count(&self, fate: Fate, max_branch_count: usize) -> usize;
5+
fn build(&mut self, fate: Fate, subtrees: impl ExactSizeIterator<Item = T>) -> T;
6+
}
7+
8+
pub fn tree<T, B>(builder_die: impl Die<B>, node_count_range: impl LengthRange) -> impl Die<T>
9+
where
10+
B: TreeBuilder<T>,
11+
{
12+
let node_count_die = dice::length(node_count_range);
13+
14+
dice::from_fn(move |mut fate| {
15+
let mut builder = fate.roll(&builder_die);
16+
let node_count = fate.roll(&node_count_die);
17+
let root_idx = 0;
18+
19+
// Generate tree structure
20+
let nodes = {
21+
let mut nodes: Vec<Vec<usize>> = Vec::with_capacity(node_count.saturating_add(1));
22+
let mut nodes_to_intialize: Vec<(usize, usize)> = Vec::new();
23+
24+
// Add root
25+
nodes.push(Vec::new());
26+
nodes_to_intialize.push((root_idx, node_count));
27+
28+
// Initialize the nodes from top to button
29+
while let Some((parent_idx, remaining_size_of_parent)) = nodes_to_intialize.pop() {
30+
// Create children for parent
31+
if remaining_size_of_parent > 0 {
32+
// Branch randomly
33+
let branch_count = builder.branch_count(fate.copy(), remaining_size_of_parent);
34+
assert!(
35+
branch_count >= 1,
36+
"Branch count is {}, but must be >= 1",
37+
branch_count
38+
);
39+
assert!(
40+
branch_count <= remaining_size_of_parent,
41+
"Branch count is {}, but must be <= {}",
42+
branch_count,
43+
remaining_size_of_parent
44+
);
45+
46+
// Split remaining size randomly
47+
let remaining_size_of_children = fate.roll(dice::terms_of_usize(
48+
remaining_size_of_parent - branch_count,
49+
branch_count,
50+
));
51+
52+
for remaining_size_of_child in remaining_size_of_children {
53+
let child_idx = nodes.len();
54+
55+
// Add child
56+
nodes.push(Vec::new());
57+
nodes_to_intialize.push((child_idx, remaining_size_of_child));
58+
59+
// Register child to parant
60+
let children = &mut nodes[parent_idx];
61+
children.push(child_idx);
62+
}
63+
}
64+
}
65+
66+
nodes
67+
};
68+
69+
// Generate actual tree
70+
let tree = {
71+
// Create slots for storing the subtrees temporarily
72+
let mut subtree_slots: Vec<Option<T>> = Vec::with_capacity(nodes.len());
73+
for _ in 0..nodes.len() {
74+
subtree_slots.push(None)
75+
}
76+
77+
// Create the subtrees from buttom to top
78+
for (idx, children) in nodes.into_iter().enumerate().rev() {
79+
// Collect subtrees
80+
let mut subtrees = Vec::with_capacity(children.len());
81+
for idx in children {
82+
subtrees.push(subtree_slots[idx].take().unwrap());
83+
}
84+
85+
// Merge subtrees
86+
let expression = builder.build(fate.copy(), subtrees.into_iter());
87+
subtree_slots[idx] = Some(expression);
88+
}
89+
90+
// Return the root
91+
subtree_slots[root_idx].take().unwrap()
92+
};
93+
94+
tree
95+
})
96+
}
97+
98+
#[cfg(test)]
99+
mod tests {
100+
use crate::prelude::*;
101+
102+
use super::TreeBuilder;
103+
104+
// Tree type for testing
105+
#[derive(Debug, Clone)]
106+
enum Expression {
107+
Constant(u32),
108+
Add(Box<Expression>, Box<Expression>),
109+
Sub(Box<Expression>, Box<Expression>),
110+
Sum(Vec<Expression>),
111+
}
112+
113+
impl Expression {
114+
fn into_node_count(self) -> usize {
115+
let mut acc = 0;
116+
let mut expressions = vec![self];
117+
while let Some(expression) = expressions.pop() {
118+
acc += 1;
119+
match expression {
120+
Expression::Constant(_) => (),
121+
Expression::Add(left, right) => expressions.extend([*left, *right]),
122+
Expression::Sub(left, right) => expressions.extend([*left, *right]),
123+
Expression::Sum(subtrees) => expressions.extend(subtrees),
124+
}
125+
}
126+
// Subtract 1 because the root doesn't count
127+
acc - 1
128+
}
129+
130+
fn into_max_depth(self) -> usize {
131+
let mut acc = 0;
132+
let mut expressions = vec![(0, self)];
133+
while let Some((max_depth, expression)) = expressions.pop() {
134+
let max_depth = max_depth + 1;
135+
match expression {
136+
Expression::Constant(_) => {
137+
acc = acc.max(max_depth);
138+
}
139+
Expression::Add(left, right) => {
140+
expressions.extend([(max_depth, *left), (max_depth, *right)])
141+
}
142+
Expression::Sub(left, right) => {
143+
expressions.extend([(max_depth, *left), (max_depth, *right)])
144+
}
145+
Expression::Sum(subtrees) => {
146+
if subtrees.is_empty() {
147+
acc = acc.max(max_depth);
148+
} else {
149+
expressions.extend(subtrees.into_iter().map(|e| (max_depth, e)))
150+
}
151+
}
152+
}
153+
}
154+
// Subtract 1 because the root doesn't count
155+
acc - 1
156+
}
157+
}
158+
159+
#[derive(Clone)]
160+
struct ExpressionBuilder;
161+
162+
impl TreeBuilder<Expression> for ExpressionBuilder {
163+
fn build(
164+
&mut self,
165+
mut fate: Fate,
166+
mut subtrees: impl ExactSizeIterator<Item = Expression>,
167+
) -> Expression {
168+
match subtrees.len() {
169+
0 => {
170+
if fate.roll(dice::weighted_bool(1, 10)) {
171+
let constant = fate.roll(dice::u32(..));
172+
Expression::Constant(constant)
173+
} else {
174+
Expression::Sum(Vec::new())
175+
}
176+
}
177+
2 => {
178+
let right = Box::new(subtrees.next().unwrap());
179+
let left = Box::new(subtrees.next().unwrap());
180+
if fate.roll(dice::bool()) {
181+
Expression::Add(left, right)
182+
} else {
183+
Expression::Sub(left, right)
184+
}
185+
}
186+
_ => Expression::Sum(subtrees.collect()),
187+
}
188+
}
189+
190+
fn branch_count(&self, mut fate: Fate, max_branch_count: usize) -> usize {
191+
let max_branching = max_branch_count.min(2);
192+
fate.roll(dice::usize(1..=max_branching))
193+
}
194+
}
195+
196+
#[test]
197+
fn tree_has_correct_node_count() {
198+
Dicetest::repeatedly().run(|mut fate| {
199+
let node_count = fate.roll(dice::length(..));
200+
let tree = fate.roll(dice::tree(dice::just(ExpressionBuilder), node_count));
201+
assert_eq!(node_count, tree.into_node_count());
202+
});
203+
}
204+
205+
#[test]
206+
fn tree_calc_stats() {
207+
Dicetest::repeatedly()
208+
.passes(0)
209+
.stats_enabled(true)
210+
.run(|mut fate| {
211+
let node_count = 100000;
212+
let tree = fate.roll(dice::tree(dice::just(ExpressionBuilder), node_count));
213+
let max_depth = tree.into_max_depth();
214+
let resolution = 10;
215+
stat!(
216+
"max depth",
217+
"~{}",
218+
(max_depth / resolution) * resolution,
219+
);
220+
})
221+
}
222+
}

0 commit comments

Comments
 (0)