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 ("whole", "yes"), 189 ("drafted", "yes"), 190 ("judged", "yes"), 191 ]); 192 let play = play(&network, &all); 193 let names: Vec<&str> = play.fired.iter().map(|rule| network.terminals[*rule].name).collect(); 194 assert_eq!(names, ["draft options", "judge the drafts"]); 195 assert_eq!(play.next, Next::Done); 196 assert_eq!(says(&network, &play), "Done: show Jev's answers to what the LLM wrote."); 197 // The same, fit to show: it would be listed. 198 let listed: Vec<(String, String)> = all.iter().filter(|(name, _)| name != "fit").cloned().chain(params(&[("fit", "yes")])).collect(); 199 assert!(says(&network, &super::play(&network, &listed)).ends_with("it goes on the feed.")); 200 } 201 202 #[test] 203 fn a_split_reading_shows_both_answers_with_no_llm() { 204 let network = Network::lmjtfy(); 205 let both = params(&[ 206 ("answerable", "yes"), 207 ("several", "no"), 208 ("yes-or-no", "yes"), 209 ("how-much", "yes"), 210 ("scale", "no"), 211 ("yes", "known"), 212 ("degree", "known"), 213 ]); 214 let play = play(&network, &both); 215 assert!(says(&network, &play).starts_with("Ask Jev for pick, how-likely, chance, fit"), "{}", says(&network, &play)); 216 assert_eq!(network.shows(&play.known), [Fact::Yes, Fact::Degree]); 217 } 218 219 #[test] 220 fn a_test_links_to_its_fact_set_and_clears_it_when_it_is_set() { 221 let mut known = Known::default(); 222 known.learn(Fact::Answerable, Value::Bool(true)); 223 let pick = Test::Is(Fact::Reads(Kind::Choice), Value::Bool(true)); 224 assert_eq!(link(&known, Clicked::Test(pick)), "/rules?answerable=yes&pick=yes"); 225 assert_eq!(link(&known, Clicked::Test(Test::Is(Fact::Answerable, Value::Bool(false)))), "/rules?answerable=no"); 226 assert_eq!(link(&known, Clicked::Test(Test::Is(Fact::Answerable, Value::Bool(true)))), "/rules"); 227 assert_eq!(link(&known, Clicked::Test(Test::Known(Fact::Yes))), "/rules?answerable=yes&yes=known"); 228 } 229 230 #[test] 231 fn a_fact_steps_through_what_it_can_be_and_back_to_not_known() { 232 let mut known = Known::default(); 233 assert_eq!(link(&known, Clicked::Fact(Fact::Fit)), "/rules?fit=yes"); 234 known.learn(Fact::Fit, Value::Bool(true)); 235 assert_eq!(link(&known, Clicked::Fact(Fact::Fit)), "/rules?fit=no"); 236 known.learn(Fact::Fit, Value::Bool(false)); 237 assert_eq!(link(&known, Clicked::Fact(Fact::Fit)), "/rules"); 238 assert_eq!(link(&Known::default(), Clicked::Fact(Fact::Yes)), "/rules?yes=known"); 239 } 240}