diff --git a/demo/shiritori.js b/demo/shiritori.js index aa917f0..dfd8c28 100644 --- a/demo/shiritori.js +++ b/demo/shiritori.js @@ -25,7 +25,7 @@ ws.onmessage = e => { case "word": let waiting = data.author === id; displayWord(data.word, true); - input.placeholder = waiting ? "Waiting for other players..." : `${data.next_char}…`; + input.placeholder = waiting ? "Waiting for other players..." : `${data.next_mora}…`; input.disabled = waiting; if (!waiting) input.focus(); break; diff --git a/src/database.rs b/src/database.rs index 23328b9..19a3256 100644 --- a/src/database.rs +++ b/src/database.rs @@ -47,7 +47,6 @@ impl Database { } pub fn load_words_before(&self, before_id: i64) -> Result> { - println!("{}", before_id); self.conn .prepare("SELECT id, word, reading, timestamp FROM word WHERE id < ? ORDER BY id DESC LIMIT 10")? .query_map(params![before_id], |row| { diff --git a/src/server.rs b/src/server.rs index 95109b0..c8ba785 100644 --- a/src/server.rs +++ b/src/server.rs @@ -30,7 +30,7 @@ enum MessageResponseData { Word { author: u64, word: Word, - next_char: char, + next_mora: String, }, History { words: Vec, @@ -70,7 +70,7 @@ pub struct Server { event_hub: EventHub, database: Database, clients: HashMap, - next_char: Option, + next_mora: Option, last_client_id: Option, } @@ -85,13 +85,59 @@ pub enum ServerError { DatabaseError(rusqlite::Error), } +fn get_starting_mora(word: &str) -> String { + if word.is_empty() { + return String::from(""); + } + let word = word.to_hiragana(); + let mut iter = word.chars(); + let starting_char = iter.next().unwrap(); + let second_char = iter.next().unwrap(); + match second_char { + 'ゃ' | 'ゅ' | 'ょ' => format!("{starting_char}{second_char}"), + _ => starting_char.to_string(), + } +} + +// Trim off lengtheners and non-kana that are irrelevant to shiritori +// TODO: Use slices +fn trim_irrelevant_chars(word: &str) -> String { + let mut iter = word.chars().rev().peekable(); + while let Some(c) = iter.peek() { + if *c == 'ー' || !c.to_string().is_kana() { + iter.next(); + } else { + break; + } + } + iter.rev().collect() +} + +// Get final mora, which could be multiple chars e.g. しゃ +// TODO: Use slices +fn get_final_mora(word: &str) -> String { + let word = trim_irrelevant_chars(word).to_hiragana(); + if word.is_empty() { + return String::from(""); + } + let mut iter = word.chars().rev(); + let final_char = iter.next().unwrap(); + match final_char { + 'ゃ' | 'ゅ' | 'ょ' => format!("{}{final_char}", match iter.next() { + Some(c) => c.to_string(), + None => String::from(""), + }), + _ => final_char.to_string(), + } +} + impl Server { pub fn new(port: u16, testing: bool) -> Result { Ok(Server { event_hub: simple_websockets::launch(port)?, database: Database::new(testing)?, clients: HashMap::new(), - next_char: None, + next_mora: None, last_client_id: None, }) } @@ -153,32 +199,23 @@ impl Server { } else { match lookup(&message) { Some(entry) => { + println!("{}, {:?}", get_starting_mora(&entry.reading), self.next_mora); if entry.reading.chars().last().unwrap().to_string().to_hiragana() == "ん" { MessageResponseData::Error { message: String::from("Can't end with ん!"), } - } else if self.next_char.is_none() || entry.reading.chars().next().unwrap().to_string().to_hiragana().chars().next().unwrap() == self.next_char.unwrap() { - self.next_char = { - // If final character is lengthener or not kana - // Use semifinal - let mut final_chars = entry.reading.chars().rev(); - let final_char = final_chars.next().unwrap(); - Some(if final_char == 'ー' || !final_char.to_string().is_kana() { - final_chars.next().unwrap() - } else { - final_char - }.to_string().to_hiragana().chars().next().unwrap()) - }; + } else if self.next_mora.is_none() || get_starting_mora(&entry.reading).eq(self.next_mora.as_deref().unwrap()) { let word: Word = entry.clone().into(); + self.next_mora = Some(get_final_mora(&word.reading)); self.database.add_word(&word)?; MessageResponseData::Word { author: client_id, word, - next_char: self.next_char.unwrap(), + next_mora: self.next_mora.as_deref().unwrap().to_owned(), } } else { MessageResponseData::Error { - message: String::from("Wrong starting kana!"), + message: String::from("Wrong starting mora!"), } } }