The rules with facts you set by hand: /rules?answerable=yes&kind=choice.
Pure, and it asks nobody anything: the facts come from the link.
Every test on the diagram is a link that sets its fact to the value it tests, or clears it if it is already that. Every fact is a link that steps through what it can be: not known, then each value in turn. So the page holds no state; the link is the state.
9use rules::{Effect, End, Fact, Known, Network, Next, Note, Source, Test, Value, Want};
The facts the link set.
15 pub known: Known,
The rules that would have decided a step, in order.
17 pub fired: Vec<usize>,
The value a test makes its fact, when it holds.
Reads the link's parameters. Only a value its fact can have is taken; the rest of the link is ignored.
32pub fn play(network: &Network, params: &[(String, String)]) -> Play { 33 let mut known = Known::default(); 34 for (name, label) in params { 35 let set = Fact::ALL 36 .into_iter() 37 .filter(|fact| fact.name() == name) 38 .flat_map(|fact| fact.values().iter().map(move |value| (fact, *value))) 39 .find(|(_, value)| value.label() == label); 40 if let Some((fact, value)) = set { 41 known.learn(fact, value); 42 } 43 } 44 // The order the rules would have decided in: from what Jev says, each 45 // effect teaching its fact as the link says it went. 46 let mut so_far = Known::default(); 47 for fact in Fact::ALL.into_iter().filter(|fact| fact.source() == Source::Jev) { 48 if let Some(value) = known.get(fact) { 49 so_far.learn(fact, value); 50 } 51 } 52 let mut fired = Vec::new(); 53 loop { 54 let (by, next) = network.decide(&so_far); 55 fired.extend(by); 56 match next { 57 Next::Do(effect) => match known.get(effect.teaches()) { 58 Some(value) => so_far.learn(effect.teaches(), value), 59 None => break, 60 }, 61 _ => break, 62 } 63 } 64 let next = network.next(&known); 65 Play { known, fired, next } 66}
Where a click leads. A test sets its fact to what it tests, or clears the fact if it is that already. A fact steps to the next thing it can be, and from the last back to not known.
79pub fn link(known: &Known, clicked: Clicked) -> String { 80 let mut after = known.clone(); 81 match clicked { 82 Clicked::Test(test) => { 83 let (fact, value) = sets(test); 84 if known.get(fact) == Some(value) { 85 after.forget(fact); 86 } else { 87 after.learn(fact, value); 88 } 89 } 90 Clicked::Fact(fact) => { 91 let values = fact.values(); 92 let next = match known.get(fact) { 93 None => values.first(), 94 Some(now) => values.iter().skip_while(|value| **value != now).nth(1), 95 }; 96 match next { 97 Some(value) => after.learn(fact, *value), 98 None => after.forget(fact), 99 } 100 } 101 } 102 href(&after) 103}
What the rules say to do now, in words.
106pub fn says(network: &Network, play: &Play) -> String { 107 let said = match &play.next { 108 Next::Ask(facts) => { 109 let names: Vec<&str> = facts.iter().map(|fact| fact.name()).collect(); 110 format!("Ask Jev for {}, in one request.", names.join(", ")) 111 } 112 Next::Do(Effect::Draft(_)) => { 113 let wants: Vec<&str> = network 114 .wants(&play.known) 115 .into_iter() 116 .map(|want| match want { 117 Want::Options => "the options", 118 Want::Scale => "a scale", 119 Want::Split => "each of the questions", 120 }) 121 .collect(); 122 format!("Ask the LLM to write {}, in one call. Set drafted to say how that went.", wants.join(" and ")) 123 } 124 Next::Do(Effect::Judge) => "Ask Jev what the LLM wrote, in one request. Set judged to say how that went.".to_owned(), 125 Next::End(End::NotAQuestion) => "Done: Jev says no, it can't answer that.".to_owned(), 126 Next::Done => { 127 let shows: Vec<&str> = network 128 .shows(&play.known) 129 .into_iter() 130 .map(|fact| match fact { 131 Fact::Yes => "Jev's yes or no", 132 Fact::Degree => "Jev's degree", 133 Fact::Chance => "Jev's probability", 134 _ => "Jev's answers to what the LLM wrote", 135 }) 136 .collect(); 137 if shows.is_empty() { "Done: there is nothing to show.".to_owned() } else { format!("Done: show {}.", shows.join(" and ")) } 138 } 139 }; 140 if network.notes(&play.known, Note::List) { format!("{said} If it is answered, it goes on the feed.") } else { said } 141}
143#[cfg(test)] 144mod tests { 145 use rules::Kind; 146 147 use super::*; 148 149 fn params(pairs: &[(&str, &str)]) -> Vec<(String, String)> { 150 pairs.iter().map(|(name, value)| ((*name).to_owned(), (*value).to_owned())).collect() 151 } 152 153 #[test] 154 fn an_empty_link_knows_nothing_and_asks_jev_for_everything() { 155 let network = Network::lmjtfy(); 156 let play = play(&network, &[]); 157 assert_eq!(play.known, Known::default()); 158 assert!(play.fired.is_empty()); 159 assert!(says(&network, &play).starts_with("Ask Jev for answerable, several, yes-or-no, pick, how-much, how-likely, scale")); 160 } 161 162 #[test] 163 fn a_link_sets_only_what_a_rule_tests() { 164 let network = Network::lmjtfy(); 165 let play = play(&network, ¶ms(&[("pick", "yes"), ("fit", "purple"), ("nonsense", "yes"), ("yes", "known"), ("degree", "yes")])); 166 assert_eq!(play.known.get(Fact::Fit), None); 167 assert_eq!(play.known.get(Fact::Degree), None); 168 assert_eq!(play.known.get(Fact::Reads(Kind::Choice)), Some(Value::Bool(true))); 169 assert_eq!(play.known.get(Fact::Yes), Some(Value::Given)); 170 assert_eq!(play.known.get(Fact::Answerable), None); 171 } 172 173 #[test] 174 fn the_whole_run_is_replayed_in_order() { 175 let network = Network::lmjtfy(); 176 let all = params(&[ 177 ("answerable", "yes"), 178 ("several", "no"), 179 ("yes-or-no", "no"), 180 ("pick", "yes"), 181 ("how-much", "no"), 182 ("how-likely", "no"), 183 ("scale", "no"), 184 ("yes", "known"), 185 ("degree", "known"), 186 ("chance", "known"), 187 ("fit", "no"), 188 ("drafted", "yes"), 189 ("judged", "yes"), 190 ]); 191 let play = play(&network, &all); 192 let names: Vec<&str> = play.fired.iter().map(|rule| network.terminals[*rule].name).collect(); 193 assert_eq!(names, ["draft options", "judge the drafts"]); 194 assert_eq!(play.next, Next::Done); 195 assert_eq!(says(&network, &play), "Done: show Jev's answers to what the LLM wrote."); 196 // The same, fit to show: it would be listed. 197 let listed: Vec<(String, String)> = all.iter().filter(|(name, _)| name != "fit").cloned().chain(params(&[("fit", "yes")])).collect(); 198 assert!(says(&network, &super::play(&network, &listed)).ends_with("it goes on the feed.")); 199 } 200 201 #[test] 202 fn a_split_reading_shows_both_answers_with_no_llm() { 203 let network = Network::lmjtfy(); 204 let both = params(&[ 205 ("answerable", "yes"), 206 ("several", "no"), 207 ("yes-or-no", "yes"), 208 ("how-much", "yes"), 209 ("scale", "no"), 210 ("yes", "known"), 211 ("degree", "known"), 212 ]); 213 let play = play(&network, &both); 214 assert!(says(&network, &play).starts_with("Ask Jev for pick, how-likely, chance, fit"), "{}", says(&network, &play)); 215 assert_eq!(network.shows(&play.known), [Fact::Yes, Fact::Degree]); 216 } 217 218 #[test] 219 fn a_test_links_to_its_fact_set_and_clears_it_when_it_is_set() { 220 let mut known = Known::default(); 221 known.learn(Fact::Answerable, Value::Bool(true)); 222 let pick = Test::Is(Fact::Reads(Kind::Choice), Value::Bool(true)); 223 assert_eq!(link(&known, Clicked::Test(pick)), "/rules?answerable=yes&pick=yes"); 224 assert_eq!(link(&known, Clicked::Test(Test::Is(Fact::Answerable, Value::Bool(false)))), "/rules?answerable=no"); 225 assert_eq!(link(&known, Clicked::Test(Test::Is(Fact::Answerable, Value::Bool(true)))), "/rules"); 226 assert_eq!(link(&known, Clicked::Test(Test::Known(Fact::Yes))), "/rules?answerable=yes&yes=known"); 227 } 228 229 #[test] 230 fn a_fact_steps_through_what_it_can_be_and_back_to_not_known() { 231 let mut known = Known::default(); 232 assert_eq!(link(&known, Clicked::Fact(Fact::Fit)), "/rules?fit=yes"); 233 known.learn(Fact::Fit, Value::Bool(true)); 234 assert_eq!(link(&known, Clicked::Fact(Fact::Fit)), "/rules?fit=no"); 235 known.learn(Fact::Fit, Value::Bool(false)); 236 assert_eq!(link(&known, Clicked::Fact(Fact::Fit)), "/rules"); 237 assert_eq!(link(&Known::default(), Clicked::Fact(Fact::Yes)), "/rules?yes=known"); 238 } 239}