From 90086ab3498d3b877a77da8554a9466fa6fce3cc Mon Sep 17 00:00:00 2001 From: Andy Kurnia Date: Sun, 10 Mar 2024 20:10:18 +0800 Subject: [PATCH] update wolges to c67ac98d30f8549296a19f5fac6fb593303e0148 --- src/alphabet.rs | 500 ++----------- src/alphabets/catalan.txt | 27 + src/alphabets/english.txt | 27 + src/alphabets/french.txt | 27 + src/alphabets/german.txt | 30 + src/alphabets/hong_kong_english.txt | 27 + src/alphabets/norwegian.txt | 33 + src/alphabets/polish.txt | 33 + src/alphabets/slovene.txt | 36 + src/alphabets/spanish.txt | 29 + src/alphabets/super_catalan.txt | 27 + src/alphabets/super_english.txt | 27 + src/alphabets/yupik.txt | 19 + src/board_layout.rs | 1069 ++++++++++++++++++++++++--- src/display.rs | 2 +- src/game_state.rs | 4 + src/matrix.rs | 4 +- src/movegen.rs | 182 +++-- src/play_scorer.rs | 14 +- 19 files changed, 1502 insertions(+), 615 deletions(-) create mode 100644 src/alphabets/catalan.txt create mode 100644 src/alphabets/english.txt create mode 100644 src/alphabets/french.txt create mode 100644 src/alphabets/german.txt create mode 100644 src/alphabets/hong_kong_english.txt create mode 100644 src/alphabets/norwegian.txt create mode 100644 src/alphabets/polish.txt create mode 100644 src/alphabets/slovene.txt create mode 100644 src/alphabets/spanish.txt create mode 100644 src/alphabets/super_catalan.txt create mode 100644 src/alphabets/super_english.txt create mode 100644 src/alphabets/yupik.txt diff --git a/src/alphabet.rs b/src/alphabet.rs index dd4aa39..51030f2 100644 --- a/src/alphabet.rs +++ b/src/alphabet.rs @@ -1,6 +1,8 @@ // Copyright (C) 2020-2024 Andy Kurnia. -use super::{bites, bites_str}; +use super::{bites, bites_str, error}; + +use std::str::FromStr; struct Tile { label: bites_str::BitesStr, @@ -27,17 +29,17 @@ pub enum Alphabet { } impl Alphabet { - pub fn new_static(x: StaticAlphabet) -> Self { - let num_letters = x.tiles.len() as u8; + fn new_static(tiles: Vec) -> Self { + let num_letters = tiles.len() as u8; let mut same_score_tile = Box::from_iter(0..num_letters); let mut same_score_tile_bits = Vec::with_capacity(num_letters as usize); // sameness is defined only by same scores (is_vowel may mismatch). for i in 0..num_letters { if same_score_tile[i as usize] == i { let mut b = 1u64 << i; - let v = x.tiles[i as usize].score; + let v = tiles[i as usize].score; for j in i + 1..num_letters { - if x.tiles[j as usize].score == v { + if tiles[j as usize].score == v { same_score_tile[j as usize] = i; b |= 1u64 << j; } @@ -59,24 +61,65 @@ impl Alphabet { } let mut tiles_by_descending_scores = Box::from_iter(0..num_letters); tiles_by_descending_scores.sort_unstable_by(|&a, &b| { - x.tiles[b as usize] + tiles[b as usize] .score - .cmp(&x.tiles[a as usize].score) + .cmp(&tiles[a as usize].score) .then(a.cmp(&b)) }); Self::Static(StaticAlphabet { - widest_label_len: x.tiles.iter().fold(0, |acc, tile| { + widest_label_len: tiles.iter().fold(0, |acc, tile| { acc.max(tile.label.chars().count()) .max(tile.blank_label.chars().count()) }), - num_tiles: x.tiles.iter().map(|tile| tile.freq as u16).sum(), + num_tiles: tiles.iter().map(|tile| tile.freq as u16).sum(), same_score_tile, same_score_tile_bits: same_score_tile_bits.into_boxed_slice(), tiles_by_descending_scores, - ..x + tiles, }) } + fn new_static_from_text(s: &str) -> error::Returns { + let mut tiles = Vec::new(); + for line_str in s.lines() { + let mut tokens = line_str.split_whitespace(); + // there is minimal error handling... + let label = tokens.next().ok_or("not enough tokens")?.into(); + let blank_label = tokens.next().ok_or("not enough tokens")?.into(); + let freq = u8::from_str(tokens.next().ok_or("not enough tokens")?)?; + let score = i8::from_str(tokens.next().ok_or("not enough tokens")?)?; + let is_vowel = match u8::from_str(tokens.next().ok_or("not enough tokens")?)? { + 0 => false, + 1 => true, + _ => Err("invalid bool")?, + }; + let num_alias_labels = usize::from_str(tokens.next().ok_or("not enough tokens")?)?; + let mut alias_labels = Vec::with_capacity(num_alias_labels); + for _ in 0..num_alias_labels { + alias_labels.push(tokens.next().ok_or("not enough tokens")?.into()); + } + let num_alias_blank_labels = + usize::from_str(tokens.next().ok_or("not enough tokens")?)?; + let mut alias_blank_labels = Vec::with_capacity(num_alias_blank_labels); + for _ in 0..num_alias_blank_labels { + alias_blank_labels.push(tokens.next().ok_or("not enough tokens")?.into()); + } + if tokens.next().is_some() { + return Err("too many tokens".into()); + } + tiles.push(Tile { + label, + blank_label, + freq, + score, + is_vowel, + alias_labels, + alias_blank_labels, + }); + } + Ok(Self::new_static(tiles)) + } + #[inline(always)] pub fn len(&self) -> u8 { match self { @@ -210,282 +253,48 @@ impl std::fmt::Display for WriteableRack<'_> { } } -macro_rules! v { - ($($item: expr),*) => { vec![$($item.into(), )*] }; -} - -macro_rules! tile { - ($label: expr, $blank_label: expr, $freq: expr, $score: expr, $vowel_int: expr) => { - tile!($label, $blank_label, $freq, $score, $vowel_int, v![], v![]) - }; - ($label: expr, $blank_label: expr, $freq: expr, $score: expr, $vowel_int: expr, $alias_labels: expr, $alias_blank_labels: expr) => { - Tile { - label: $label.into(), - blank_label: $blank_label.into(), - freq: $freq, - score: $score, - is_vowel: ($vowel_int) != 0, - alias_labels: $alias_labels, - alias_blank_labels: $alias_blank_labels, - } +macro_rules! new_static_alphabet_from_file { + ($filename: expr) => { + Alphabet::new_static_from_text(include_str!($filename)).unwrap() }; } // https://en.wikipedia.org/wiki/Scrabble_letter_distributions#Catalan // with QU tile instead of Q pub fn make_catalan_alphabet() -> Alphabet { - Alphabet::new_static(StaticAlphabet { - tiles: vec![ - tile!("?", "?", 2, 0, 0), - tile!("A", "a", 12, 1, 1), - tile!("B", "b", 2, 3, 0), - tile!("C", "c", 3, 2, 0), - tile!("Ç", "ç", 1, 10, 0, v!["K"], v!["k"]), - tile!("D", "d", 3, 2, 0), - tile!("E", "e", 13, 1, 1), - tile!("F", "f", 1, 4, 0), - tile!("G", "g", 2, 3, 0), - tile!("H", "h", 1, 8, 0), - tile!("I", "i", 8, 1, 1), - tile!("J", "j", 1, 8, 0), - tile!("L", "l", 4, 1, 0), - tile!("L·L", "l·l", 1, 10, 0, v!["W"], v!["w"]), - tile!("M", "m", 3, 2, 0), - tile!("N", "n", 6, 1, 0), - tile!("NY", "ny", 1, 10, 0, v!["Y"], v!["y"]), - tile!("O", "o", 5, 1, 1), - tile!("P", "p", 2, 3, 0), - tile!("QU", "qu", 1, 8, 0, v!["Q"], v!["q"]), - tile!("R", "r", 8, 1, 0), - tile!("S", "s", 8, 1, 0), - tile!("T", "t", 5, 1, 0), - tile!("U", "u", 4, 1, 1), - tile!("V", "v", 1, 4, 0), - tile!("X", "x", 1, 10, 0), - tile!("Z", "z", 1, 8, 0), - ], - ..Default::default() - }) + new_static_alphabet_from_file!("alphabets/catalan.txt") } // https://en.wikipedia.org/wiki/Scrabble_letter_distributions#Catalan +// note: Ç and L·L have different scores from regular. pub fn make_super_catalan_alphabet() -> Alphabet { - Alphabet::new_static(StaticAlphabet { - tiles: vec![ - tile!("?", "?", 5, 0, 0), - tile!("A", "a", 25, 1, 1), - tile!("B", "b", 3, 3, 0), - tile!("C", "c", 5, 2, 0), - tile!("Ç", "ç", 2, 12, 0, v!["K"], v!["k"]), // note: different score from regular - tile!("D", "d", 5, 2, 0), - tile!("E", "e", 27, 1, 1), - tile!("F", "f", 2, 4, 0), - tile!("G", "g", 3, 3, 0), - tile!("H", "h", 2, 8, 0), - tile!("I", "i", 17, 1, 1), - tile!("J", "j", 2, 8, 0), - tile!("L", "l", 8, 1, 0), - tile!("L·L", "l·l", 1, 15, 0, v!["W"], v!["w"]), // note: different score from regular - tile!("M", "m", 7, 2, 0), - tile!("N", "n", 12, 1, 0), - tile!("NY", "ny", 2, 10, 0, v!["Y"], v!["y"]), - tile!("O", "o", 10, 1, 1), - tile!("P", "p", 3, 3, 0), - tile!("QU", "qu", 2, 8, 0, v!["Q"], v!["q"]), - tile!("R", "r", 16, 1, 0), - tile!("S", "s", 19, 1, 0), - tile!("T", "t", 10, 1, 0), - tile!("U", "u", 6, 1, 1), - tile!("V", "v", 2, 4, 0), - tile!("X", "x", 2, 10, 0), - tile!("Z", "z", 2, 8, 0), - ], - ..Default::default() - }) + new_static_alphabet_from_file!("alphabets/super_catalan.txt") } // https://en.wikipedia.org/wiki/Scrabble_letter_distributions#English pub fn make_english_alphabet() -> Alphabet { - Alphabet::new_static(StaticAlphabet { - tiles: vec![ - tile!("?", "?", 2, 0, 0), - tile!("A", "a", 9, 1, 1), - tile!("B", "b", 2, 3, 0), - tile!("C", "c", 2, 3, 0), - tile!("D", "d", 4, 2, 0), - tile!("E", "e", 12, 1, 1), - tile!("F", "f", 2, 4, 0), - tile!("G", "g", 3, 2, 0), - tile!("H", "h", 2, 4, 0), - tile!("I", "i", 9, 1, 1), - tile!("J", "j", 1, 8, 0), - tile!("K", "k", 1, 5, 0), - tile!("L", "l", 4, 1, 0), - tile!("M", "m", 2, 3, 0), - tile!("N", "n", 6, 1, 0), - tile!("O", "o", 8, 1, 1), - tile!("P", "p", 2, 3, 0), - tile!("Q", "q", 1, 10, 0), - tile!("R", "r", 6, 1, 0), - tile!("S", "s", 4, 1, 0), - tile!("T", "t", 6, 1, 0), - tile!("U", "u", 4, 1, 1), - tile!("V", "v", 2, 4, 0), - tile!("W", "w", 2, 4, 0), - tile!("X", "x", 1, 8, 0), - tile!("Y", "y", 2, 4, 0), - tile!("Z", "z", 1, 10, 0), - ], - ..Default::default() - }) + new_static_alphabet_from_file!("alphabets/english.txt") } // https://en.wikipedia.org/wiki/Scrabble_letter_distributions#French // https://en.wikipedia.org/wiki/French_orthography pub fn make_french_alphabet() -> Alphabet { - Alphabet::new_static(StaticAlphabet { - tiles: vec![ - tile!("?", "?", 2, 0, 0), - tile!("A", "a", 9, 1, 1), - tile!("B", "b", 2, 3, 0), - tile!("C", "c", 2, 3, 0), - tile!("D", "d", 3, 2, 0), - tile!("E", "e", 15, 1, 1), - tile!("F", "f", 2, 4, 0), - tile!("G", "g", 2, 2, 0), - tile!("H", "h", 2, 4, 0), - tile!("I", "i", 8, 1, 1), - tile!("J", "j", 1, 8, 0), - tile!("K", "k", 1, 10, 0), - tile!("L", "l", 5, 1, 0), - tile!("M", "m", 3, 2, 0), - tile!("N", "n", 6, 1, 0), - tile!("O", "o", 6, 1, 1), - tile!("P", "p", 2, 3, 0), - tile!("Q", "q", 1, 8, 0), - tile!("R", "r", 6, 1, 0), - tile!("S", "s", 6, 1, 0), - tile!("T", "t", 6, 1, 0), - tile!("U", "u", 6, 1, 1), - tile!("V", "v", 2, 4, 0), - tile!("W", "w", 1, 10, 0), - tile!("X", "x", 1, 10, 0), - tile!("Y", "y", 1, 10, 1), - tile!("Z", "z", 1, 10, 0), - ], - ..Default::default() - }) + new_static_alphabet_from_file!("alphabets/french.txt") } // http://hkcrosswordclub.com/?cat=14 pub fn make_hong_kong_english_alphabet() -> Alphabet { - Alphabet::new_static(StaticAlphabet { - tiles: vec![ - tile!("?", "?", 4, 0, 0), - tile!("A", "a", 9, 1, 1), - tile!("B", "b", 2, 3, 0), - tile!("C", "c", 2, 3, 0), - tile!("D", "d", 4, 2, 0), - tile!("E", "e", 12, 1, 1), - tile!("F", "f", 2, 4, 0), - tile!("G", "g", 3, 2, 0), - tile!("H", "h", 2, 4, 0), - tile!("I", "i", 9, 1, 1), - tile!("J", "j", 2, 8, 0), - tile!("K", "k", 2, 5, 0), - tile!("L", "l", 4, 1, 0), - tile!("M", "m", 2, 3, 0), - tile!("N", "n", 6, 1, 0), - tile!("O", "o", 8, 1, 1), - tile!("P", "p", 2, 3, 0), - tile!("Q", "q", 2, 10, 0), - tile!("R", "r", 6, 1, 0), - tile!("S", "s", 4, 1, 0), - tile!("T", "t", 6, 1, 0), - tile!("U", "u", 4, 1, 1), - tile!("V", "v", 2, 4, 0), - tile!("W", "w", 2, 4, 0), - tile!("X", "x", 2, 8, 0), - tile!("Y", "y", 2, 4, 0), - tile!("Z", "z", 2, 10, 0), - ], - ..Default::default() - }) + new_static_alphabet_from_file!("alphabets/hong_kong_english.txt") } // https://en.wikipedia.org/wiki/Super_Scrabble pub fn make_super_english_alphabet() -> Alphabet { - Alphabet::new_static(StaticAlphabet { - tiles: vec![ - tile!("?", "?", 4, 0, 0), - tile!("A", "a", 16, 1, 1), - tile!("B", "b", 4, 3, 0), - tile!("C", "c", 6, 3, 0), - tile!("D", "d", 8, 2, 0), - tile!("E", "e", 24, 1, 1), - tile!("F", "f", 4, 4, 0), - tile!("G", "g", 5, 2, 0), - tile!("H", "h", 5, 4, 0), - tile!("I", "i", 13, 1, 1), - tile!("J", "j", 2, 8, 0), - tile!("K", "k", 2, 5, 0), - tile!("L", "l", 7, 1, 0), - tile!("M", "m", 6, 3, 0), - tile!("N", "n", 13, 1, 0), - tile!("O", "o", 15, 1, 1), - tile!("P", "p", 4, 3, 0), - tile!("Q", "q", 2, 10, 0), - tile!("R", "r", 13, 1, 0), - tile!("S", "s", 10, 1, 0), - tile!("T", "t", 15, 1, 0), - tile!("U", "u", 7, 1, 1), - tile!("V", "v", 3, 4, 0), - tile!("W", "w", 4, 4, 0), - tile!("X", "x", 2, 8, 0), - tile!("Y", "y", 4, 4, 0), - tile!("Z", "z", 2, 10, 0), - ], - ..Default::default() - }) + new_static_alphabet_from_file!("alphabets/super_english.txt") } // https://en.wikipedia.org/wiki/Scrabble_letter_distributions#German pub fn make_german_alphabet() -> Alphabet { - Alphabet::new_static(StaticAlphabet { - tiles: vec![ - tile!("?", "?", 2, 0, 0), - tile!("A", "a", 5, 1, 1), - tile!("Ä", "ä", 1, 6, 1), - tile!("B", "b", 2, 3, 0), - tile!("C", "c", 2, 4, 0), - tile!("D", "d", 4, 1, 0), - tile!("E", "e", 15, 1, 1), - tile!("F", "f", 2, 4, 0), - tile!("G", "g", 3, 2, 0), - tile!("H", "h", 4, 2, 0), - tile!("I", "i", 6, 1, 1), - tile!("J", "j", 1, 6, 0), - tile!("K", "k", 2, 4, 0), - tile!("L", "l", 3, 2, 0), - tile!("M", "m", 4, 3, 0), - tile!("N", "n", 9, 1, 0), - tile!("O", "o", 3, 2, 1), - tile!("Ö", "ö", 1, 8, 1), - tile!("P", "p", 1, 4, 0), - tile!("Q", "q", 1, 10, 0), - tile!("R", "r", 6, 1, 0), - tile!("S", "s", 7, 1, 0), - tile!("T", "t", 6, 1, 0), - tile!("U", "u", 6, 1, 1), - tile!("Ü", "ü", 1, 6, 1), - tile!("V", "v", 1, 6, 0), - tile!("W", "w", 1, 3, 0), - tile!("X", "x", 1, 8, 0), - tile!("Y", "y", 1, 10, 0), - tile!("Z", "z", 1, 3, 0), - ], - ..Default::default() - }) + new_static_alphabet_from_file!("alphabets/german.txt") } // https://en.wikipedia.org/wiki/Scrabble_letter_distributions#Norwegian @@ -493,204 +302,35 @@ pub fn make_german_alphabet() -> Alphabet { // https://unicode.org/mail-arch/unicode-ml/y2002-m01/0297.html // also this ordering matches system locale files pub fn make_norwegian_alphabet() -> Alphabet { - Alphabet::new_static(StaticAlphabet { - tiles: vec![ - tile!("?", "?", 2, 0, 0), - tile!("A", "a", 7, 1, 1), - tile!("B", "b", 3, 4, 0), - tile!("C", "c", 1, 10, 0), - tile!("D", "d", 5, 1, 0), - tile!("E", "e", 9, 1, 1), - tile!("F", "f", 4, 2, 0), - tile!("G", "g", 4, 2, 0), - tile!("H", "h", 3, 3, 0), - tile!("I", "i", 5, 1, 1), - tile!("J", "j", 2, 4, 0), - tile!("K", "k", 4, 2, 0), - tile!("L", "l", 5, 1, 0), - tile!("M", "m", 3, 2, 0), - tile!("N", "n", 6, 1, 0), - tile!("O", "o", 4, 2, 1), - tile!("P", "p", 2, 4, 0), - tile!("Q", "q", 0, 0, 0), - tile!("R", "r", 6, 1, 0), - tile!("S", "s", 6, 1, 0), - tile!("T", "t", 6, 1, 0), - tile!("U", "u", 3, 4, 1), - tile!("V", "v", 3, 4, 0), - tile!("W", "w", 1, 8, 0), - tile!("X", "x", 0, 0, 0), - tile!("Y", "y", 1, 6, 1), - tile!("Ü", "ü", 0, 0, 1), - tile!("Z", "z", 0, 0, 0), - tile!("Æ", "æ", 1, 6, 1), - tile!("Ä", "ä", 0, 0, 1), - tile!("Ø", "ø", 2, 5, 1), - tile!("Ö", "ö", 0, 0, 1), - tile!("Å", "å", 2, 4, 1), - ], - ..Default::default() - }) + new_static_alphabet_from_file!("alphabets/norwegian.txt") } // https://en.wikipedia.org/wiki/Scrabble_letter_distributions#Polish // https://en.wikipedia.org/wiki/Polish_alphabet#Letters // https://en.wikipedia.org/wiki/Polish_phonology#Vowels pub fn make_polish_alphabet() -> Alphabet { - Alphabet::new_static(StaticAlphabet { - tiles: vec![ - tile!("?", "?", 2, 0, 0), - tile!("A", "a", 9, 1, 1), - tile!("Ą", "ą", 1, 5, 1), - tile!("B", "b", 2, 3, 0), - tile!("C", "c", 3, 2, 0), - tile!("Ć", "ć", 1, 6, 0), - tile!("D", "d", 3, 2, 0), - tile!("E", "e", 7, 1, 1), - tile!("Ę", "ę", 1, 5, 1), - tile!("F", "f", 1, 5, 0), - tile!("G", "g", 2, 3, 0), - tile!("H", "h", 2, 3, 0), - tile!("I", "i", 8, 1, 1), - tile!("J", "j", 2, 3, 0), - tile!("K", "k", 3, 2, 0), - tile!("L", "l", 3, 2, 0), - tile!("Ł", "ł", 2, 3, 0), - tile!("M", "m", 3, 2, 0), - tile!("N", "n", 5, 1, 0), - tile!("Ń", "ń", 1, 7, 0), - tile!("O", "o", 6, 1, 1), - tile!("Ó", "ó", 1, 5, 1), - tile!("P", "p", 3, 2, 0), - tile!("R", "r", 4, 1, 0), - tile!("S", "s", 4, 1, 0), - tile!("Ś", "ś", 1, 5, 0), - tile!("T", "t", 3, 2, 0), - tile!("U", "u", 2, 3, 1), - tile!("W", "w", 4, 1, 0), - tile!("Y", "y", 4, 2, 1), - tile!("Z", "z", 5, 1, 0), - tile!("Ź", "ź", 1, 9, 0), - tile!("Ż", "ż", 1, 5, 0), - ], - ..Default::default() - }) + new_static_alphabet_from_file!("alphabets/polish.txt") } // https://en.wikipedia.org/wiki/Scrabble_letter_distributions#Slovenian // the additional letters are unofficial and experimental // (so data files may not be stable). +// note: Å Ä Ç Ñ Ö Q Ü W X Y are experimental. pub fn make_slovene_alphabet() -> Alphabet { - Alphabet::new_static(StaticAlphabet { - tiles: vec![ - tile!("?", "?", 2, 0, 0), - tile!("A", "a", 10, 1, 1), - tile!("Å", "å", 0, 0, 1), // ? - tile!("Ä", "ä", 0, 0, 1), // ? - tile!("B", "b", 2, 4, 0), - tile!("C", "c", 1, 8, 0), - tile!("Ç", "ç", 0, 0, 0), // ? - tile!("Č", "č", 1, 5, 0), - tile!("D", "d", 4, 2, 0), - tile!("E", "e", 11, 1, 1), - tile!("F", "f", 1, 10, 0), - tile!("G", "g", 2, 4, 0), - tile!("H", "h", 1, 5, 0), - tile!("I", "i", 9, 1, 1), - tile!("J", "j", 4, 1, 0), - tile!("K", "k", 3, 3, 0), - tile!("L", "l", 4, 1, 0), - tile!("M", "m", 2, 3, 0), - tile!("N", "n", 7, 1, 0), - tile!("Ñ", "ñ", 0, 0, 0), // ? - tile!("O", "o", 8, 1, 1), - tile!("Ö", "ö", 0, 0, 1), // ? - tile!("P", "p", 2, 3, 0), - tile!("Q", "q", 0, 0, 0), // ? - tile!("R", "r", 6, 1, 0), - tile!("S", "s", 6, 1, 0), - tile!("Š", "š", 1, 6, 0), - tile!("T", "t", 4, 1, 0), - tile!("U", "u", 2, 3, 1), - tile!("Ü", "ü", 0, 0, 1), // ? - tile!("V", "v", 4, 2, 0), - tile!("W", "w", 0, 0, 0), // ? - tile!("X", "x", 0, 0, 0), // ? - tile!("Y", "y", 0, 0, 0), // ? - tile!("Z", "z", 2, 4, 0), - tile!("Ž", "ž", 1, 10, 0), - ], - ..Default::default() - }) + new_static_alphabet_from_file!("alphabets/slovene.txt") } // https://en.wikipedia.org/wiki/Scrabble_letter_distributions#Spanish // based on Spanish-language sets sold outside North America // (CH/LL/RR are ambiguous and should not be supported) pub fn make_spanish_alphabet() -> Alphabet { - Alphabet::new_static(StaticAlphabet { - tiles: vec![ - tile!("?", "?", 2, 0, 0), - tile!("A", "a", 12, 1, 1), - tile!("B", "b", 2, 3, 0), - tile!("C", "c", 4, 3, 0), - tile!("[CH]", "[ch]", 1, 5, 0, v!["1"], v![]), - tile!("D", "d", 5, 2, 0), - tile!("E", "e", 12, 1, 1), - tile!("F", "f", 1, 4, 0), - tile!("G", "g", 2, 2, 0), - tile!("H", "h", 2, 4, 0), - tile!("I", "i", 6, 1, 1), - tile!("J", "j", 1, 8, 0), - tile!("L", "l", 4, 1, 0), - tile!("[LL]", "[ll]", 1, 8, 0, v!["2"], v![]), - tile!("M", "m", 2, 3, 0), - tile!("N", "n", 5, 1, 0), - tile!("Ñ", "ñ", 1, 8, 0), - tile!("O", "o", 9, 1, 1), - tile!("P", "p", 2, 3, 0), - tile!("Q", "q", 1, 5, 0), - tile!("R", "r", 5, 1, 0), - tile!("[RR]", "[rr]", 1, 8, 0, v!["3"], v![]), - tile!("S", "s", 6, 1, 0), - tile!("T", "t", 4, 1, 0), - tile!("U", "u", 5, 1, 1), - tile!("V", "v", 1, 4, 0), - tile!("X", "x", 1, 8, 0), - tile!("Y", "y", 1, 4, 0), - tile!("Z", "z", 1, 10, 0), - ], - ..Default::default() - }) + new_static_alphabet_from_file!("alphabets/spanish.txt") } // TODO: find citeable source // https://discord.com/channels/741321677828522035/778469677588283403/1171937313224392704 pub fn make_yupik_alphabet() -> Alphabet { - Alphabet::new_static(StaticAlphabet { - tiles: vec![ - tile!("?", "?", 2, 0, 0), - tile!("A", "a", 17, 1, 1), - tile!("C", "c", 2, 6, 0), - tile!("E", "e", 6, 1, 1), - tile!("G", "g", 5, 2, 0), - tile!("I", "i", 9, 1, 1), - tile!("K", "k", 5, 2, 0), - tile!("L", "l", 8, 1, 0), - tile!("M", "m", 4, 4, 0), - tile!("N", "n", 8, 1, 0), - tile!("P", "p", 1, 8, 0), - tile!("Q", "q", 4, 4, 0), - tile!("R", "r", 6, 1, 0), - tile!("S", "s", 1, 8, 0), - tile!("T", "t", 8, 1, 0), - tile!("U", "u", 12, 1, 1), - tile!("V", "v", 1, 10, 0), - tile!("W", "w", 1, 10, 0), - tile!("Y", "y", 2, 6, 0), - ], - ..Default::default() - }) + new_static_alphabet_from_file!("alphabets/yupik.txt") } pub struct AlphabetReader { diff --git a/src/alphabets/catalan.txt b/src/alphabets/catalan.txt new file mode 100644 index 0000000..aaa0459 --- /dev/null +++ b/src/alphabets/catalan.txt @@ -0,0 +1,27 @@ +? ? 2 0 0 0 0 +A a 12 1 1 0 0 +B b 2 3 0 0 0 +C c 3 2 0 0 0 +Ç ç 1 10 0 1 K 1 k +D d 3 2 0 0 0 +E e 13 1 1 0 0 +F f 1 4 0 0 0 +G g 2 3 0 0 0 +H h 1 8 0 0 0 +I i 8 1 1 0 0 +J j 1 8 0 0 0 +L l 4 1 0 0 0 +L·L l·l 1 10 0 1 W 1 w +M m 3 2 0 0 0 +N n 6 1 0 0 0 +NY ny 1 10 0 1 Y 1 y +O o 5 1 1 0 0 +P p 2 3 0 0 0 +QU qu 1 8 0 1 Q 1 q +R r 8 1 0 0 0 +S s 8 1 0 0 0 +T t 5 1 0 0 0 +U u 4 1 1 0 0 +V v 1 4 0 0 0 +X x 1 10 0 0 0 +Z z 1 8 0 0 0 diff --git a/src/alphabets/english.txt b/src/alphabets/english.txt new file mode 100644 index 0000000..bdbd2ad --- /dev/null +++ b/src/alphabets/english.txt @@ -0,0 +1,27 @@ +? ? 2 0 0 0 0 +A a 9 1 1 0 0 +B b 2 3 0 0 0 +C c 2 3 0 0 0 +D d 4 2 0 0 0 +E e 12 1 1 0 0 +F f 2 4 0 0 0 +G g 3 2 0 0 0 +H h 2 4 0 0 0 +I i 9 1 1 0 0 +J j 1 8 0 0 0 +K k 1 5 0 0 0 +L l 4 1 0 0 0 +M m 2 3 0 0 0 +N n 6 1 0 0 0 +O o 8 1 1 0 0 +P p 2 3 0 0 0 +Q q 1 10 0 0 0 +R r 6 1 0 0 0 +S s 4 1 0 0 0 +T t 6 1 0 0 0 +U u 4 1 1 0 0 +V v 2 4 0 0 0 +W w 2 4 0 0 0 +X x 1 8 0 0 0 +Y y 2 4 0 0 0 +Z z 1 10 0 0 0 diff --git a/src/alphabets/french.txt b/src/alphabets/french.txt new file mode 100644 index 0000000..5ec32f6 --- /dev/null +++ b/src/alphabets/french.txt @@ -0,0 +1,27 @@ +? ? 2 0 0 0 0 +A a 9 1 1 0 0 +B b 2 3 0 0 0 +C c 2 3 0 0 0 +D d 3 2 0 0 0 +E e 15 1 1 0 0 +F f 2 4 0 0 0 +G g 2 2 0 0 0 +H h 2 4 0 0 0 +I i 8 1 1 0 0 +J j 1 8 0 0 0 +K k 1 10 0 0 0 +L l 5 1 0 0 0 +M m 3 2 0 0 0 +N n 6 1 0 0 0 +O o 6 1 1 0 0 +P p 2 3 0 0 0 +Q q 1 8 0 0 0 +R r 6 1 0 0 0 +S s 6 1 0 0 0 +T t 6 1 0 0 0 +U u 6 1 1 0 0 +V v 2 4 0 0 0 +W w 1 10 0 0 0 +X x 1 10 0 0 0 +Y y 1 10 1 0 0 +Z z 1 10 0 0 0 diff --git a/src/alphabets/german.txt b/src/alphabets/german.txt new file mode 100644 index 0000000..e9ee391 --- /dev/null +++ b/src/alphabets/german.txt @@ -0,0 +1,30 @@ +? ? 2 0 0 0 0 +A a 5 1 1 0 0 +Ä ä 1 6 1 0 0 +B b 2 3 0 0 0 +C c 2 4 0 0 0 +D d 4 1 0 0 0 +E e 15 1 1 0 0 +F f 2 4 0 0 0 +G g 3 2 0 0 0 +H h 4 2 0 0 0 +I i 6 1 1 0 0 +J j 1 6 0 0 0 +K k 2 4 0 0 0 +L l 3 2 0 0 0 +M m 4 3 0 0 0 +N n 9 1 0 0 0 +O o 3 2 1 0 0 +Ö ö 1 8 1 0 0 +P p 1 4 0 0 0 +Q q 1 10 0 0 0 +R r 6 1 0 0 0 +S s 7 1 0 0 0 +T t 6 1 0 0 0 +U u 6 1 1 0 0 +Ü ü 1 6 1 0 0 +V v 1 6 0 0 0 +W w 1 3 0 0 0 +X x 1 8 0 0 0 +Y y 1 10 0 0 0 +Z z 1 3 0 0 0 diff --git a/src/alphabets/hong_kong_english.txt b/src/alphabets/hong_kong_english.txt new file mode 100644 index 0000000..0bf4580 --- /dev/null +++ b/src/alphabets/hong_kong_english.txt @@ -0,0 +1,27 @@ +? ? 4 0 0 0 0 +A a 9 1 1 0 0 +B b 2 3 0 0 0 +C c 2 3 0 0 0 +D d 4 2 0 0 0 +E e 12 1 1 0 0 +F f 2 4 0 0 0 +G g 3 2 0 0 0 +H h 2 4 0 0 0 +I i 9 1 1 0 0 +J j 2 8 0 0 0 +K k 2 5 0 0 0 +L l 4 1 0 0 0 +M m 2 3 0 0 0 +N n 6 1 0 0 0 +O o 8 1 1 0 0 +P p 2 3 0 0 0 +Q q 2 10 0 0 0 +R r 6 1 0 0 0 +S s 4 1 0 0 0 +T t 6 1 0 0 0 +U u 4 1 1 0 0 +V v 2 4 0 0 0 +W w 2 4 0 0 0 +X x 2 8 0 0 0 +Y y 2 4 0 0 0 +Z z 2 10 0 0 0 diff --git a/src/alphabets/norwegian.txt b/src/alphabets/norwegian.txt new file mode 100644 index 0000000..5242080 --- /dev/null +++ b/src/alphabets/norwegian.txt @@ -0,0 +1,33 @@ +? ? 2 0 0 0 0 +A a 7 1 1 0 0 +B b 3 4 0 0 0 +C c 1 10 0 0 0 +D d 5 1 0 0 0 +E e 9 1 1 0 0 +F f 4 2 0 0 0 +G g 4 2 0 0 0 +H h 3 3 0 0 0 +I i 5 1 1 0 0 +J j 2 4 0 0 0 +K k 4 2 0 0 0 +L l 5 1 0 0 0 +M m 3 2 0 0 0 +N n 6 1 0 0 0 +O o 4 2 1 0 0 +P p 2 4 0 0 0 +Q q 0 0 0 0 0 +R r 6 1 0 0 0 +S s 6 1 0 0 0 +T t 6 1 0 0 0 +U u 3 4 1 0 0 +V v 3 4 0 0 0 +W w 1 8 0 0 0 +X x 0 0 0 0 0 +Y y 1 6 1 0 0 +Ü ü 0 0 1 0 0 +Z z 0 0 0 0 0 +Æ æ 1 6 1 0 0 +Ä ä 0 0 1 0 0 +Ø ø 2 5 1 0 0 +Ö ö 0 0 1 0 0 +Å å 2 4 1 0 0 diff --git a/src/alphabets/polish.txt b/src/alphabets/polish.txt new file mode 100644 index 0000000..75ee472 --- /dev/null +++ b/src/alphabets/polish.txt @@ -0,0 +1,33 @@ +? ? 2 0 0 0 0 +A a 9 1 1 0 0 +Ą ą 1 5 1 0 0 +B b 2 3 0 0 0 +C c 3 2 0 0 0 +Ć ć 1 6 0 0 0 +D d 3 2 0 0 0 +E e 7 1 1 0 0 +Ę ę 1 5 1 0 0 +F f 1 5 0 0 0 +G g 2 3 0 0 0 +H h 2 3 0 0 0 +I i 8 1 1 0 0 +J j 2 3 0 0 0 +K k 3 2 0 0 0 +L l 3 2 0 0 0 +Ł ł 2 3 0 0 0 +M m 3 2 0 0 0 +N n 5 1 0 0 0 +Ń ń 1 7 0 0 0 +O o 6 1 1 0 0 +Ó ó 1 5 1 0 0 +P p 3 2 0 0 0 +R r 4 1 0 0 0 +S s 4 1 0 0 0 +Ś ś 1 5 0 0 0 +T t 3 2 0 0 0 +U u 2 3 1 0 0 +W w 4 1 0 0 0 +Y y 4 2 1 0 0 +Z z 5 1 0 0 0 +Ź ź 1 9 0 0 0 +Ż ż 1 5 0 0 0 diff --git a/src/alphabets/slovene.txt b/src/alphabets/slovene.txt new file mode 100644 index 0000000..9c32b3c --- /dev/null +++ b/src/alphabets/slovene.txt @@ -0,0 +1,36 @@ +? ? 2 0 0 0 0 +A a 10 1 1 0 0 +Å å 0 0 1 0 0 +Ä ä 0 0 1 0 0 +B b 2 4 0 0 0 +C c 1 8 0 0 0 +Ç ç 0 0 0 0 0 +Č č 1 5 0 0 0 +D d 4 2 0 0 0 +E e 11 1 1 0 0 +F f 1 10 0 0 0 +G g 2 4 0 0 0 +H h 1 5 0 0 0 +I i 9 1 1 0 0 +J j 4 1 0 0 0 +K k 3 3 0 0 0 +L l 4 1 0 0 0 +M m 2 3 0 0 0 +N n 7 1 0 0 0 +Ñ ñ 0 0 0 0 0 +O o 8 1 1 0 0 +Ö ö 0 0 1 0 0 +P p 2 3 0 0 0 +Q q 0 0 0 0 0 +R r 6 1 0 0 0 +S s 6 1 0 0 0 +Š š 1 6 0 0 0 +T t 4 1 0 0 0 +U u 2 3 1 0 0 +Ü ü 0 0 1 0 0 +V v 4 2 0 0 0 +W w 0 0 0 0 0 +X x 0 0 0 0 0 +Y y 0 0 0 0 0 +Z z 2 4 0 0 0 +Ž ž 1 10 0 0 0 diff --git a/src/alphabets/spanish.txt b/src/alphabets/spanish.txt new file mode 100644 index 0000000..5cd25a0 --- /dev/null +++ b/src/alphabets/spanish.txt @@ -0,0 +1,29 @@ +? ? 2 0 0 0 0 +A a 12 1 1 0 0 +B b 2 3 0 0 0 +C c 4 3 0 0 0 +[CH] [ch] 1 5 0 1 1 0 +D d 5 2 0 0 0 +E e 12 1 1 0 0 +F f 1 4 0 0 0 +G g 2 2 0 0 0 +H h 2 4 0 0 0 +I i 6 1 1 0 0 +J j 1 8 0 0 0 +L l 4 1 0 0 0 +[LL] [ll] 1 8 0 1 2 0 +M m 2 3 0 0 0 +N n 5 1 0 0 0 +Ñ ñ 1 8 0 0 0 +O o 9 1 1 0 0 +P p 2 3 0 0 0 +Q q 1 5 0 0 0 +R r 5 1 0 0 0 +[RR] [rr] 1 8 0 1 3 0 +S s 6 1 0 0 0 +T t 4 1 0 0 0 +U u 5 1 1 0 0 +V v 1 4 0 0 0 +X x 1 8 0 0 0 +Y y 1 4 0 0 0 +Z z 1 10 0 0 0 diff --git a/src/alphabets/super_catalan.txt b/src/alphabets/super_catalan.txt new file mode 100644 index 0000000..64b0f11 --- /dev/null +++ b/src/alphabets/super_catalan.txt @@ -0,0 +1,27 @@ +? ? 5 0 0 0 0 +A a 25 1 1 0 0 +B b 3 3 0 0 0 +C c 5 2 0 0 0 +Ç ç 2 12 0 1 K 1 k +D d 5 2 0 0 0 +E e 27 1 1 0 0 +F f 2 4 0 0 0 +G g 3 3 0 0 0 +H h 2 8 0 0 0 +I i 17 1 1 0 0 +J j 2 8 0 0 0 +L l 8 1 0 0 0 +L·L l·l 1 15 0 1 W 1 w +M m 7 2 0 0 0 +N n 12 1 0 0 0 +NY ny 2 10 0 1 Y 1 y +O o 10 1 1 0 0 +P p 3 3 0 0 0 +QU qu 2 8 0 1 Q 1 q +R r 16 1 0 0 0 +S s 19 1 0 0 0 +T t 10 1 0 0 0 +U u 6 1 1 0 0 +V v 2 4 0 0 0 +X x 2 10 0 0 0 +Z z 2 8 0 0 0 diff --git a/src/alphabets/super_english.txt b/src/alphabets/super_english.txt new file mode 100644 index 0000000..137b3b1 --- /dev/null +++ b/src/alphabets/super_english.txt @@ -0,0 +1,27 @@ +? ? 4 0 0 0 0 +A a 16 1 1 0 0 +B b 4 3 0 0 0 +C c 6 3 0 0 0 +D d 8 2 0 0 0 +E e 24 1 1 0 0 +F f 4 4 0 0 0 +G g 5 2 0 0 0 +H h 5 4 0 0 0 +I i 13 1 1 0 0 +J j 2 8 0 0 0 +K k 2 5 0 0 0 +L l 7 1 0 0 0 +M m 6 3 0 0 0 +N n 13 1 0 0 0 +O o 15 1 1 0 0 +P p 4 3 0 0 0 +Q q 2 10 0 0 0 +R r 13 1 0 0 0 +S s 10 1 0 0 0 +T t 15 1 0 0 0 +U u 7 1 1 0 0 +V v 3 4 0 0 0 +W w 4 4 0 0 0 +X x 2 8 0 0 0 +Y y 4 4 0 0 0 +Z z 2 10 0 0 0 diff --git a/src/alphabets/yupik.txt b/src/alphabets/yupik.txt new file mode 100644 index 0000000..1ec19f6 --- /dev/null +++ b/src/alphabets/yupik.txt @@ -0,0 +1,19 @@ +? ? 2 0 0 0 0 +A a 17 1 1 0 0 +C c 2 6 0 0 0 +E e 6 1 1 0 0 +G g 5 2 0 0 0 +I i 9 1 1 0 0 +K k 5 2 0 0 0 +L l 8 1 0 0 0 +M m 4 4 0 0 0 +N n 8 1 0 0 0 +P p 1 8 0 0 0 +Q q 4 4 0 0 0 +R r 6 1 0 0 0 +S s 1 8 0 0 0 +T t 8 1 0 0 0 +U u 12 1 1 0 0 +V v 1 10 0 0 0 +W w 1 10 0 0 0 +Y y 2 6 0 0 0 diff --git a/src/board_layout.rs b/src/board_layout.rs index f806597..4f13d19 100644 --- a/src/board_layout.rs +++ b/src/board_layout.rs @@ -2,46 +2,76 @@ use super::matrix; -#[derive(Clone, Copy)] +#[derive(Clone)] pub struct Premium { pub word_multiplier: i8, pub tile_multiplier: i8, } -static QWS: Premium = Premium { - word_multiplier: 4, - tile_multiplier: 1, -}; -static TWS: Premium = Premium { - word_multiplier: 3, - tile_multiplier: 1, -}; -static DWS: Premium = Premium { - word_multiplier: 2, - tile_multiplier: 1, -}; -static QLS: Premium = Premium { - word_multiplier: 1, - tile_multiplier: 4, -}; -static TLS: Premium = Premium { - word_multiplier: 1, - tile_multiplier: 3, -}; -static DLS: Premium = Premium { - word_multiplier: 1, - tile_multiplier: 2, -}; -static FVS: Premium = Premium { - word_multiplier: 1, - tile_multiplier: 1, -}; +#[inline(always)] +fn qws() -> Premium { + Premium { + word_multiplier: 4, + tile_multiplier: 1, + } +} + +#[inline(always)] +fn tws() -> Premium { + Premium { + word_multiplier: 3, + tile_multiplier: 1, + } +} + +#[inline(always)] +fn dws() -> Premium { + Premium { + word_multiplier: 2, + tile_multiplier: 1, + } +} + +#[inline(always)] +fn qls() -> Premium { + Premium { + word_multiplier: 1, + tile_multiplier: 4, + } +} + +#[inline(always)] +fn tls() -> Premium { + Premium { + word_multiplier: 1, + tile_multiplier: 3, + } +} + +#[inline(always)] +fn dls() -> Premium { + Premium { + word_multiplier: 1, + tile_multiplier: 2, + } +} + +#[inline(always)] +fn fvs() -> Premium { + Premium { + word_multiplier: 1, + tile_multiplier: 1, + } +} // This is a punctured square. No tile may be played on it. -static DEL: Premium = Premium { - word_multiplier: 0, - tile_multiplier: 0, -}; +#[inline(always)] +fn del() -> Premium { + Premium { + word_multiplier: 0, + tile_multiplier: 0, + } +} #[derive(Default)] pub struct StaticBoardLayout { @@ -65,7 +95,7 @@ impl BoardLayout { let mut transposed_premiums = Vec::with_capacity(rows_times_cols); for col in 0..x.dim.cols { for row in 0..x.dim.rows { - transposed_premiums.push(x.premiums[x.dim.at_row_col(row, col)]); + transposed_premiums.push(x.premiums[x.dim.at_row_col(row, col)].clone()); } } let mut danger_star_across = vec![false; x.dim.cols as usize]; @@ -118,8 +148,8 @@ impl BoardLayout { && x.star_row == x.star_col && (0..x.dim.rows).all(|row| { (0..row).all(|col| { - let p1 = x.premiums[x.dim.at_row_col(row, col)]; - let p2 = x.premiums[x.dim.at_row_col(col, row)]; + let p1 = &x.premiums[x.dim.at_row_col(row, col)]; + let p2 = &x.premiums[x.dim.at_row_col(col, row)]; p1.word_multiplier == p2.word_multiplier && p1.tile_multiplier == p2.tile_multiplier }) @@ -129,9 +159,9 @@ impl BoardLayout { } #[inline(always)] - pub fn dim(&self) -> matrix::Dim { + pub fn dim(&self) -> &matrix::Dim { match self { - BoardLayout::Static(x) => x.dim, + BoardLayout::Static(x) => &x.dim, } } @@ -193,21 +223,231 @@ impl BoardLayout { pub fn make_standard_board_layout() -> BoardLayout { BoardLayout::new_static(StaticBoardLayout { premiums: Box::new([ - TWS, FVS, FVS, DLS, FVS, FVS, FVS, TWS, FVS, FVS, FVS, DLS, FVS, FVS, TWS, // - FVS, DWS, FVS, FVS, FVS, TLS, FVS, FVS, FVS, TLS, FVS, FVS, FVS, DWS, FVS, // - FVS, FVS, DWS, FVS, FVS, FVS, DLS, FVS, DLS, FVS, FVS, FVS, DWS, FVS, FVS, // - DLS, FVS, FVS, DWS, FVS, FVS, FVS, DLS, FVS, FVS, FVS, DWS, FVS, FVS, DLS, // - FVS, FVS, FVS, FVS, DWS, FVS, FVS, FVS, FVS, FVS, DWS, FVS, FVS, FVS, FVS, // - FVS, TLS, FVS, FVS, FVS, TLS, FVS, FVS, FVS, TLS, FVS, FVS, FVS, TLS, FVS, // - FVS, FVS, DLS, FVS, FVS, FVS, DLS, FVS, DLS, FVS, FVS, FVS, DLS, FVS, FVS, // - TWS, FVS, FVS, DLS, FVS, FVS, FVS, DWS, FVS, FVS, FVS, DLS, FVS, FVS, TWS, // - FVS, FVS, DLS, FVS, FVS, FVS, DLS, FVS, DLS, FVS, FVS, FVS, DLS, FVS, FVS, // - FVS, TLS, FVS, FVS, FVS, TLS, FVS, FVS, FVS, TLS, FVS, FVS, FVS, TLS, FVS, // - FVS, FVS, FVS, FVS, DWS, FVS, FVS, FVS, FVS, FVS, DWS, FVS, FVS, FVS, FVS, // - DLS, FVS, FVS, DWS, FVS, FVS, FVS, DLS, FVS, FVS, FVS, DWS, FVS, FVS, DLS, // - FVS, FVS, DWS, FVS, FVS, FVS, DLS, FVS, DLS, FVS, FVS, FVS, DWS, FVS, FVS, // - FVS, DWS, FVS, FVS, FVS, TLS, FVS, FVS, FVS, TLS, FVS, FVS, FVS, DWS, FVS, // - TWS, FVS, FVS, DLS, FVS, FVS, FVS, TWS, FVS, FVS, FVS, DLS, FVS, FVS, TWS, // + tws(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + tws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + tws(), // + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), // + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), // + dls(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + dls(), // + fvs(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + fvs(), // + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), // + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), // + tws(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + tws(), // + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), // + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), // + fvs(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + fvs(), // + dls(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + dls(), // + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), // + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), // + tws(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + tws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + tws(), // ]), dim: matrix::Dim { rows: 15, cols: 15 }, star_row: 7, @@ -220,21 +460,231 @@ pub fn make_standard_board_layout() -> BoardLayout { pub fn make_punctured_board_layout() -> BoardLayout { BoardLayout::new_static(StaticBoardLayout { premiums: Box::new([ - DEL, FVS, FVS, DLS, FVS, FVS, FVS, TWS, FVS, FVS, FVS, DLS, FVS, FVS, DEL, // - FVS, DWS, FVS, FVS, FVS, TLS, FVS, DEL, FVS, TLS, FVS, FVS, FVS, DWS, FVS, // - FVS, FVS, DEL, FVS, FVS, FVS, DLS, FVS, DLS, FVS, FVS, FVS, DEL, FVS, FVS, // - DLS, FVS, FVS, DWS, FVS, FVS, FVS, DLS, FVS, FVS, FVS, DWS, FVS, FVS, DLS, // - FVS, FVS, FVS, FVS, DEL, FVS, FVS, FVS, FVS, FVS, DEL, FVS, FVS, FVS, FVS, // - FVS, TLS, FVS, FVS, FVS, TLS, FVS, DEL, FVS, TLS, FVS, FVS, FVS, TLS, FVS, // - FVS, FVS, DLS, FVS, FVS, FVS, DLS, FVS, DLS, FVS, FVS, FVS, DLS, FVS, FVS, // - TWS, DEL, FVS, DLS, FVS, DEL, FVS, DWS, FVS, DEL, FVS, DLS, FVS, DEL, TWS, // - FVS, FVS, DLS, FVS, FVS, FVS, DLS, FVS, DLS, FVS, FVS, FVS, DLS, FVS, FVS, // - FVS, TLS, FVS, FVS, FVS, TLS, FVS, DEL, FVS, TLS, FVS, FVS, FVS, TLS, FVS, // - FVS, FVS, FVS, FVS, DEL, FVS, FVS, FVS, FVS, FVS, DEL, FVS, FVS, FVS, FVS, // - DLS, FVS, FVS, DWS, FVS, FVS, FVS, DLS, FVS, FVS, FVS, DWS, FVS, FVS, DLS, // - FVS, FVS, DEL, FVS, FVS, FVS, DLS, FVS, DLS, FVS, FVS, FVS, DEL, FVS, FVS, // - FVS, DWS, FVS, FVS, FVS, TLS, FVS, DEL, FVS, TLS, FVS, FVS, FVS, DWS, FVS, // - DEL, FVS, FVS, DLS, FVS, FVS, FVS, TWS, FVS, FVS, FVS, DLS, FVS, FVS, DEL, // + del(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + tws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + del(), // + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + del(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), // + fvs(), + fvs(), + del(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + del(), + fvs(), + fvs(), // + dls(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + dls(), // + fvs(), + fvs(), + fvs(), + fvs(), + del(), + fvs(), + fvs(), + fvs(), + fvs(), + fvs(), + del(), + fvs(), + fvs(), + fvs(), + fvs(), // + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + del(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), // + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), // + tws(), + del(), + fvs(), + dls(), + fvs(), + del(), + fvs(), + dws(), + fvs(), + del(), + fvs(), + dls(), + fvs(), + del(), + tws(), // + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), // + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + del(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), // + fvs(), + fvs(), + fvs(), + fvs(), + del(), + fvs(), + fvs(), + fvs(), + fvs(), + fvs(), + del(), + fvs(), + fvs(), + fvs(), + fvs(), // + dls(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + dls(), // + fvs(), + fvs(), + del(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + del(), + fvs(), + fvs(), // + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + del(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), // + del(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + tws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + del(), // ]), dim: matrix::Dim { rows: 15, cols: 15 }, star_row: 7, @@ -247,48 +697,447 @@ pub fn make_punctured_board_layout() -> BoardLayout { pub fn make_super_board_layout() -> BoardLayout { BoardLayout::new_static(StaticBoardLayout { premiums: Box::new([ - QWS, FVS, FVS, DLS, FVS, FVS, FVS, TWS, FVS, FVS, DLS, FVS, FVS, TWS, FVS, FVS, FVS, - DLS, FVS, FVS, QWS, // - FVS, DWS, FVS, FVS, TLS, FVS, FVS, FVS, DWS, FVS, FVS, FVS, DWS, FVS, FVS, FVS, TLS, - FVS, FVS, DWS, FVS, // - FVS, FVS, DWS, FVS, FVS, QLS, FVS, FVS, FVS, DWS, FVS, DWS, FVS, FVS, FVS, QLS, FVS, - FVS, DWS, FVS, FVS, // - DLS, FVS, FVS, TWS, FVS, FVS, DLS, FVS, FVS, FVS, TWS, FVS, FVS, FVS, DLS, FVS, FVS, - TWS, FVS, FVS, DLS, // - FVS, TLS, FVS, FVS, DWS, FVS, FVS, FVS, TLS, FVS, FVS, FVS, TLS, FVS, FVS, FVS, DWS, - FVS, FVS, TLS, FVS, // - FVS, FVS, QLS, FVS, FVS, DWS, FVS, FVS, FVS, DLS, FVS, DLS, FVS, FVS, FVS, DWS, FVS, - FVS, QLS, FVS, FVS, // - FVS, FVS, FVS, DLS, FVS, FVS, DWS, FVS, FVS, FVS, DLS, FVS, FVS, FVS, DWS, FVS, FVS, - DLS, FVS, FVS, FVS, // - TWS, FVS, FVS, FVS, FVS, FVS, FVS, DWS, FVS, FVS, FVS, FVS, FVS, DWS, FVS, FVS, FVS, - FVS, FVS, FVS, TWS, // - FVS, DWS, FVS, FVS, TLS, FVS, FVS, FVS, TLS, FVS, FVS, FVS, TLS, FVS, FVS, FVS, TLS, - FVS, FVS, DWS, FVS, // - FVS, FVS, DWS, FVS, FVS, DLS, FVS, FVS, FVS, DLS, FVS, DLS, FVS, FVS, FVS, DLS, FVS, - FVS, DWS, FVS, FVS, // - DLS, FVS, FVS, TWS, FVS, FVS, DLS, FVS, FVS, FVS, DWS, FVS, FVS, FVS, DLS, FVS, FVS, - TWS, FVS, FVS, DLS, // - FVS, FVS, DWS, FVS, FVS, DLS, FVS, FVS, FVS, DLS, FVS, DLS, FVS, FVS, FVS, DLS, FVS, - FVS, DWS, FVS, FVS, // - FVS, DWS, FVS, FVS, TLS, FVS, FVS, FVS, TLS, FVS, FVS, FVS, TLS, FVS, FVS, FVS, TLS, - FVS, FVS, DWS, FVS, // - TWS, FVS, FVS, FVS, FVS, FVS, FVS, DWS, FVS, FVS, FVS, FVS, FVS, DWS, FVS, FVS, FVS, - FVS, FVS, FVS, TWS, // - FVS, FVS, FVS, DLS, FVS, FVS, DWS, FVS, FVS, FVS, DLS, FVS, FVS, FVS, DWS, FVS, FVS, - DLS, FVS, FVS, FVS, // - FVS, FVS, QLS, FVS, FVS, DWS, FVS, FVS, FVS, DLS, FVS, DLS, FVS, FVS, FVS, DWS, FVS, - FVS, QLS, FVS, FVS, // - FVS, TLS, FVS, FVS, DWS, FVS, FVS, FVS, TLS, FVS, FVS, FVS, TLS, FVS, FVS, FVS, DWS, - FVS, FVS, TLS, FVS, // - DLS, FVS, FVS, TWS, FVS, FVS, DLS, FVS, FVS, FVS, TWS, FVS, FVS, FVS, DLS, FVS, FVS, - TWS, FVS, FVS, DLS, // - FVS, FVS, DWS, FVS, FVS, QLS, FVS, FVS, FVS, DWS, FVS, DWS, FVS, FVS, FVS, QLS, FVS, - FVS, DWS, FVS, FVS, // - FVS, DWS, FVS, FVS, TLS, FVS, FVS, FVS, DWS, FVS, FVS, FVS, DWS, FVS, FVS, FVS, TLS, - FVS, FVS, DWS, FVS, // - QWS, FVS, FVS, DLS, FVS, FVS, FVS, TWS, FVS, FVS, DLS, FVS, FVS, TWS, FVS, FVS, FVS, - DLS, FVS, FVS, QWS, // + qws(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + tws(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + tws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + qws(), // + fvs(), + dws(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + dws(), + fvs(), // + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + qls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + qls(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), // + dls(), + fvs(), + fvs(), + tws(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + tws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + tws(), + fvs(), + fvs(), + dls(), // + fvs(), + tls(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + tls(), + fvs(), // + fvs(), + fvs(), + qls(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + qls(), + fvs(), + fvs(), // + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), // + tws(), + fvs(), + fvs(), + fvs(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + fvs(), + fvs(), + fvs(), + tws(), // + fvs(), + dws(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + dws(), + fvs(), // + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), // + dls(), + fvs(), + fvs(), + tws(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + tws(), + fvs(), + fvs(), + dls(), // + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), // + fvs(), + dws(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + dws(), + fvs(), // + tws(), + fvs(), + fvs(), + fvs(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + fvs(), + fvs(), + fvs(), + tws(), // + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), // + fvs(), + fvs(), + qls(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + qls(), + fvs(), + fvs(), // + fvs(), + tls(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + tls(), + fvs(), // + dls(), + fvs(), + fvs(), + tws(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + tws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + tws(), + fvs(), + fvs(), + dls(), // + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + qls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + qls(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), // + fvs(), + dws(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + dws(), + fvs(), + fvs(), + fvs(), + tls(), + fvs(), + fvs(), + dws(), + fvs(), // + qws(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + fvs(), + tws(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + tws(), + fvs(), + fvs(), + fvs(), + dls(), + fvs(), + fvs(), + qws(), // ]), dim: matrix::Dim { rows: 21, cols: 21 }, star_row: 10, diff --git a/src/display.rs b/src/display.rs index 386f9ed..47678cf 100644 --- a/src/display.rs +++ b/src/display.rs @@ -8,7 +8,7 @@ pub fn empty_label(board_layout: &board_layout::BoardLayout, row: i8, col: i8) - if row == board_layout.star_row() && col == board_layout.star_col() { return "*"; } - let premium = board_layout.premiums()[board_layout.dim().at_row_col(row, col)]; + let premium = &board_layout.premiums()[board_layout.dim().at_row_col(row, col)]; match premium.word_multiplier { 4 => "~", 3 => "=", diff --git a/src/game_state.rs b/src/game_state.rs index 0252acd..fab1987 100644 --- a/src/game_state.rs +++ b/src/game_state.rs @@ -213,8 +213,12 @@ impl GameState { pub fn next_turn(&mut self) { let num_players = self.players.len() as u8; + let cur_turn = self.turn; self.turn += 1; self.turn -= num_players & -((self.turn >= num_players) as i8) as u8; + if (cur_turn ^ self.turn) & 1 != 0 { + self.bag.0.reverse(); + } } pub fn check_game_ended( diff --git a/src/matrix.rs b/src/matrix.rs index 7b078d6..7ed8517 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -1,6 +1,6 @@ // Copyright (C) 2020-2024 Andy Kurnia. -#[derive(Clone, Copy)] +#[derive(Clone)] pub struct Strider { base: i16, step: i8, @@ -34,7 +34,7 @@ impl Strider { } } -#[derive(Clone, Copy, Default)] +#[derive(Clone, Default)] pub struct Dim { pub rows: i8, pub cols: i8, diff --git a/src/movegen.rs b/src/movegen.rs index 3dea482..99aaea3 100644 --- a/src/movegen.rs +++ b/src/movegen.rs @@ -8,7 +8,7 @@ struct CrossSet { score: i32, } -#[derive(Clone, Copy)] +#[derive(Clone)] struct CachedCrossSet { p_left: i32, p_right: i32, @@ -33,6 +33,14 @@ struct PossiblePlacement { best_possible_equity: f32, } +#[derive(Clone)] +struct MultiJump { + left_score: i32, + right_score: i32, + left_idx: i8, + right_idx: i8, +} + // WorkingBuffer can only be reused for the same game_config and kwg. // (The kwg is partially cached in cached_cross_set.) // WorkingBuffer can also be reset for reuse with another kwg by calling @@ -68,7 +76,8 @@ struct WorkingBuffer { aggregated_word_multipliers: Vec, // sorted, unique, O(n) insertion but n is tiny. precomputed_square_multiplier_buffer: Vec, indexes_to_descending_square_multiplier_buffer: Vec, - best_leave_values: Vec, // rack.len() + 1 + multi_jumps_buffer: Box<[MultiJump]>, // max(r, c) + best_leave_values: Vec, // rack.len() + 1 found_placements: Vec, used_letters_tally: Vec, // 27 for ?A-Z, ? is always 0, jumbled mode only used_tile_scores: Vec, // rack.len() (stack) @@ -126,6 +135,7 @@ impl Clone for WorkingBuffer { indexes_to_descending_square_multiplier_buffer: self .indexes_to_descending_square_multiplier_buffer .clone(), + multi_jumps_buffer: self.multi_jumps_buffer.clone(), best_leave_values: self.best_leave_values.clone(), found_placements: self.found_placements.clone(), used_letters_tally: self.used_letters_tally.clone(), @@ -188,6 +198,8 @@ impl Clone for WorkingBuffer { .clone_from(&source.precomputed_square_multiplier_buffer); self.indexes_to_descending_square_multiplier_buffer .clone_from(&source.indexes_to_descending_square_multiplier_buffer); + self.multi_jumps_buffer + .clone_from(&source.multi_jumps_buffer); self.best_leave_values.clone_from(&source.best_leave_values); self.found_placements.clone_from(&source.found_placements); self.used_letters_tally @@ -269,6 +281,16 @@ impl WorkingBuffer { aggregated_word_multipliers: Vec::new(), precomputed_square_multiplier_buffer: Vec::new(), indexes_to_descending_square_multiplier_buffer: Vec::new(), + multi_jumps_buffer: vec![ + MultiJump { + left_score: 0, + right_score: 0, + left_idx: 0, + right_idx: 0, + }; + dim.rows.max(dim.cols) as usize + ] + .into_boxed_slice(), best_leave_values: Vec::new(), found_placements: Vec::new(), used_letters_tally: Vec::new(), @@ -317,7 +339,7 @@ impl WorkingBuffer { let idx = strip_range_start + col as usize; let b = board_snapshot.board_tiles[idx]; if b == 0 { - let premium = premiums[idx]; + let premium = &premiums[idx]; self.remaining_word_multipliers_for_across_plays[idx] = premium.word_multiplier; self.remaining_tile_multipliers_for_across_plays[idx] = premium.tile_multiplier; self.face_value_scores_for_across_plays[idx] = 0; @@ -341,7 +363,7 @@ impl WorkingBuffer { let idx = strip_range_start + row as usize; let b = self.transposed_board_tiles[idx]; if b == 0 { - let premium = transposed_premiums[idx]; + let premium = &transposed_premiums[idx]; self.remaining_word_multipliers_for_down_plays[idx] = premium.word_multiplier; self.remaining_tile_multipliers_for_down_plays[idx] = premium.tile_multiplier; self.face_value_scores_for_down_plays[idx] = 0; @@ -457,7 +479,7 @@ impl WorkingBuffer { for col in 0..dim.cols { let idx = strip_range_start + col as usize; let cross_set = &mut self.cross_set_for_across_plays[idx]; - let premium = premiums[idx]; + let premium = &premiums[idx]; if premium.word_multiplier == 0 && premium.tile_multiplier == 0 { cross_set.bits = 1; } @@ -473,7 +495,7 @@ impl WorkingBuffer { for row in 0..dim.rows { let idx = strip_range_start + row as usize; let cross_set = &mut self.cross_set_for_down_plays[idx]; - let premium = transposed_premiums[idx]; + let premium = &transposed_premiums[idx]; if premium.word_multiplier == 0 && premium.tile_multiplier == 0 { cross_set.bits = 1; } @@ -886,7 +908,6 @@ struct GenPlacePlacementsParams<'a> { cross_set_strip: &'a [CrossSet], remaining_word_multipliers_strip: &'a [i8], remaining_tile_multipliers_strip: &'a [i8], - face_value_scores_strip: &'a [i8], perpendicular_word_multipliers_strip: &'a [i8], perpendicular_scores_strip: &'a [i32], rack_bits: u64, @@ -894,6 +915,7 @@ struct GenPlacePlacementsParams<'a> { aggregated_word_multipliers: &'a mut Vec, precomputed_square_multiplier_buffer: &'a mut Vec, indexes_to_descending_square_multiplier_buffer: &'a mut Vec, + multi_jumps_buffer: &'a mut [MultiJump], best_leave_values: &'a [f32], num_max_played: u8, rack_tally_shadowl: &'a mut [u8], @@ -909,6 +931,7 @@ fn gen_place_placements<'a, PossibleStripPlacementCallbackType: FnMut(i8, i8, i8 let strider_len = params.board_strip.len(); if !want_raw { + // process the square multipliers. params.aggregated_word_multipliers.clear(); // each contiguous subsequence of multiple 1s needs to be processed just once. let mut last_was_one = false; @@ -952,18 +975,52 @@ fn gen_place_placements<'a, PossibleStripPlacementCallbackType: FnMut(i8, i8, i8 &mut params.precomputed_square_multiplier_buffer[low_end..high_end]; let indexes_to_descending_square_multiplier_slice = &mut params.indexes_to_descending_square_multiplier_buffer[low_end..high_end]; - for j in 0..strider_len { + let mut left = 0; + for j in (0..strider_len).filter(|&j| params.board_strip[j] == 0) { // perpendicular_word_multipliers_strip[j] is 0 if no perpendicular tile. precomputed_square_multiplier_slice[j] = params.remaining_tile_multipliers_strip[j] as i32 * (k + params.perpendicular_word_multipliers_strip[j] as i32); - indexes_to_descending_square_multiplier_slice[j] = j as i8; + // put the indexes of empty squares first. + // the indexes of non-empty squares should never be visited. + indexes_to_descending_square_multiplier_slice[left] = j as i8; + left += 1; } - indexes_to_descending_square_multiplier_slice.sort_unstable_by(|&a, &b| { + indexes_to_descending_square_multiplier_slice[..left].sort_unstable_by(|&a, &b| { precomputed_square_multiplier_slice[b as usize] .cmp(&precomputed_square_multiplier_slice[a as usize]) }); } + + // precompute the multi jumps. (code is similar to cross set computation.) + let mut score = 0i32; + let mut last_empty = strider_len as i8; + for j in (0..strider_len).rev() { + let b = params.board_strip[j]; + if b != 0 { + score += params.alphabet.score(b) as i32; + } else { + // empty square, reset + score = 0; // cumulative face-value score + last_empty = j as i8; // last seen empty square + } + params.multi_jumps_buffer[j].right_score = score; + params.multi_jumps_buffer[j].right_idx = last_empty; + } + score = 0i32; + last_empty = -1i8; + for j in 0..strider_len { + let b = params.board_strip[j]; + if b != 0 { + score += params.alphabet.score(b) as i32; + } else { + // empty square, reset + score = 0; // cumulative face-value score + last_empty = j as i8; // last seen empty square + } + params.multi_jumps_buffer[j].left_score = score; + params.multi_jumps_buffer[j].left_idx = last_empty; + } } struct Env<'a> { @@ -1002,43 +1059,46 @@ fn gen_place_placements<'a, PossibleStripPlacementCallbackType: FnMut(i8, i8, i8 idx_right: i8, num_played: u8, ) { - // if a square requiring [B] is encountered while holding a B, the B - // must go there. if a square requiring [A,B] is encountered earlier, - // that square must be A, but this is not currently implemented. - let low_end = env - .params - .aggregated_word_multipliers - .binary_search(&acc.word_multiplier) - .unwrap() - * env.strider_len; - let high_end = low_end + env.strider_len; - let precomputed_square_multiplier_slice = - &env.params.precomputed_square_multiplier_buffer[low_end..high_end]; - env.params - .used_tile_scores_buffer - .clone_from(env.params.used_tile_scores); - env.params.used_tile_scores_buffer.sort_unstable(); // highest score is now last item - let mut desc_scores_iter = env.params.descending_scores.iter().filter(|&score| { - if env.params.used_tile_scores_buffer.last() == Some(score) { - env.params.used_tile_scores_buffer.pop(); - false - } else { - true - } - }); let mut best_scoring = 0; - let mut to_assign = num_played; - for &idx in &env.params.indexes_to_descending_square_multiplier_buffer[low_end..high_end] { - if idx_left <= idx - && idx < idx_right - && env.params.board_strip[idx as usize] == 0 - && env.params.shadow_strip_buffer[idx as usize] == 0 + let mut to_assign = num_played - env.params.used_tile_scores.len() as u8; + if to_assign != 0 { + // if a square requiring [B] is encountered while holding a B, the B + // must go there. if a square requiring [A,B] is encountered earlier, + // that square must be A, but this is not currently implemented. + let low_end = env + .params + .aggregated_word_multipliers + .binary_search(&acc.word_multiplier) + .unwrap() + * env.strider_len; + let high_end = low_end + env.strider_len; + let precomputed_square_multiplier_slice = + &env.params.precomputed_square_multiplier_buffer[low_end..high_end]; + env.params + .used_tile_scores_buffer + .clone_from(env.params.used_tile_scores); + env.params.used_tile_scores_buffer.sort_unstable(); // highest score is now last item + let mut desc_scores_iter = env.params.descending_scores.iter().filter(|&score| { + if env.params.used_tile_scores_buffer.last() == Some(score) { + env.params.used_tile_scores_buffer.pop(); + false + } else { + true + } + }); + for &idx in + &env.params.indexes_to_descending_square_multiplier_buffer[low_end..high_end] { - best_scoring += *desc_scores_iter.next().unwrap() as i32 - * precomputed_square_multiplier_slice[idx as usize]; - to_assign -= 1; - if to_assign == 0 { - break; + if idx_left <= idx + && idx < idx_right + && env.params.shadow_strip_buffer[idx as usize] == 0 + { + best_scoring += *desc_scores_iter.next().unwrap() as i32 + * precomputed_square_multiplier_slice[idx as usize]; + to_assign -= 1; + if to_assign == 0 { + break; + } } } } @@ -1066,14 +1126,11 @@ fn gen_place_placements<'a, PossibleStripPlacementCallbackType: FnMut(i8, i8, i8 .rack_tally_shadowr .clone_from_slice(env.params.rack_tally_shadowl); loop { - // tail-recurse placing current sequence of tiles - while idx < env.rightmost { - let b = env.params.board_strip[idx as usize]; - if b == 0 { - break; - } - acc.main_score += env.params.face_value_scores_strip[idx as usize] as i32; - idx += 1; + if idx < env.rightmost { + // tail-recurse placing current sequence of tiles in one go + let multi_jump = &env.params.multi_jumps_buffer[idx as usize]; + acc.main_score += multi_jump.right_score; + idx = multi_jump.right_idx; } // tiles have been placed from idx_left to idx - 1. // here idx <= env.rightmost. @@ -1186,14 +1243,11 @@ fn gen_place_placements<'a, PossibleStripPlacementCallbackType: FnMut(i8, i8, i8 .rack_tally_shadowl .clone_from_slice(env.params.rack_tally); loop { - // tail-recurse placing current sequence of tiles - while idx >= env.leftmost { - let b = env.params.board_strip[idx as usize]; - if b == 0 { - break; - } - acc.main_score += env.params.face_value_scores_strip[idx as usize] as i32; - idx -= 1; + if idx >= env.leftmost { + // tail-recurse placing current sequence of tiles in one go + let multi_jump = &env.params.multi_jumps_buffer[idx as usize]; + acc.main_score += multi_jump.left_score; + idx = multi_jump.left_idx; } // tiles have been placed from env.anchor to idx + 1. // here idx >= env.leftmost - 1. @@ -2788,8 +2842,6 @@ fn kurnia_gen_place_moves_iter< remaining_tile_multipliers_strip: &working_buffer .remaining_tile_multipliers_for_across_plays [strip_range_start..strip_range_end], - face_value_scores_strip: &working_buffer.face_value_scores_for_across_plays - [strip_range_start..strip_range_end], perpendicular_word_multipliers_strip: &working_buffer .perpendicular_word_multipliers_for_across_plays [strip_range_start..strip_range_end], @@ -2802,6 +2854,7 @@ fn kurnia_gen_place_moves_iter< .precomputed_square_multiplier_buffer, indexes_to_descending_square_multiplier_buffer: &mut working_buffer .indexes_to_descending_square_multiplier_buffer, + multi_jumps_buffer: &mut working_buffer.multi_jumps_buffer, best_leave_values: &working_buffer.best_leave_values, num_max_played, rack_tally_shadowl: &mut working_buffer.rack_tally_shadowl, @@ -2840,8 +2893,6 @@ fn kurnia_gen_place_moves_iter< .remaining_word_multipliers_for_down_plays[strip_range_start..strip_range_end], remaining_tile_multipliers_strip: &working_buffer .remaining_tile_multipliers_for_down_plays[strip_range_start..strip_range_end], - face_value_scores_strip: &working_buffer.face_value_scores_for_down_plays - [strip_range_start..strip_range_end], perpendicular_word_multipliers_strip: &working_buffer .perpendicular_word_multipliers_for_down_plays [strip_range_start..strip_range_end], @@ -2854,6 +2905,7 @@ fn kurnia_gen_place_moves_iter< .precomputed_square_multiplier_buffer, indexes_to_descending_square_multiplier_buffer: &mut working_buffer .indexes_to_descending_square_multiplier_buffer, + multi_jumps_buffer: &mut working_buffer.multi_jumps_buffer, best_leave_values: &working_buffer.best_leave_values, num_max_played, rack_tally_shadowl: &mut working_buffer.rack_tally_shadowl, diff --git a/src/play_scorer.rs b/src/play_scorer.rs index 611ddec..280ade4 100644 --- a/src/play_scorer.rs +++ b/src/play_scorer.rs @@ -122,7 +122,7 @@ impl PlayScorer { if board_tile != 0 { return_error!("cannot place a tile onto an occupied square".into()); } - let premium = premiums[strider_at_i]; + let premium = &premiums[strider_at_i]; if premium.word_multiplier == 0 && premium.tile_multiplier == 0 { return_error!("cannot place a tile onto a punctured square".into()); } @@ -368,7 +368,7 @@ impl PlayScorer { for (i, &tile) in (*idx..).zip(word.iter()) { let strider_at_i = strider.at(i); let tile_multiplier; - let premium = premiums[strider_at_i]; + let premium = &premiums[strider_at_i]; let placed_tile = if tile != 0 { num_played += 1; word_multiplier *= premium.word_multiplier as i32; @@ -411,7 +411,7 @@ impl PlayScorer { for j in j..perpendicular_strider_len { let perpendicular_strider_at_j = perpendicular_strider.at(j); let tile_multiplier; - let premium = premiums[perpendicular_strider_at_j]; + let premium = &premiums[perpendicular_strider_at_j]; let placed_tile = if j == *lane { word_multiplier *= premium.word_multiplier as i32; tile_multiplier = premium.tile_multiplier; @@ -527,15 +527,15 @@ impl PlayScorer { .filter(|(i, &tile)| { tile != 0 && alphabet.is_vowel(tile) && { (match strider1 { - Some(strider) => { - let premium = premiums[strider.at(*i)]; + Some(ref strider) => { + let premium = &premiums[strider.at(*i)]; premium.tile_multiplier != 1 || premium.word_multiplier != 1 } None => false, }) || (match strider2 { - Some(strider) => { - let premium = premiums[strider.at(*i)]; + Some(ref strider) => { + let premium = &premiums[strider.at(*i)]; premium.tile_multiplier != 1 || premium.word_multiplier != 1 }