use std::cell::Cell; use std::cell::Ref; use std::cell::RefCell; use std::cell::RefMut; use crate::slot::Slot; use std::rc::Rc; #[derive(Debug)] enum NodeType { Question, Animal, } #[derive(Debug)] struct NodeStruct { kind: NodeType, text: String, branch_true: Slot, branch_false: Slot, } pub struct AnimalDB { root: Slot, enable_debug: bool, } pub trait UserAPI { fn notify_new_game(&self); fn notify_game_ended(&self); fn notify_new_animal(&self, animal: &str); fn notify_victory(&self); fn answer_yes_no(&self, question: &str) -> bool; fn is_it_a(&self, animal: &str) -> bool; fn what_is_it(&self) -> String; fn how_to_tell_apart(&self, other: &str) -> (String, bool); } impl AnimalDB { pub fn new() -> AnimalDB { AnimalDB { root: Slot::new(), enable_debug: false, } } pub fn enable_debug(&mut self, yes: bool) { self.enable_debug = yes; } fn debug(&self, s: &str) { if self.enable_debug { println!("{}", s); } } fn insert_new_animal( &self, user: &dyn UserAPI, following: Rc, updated_branch: &Slot, ) { let new_name = user.what_is_it(); let (question, answer_for_new) = user.how_to_tell_apart(&following.text); self.debug(&format!( "Updating DB with: ({}, y:{}, n:{})", question, if answer_for_new { &new_name } else { &following.text }, if answer_for_new { &following.text } else { &new_name } )); // we have to insert a new Question node between the parent node and the // Animal node we just found drop(following); // must drop this lease, or .take() will panic let old_animal = Rc::try_unwrap(updated_branch.take()).unwrap(); let mut new_q = NodeStruct { kind: NodeType::Question, text: question, branch_true: Slot::new(), branch_false: Slot::new(), }; let (new_animal_branch, old_animal_branch) = match answer_for_new { true => (&new_q.branch_true, &new_q.branch_false), false => (&new_q.branch_false, &new_q.branch_true), }; user.notify_new_animal(&new_name); let mut new_animal = NodeStruct { kind: NodeType::Animal, text: new_name, branch_true: Slot::new(), branch_false: Slot::new(), }; new_animal_branch.put(new_animal); old_animal_branch.put(old_animal); assert!(updated_branch.is_empty()); updated_branch.put(new_q); } fn play_game(&self, user: &dyn UserAPI) { // question node for the upcoming iteration of the loop let mut next = self.root.lease(); loop { let user_answer; let updated_branch; let following; if let NodeType::Animal = &next.kind { // this is a leaf node (happens only when a leaf is in root). // root is used as a parent reference for inserting a new // in-between question if needed. updated_branch = &self.root; // move from next instead of another lease // we can do this here, because updated_branch is borrowed // from root directly, so there would be no orphaned reference // like in the 'else' branch following = next; } else { user_answer = user.answer_yes_no(&next.text); // find which branch will be updated if this is a new animal updated_branch = match user_answer { true => &next.branch_true, false => &next.branch_false, }; // cannot overwrite next and use it in the rest of the function, // because updated_branch is borrowed from it and the references // would become invalid following = updated_branch.lease(); } if let NodeType::Animal = &following.kind { if user.is_it_a(&following.text) { user.notify_victory(); } else { self.insert_new_animal(user, following, updated_branch); } break; } else { // we found another Question node, proceed with questions. next = following; // move lease } } } //noinspection RsBorrowChecker pub fn start_game(&self, user: &dyn UserAPI) { user.notify_new_game(); if self.root.is_empty() { let name = user.what_is_it(); self.debug(&format!("Initializing empty DB with: ({})", name)); self.root.put(NodeStruct { kind: NodeType::Animal, text: name, branch_true: Slot::new(), branch_false: Slot::new(), }); } else { self.play_game(user); } user.notify_game_ended(); } }