separate into packages & start on actual game
This commit is contained in:
parent
76ff849a8c
commit
45289ac8ab
20 changed files with 482 additions and 72 deletions
249
Cargo.lock
generated
249
Cargo.lock
generated
|
@ -2,34 +2,88 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console_error_panic_hook"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
|
checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if 1.0.0",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.56"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.119"
|
version = "0.2.119"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
|
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memory_units"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "optimle"
|
name = "optimle"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand",
|
"rand",
|
||||||
|
"rand_chacha",
|
||||||
|
"wordlelike",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -38,6 +92,24 @@ version = "0.2.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
|
@ -68,8 +140,183 @@ dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scoped-tls"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.10.2+wasi-snapshot-preview1"
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-futures"
|
||||||
|
version = "0.4.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-test"
|
||||||
|
version = "0.3.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "45c8d417d87eefa0087e62e3c75ad086be39433449e2961add9a5d9ce5acc2f1"
|
||||||
|
dependencies = [
|
||||||
|
"console_error_panic_hook",
|
||||||
|
"js-sys",
|
||||||
|
"scoped-tls",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"wasm-bindgen-test-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-test-macro"
|
||||||
|
version = "0.3.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0e560d44db5e73b69a9757a15512fe7e1ef93ed2061c928871a4025798293dd"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-optimle"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"console_error_panic_hook",
|
||||||
|
"optimle",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-test",
|
||||||
|
"wee_alloc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.56"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wee_alloc"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.10",
|
||||||
|
"libc",
|
||||||
|
"memory_units",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wordlelike"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
15
Cargo.toml
15
Cargo.toml
|
@ -1,9 +1,6 @@
|
||||||
[package]
|
[workspace]
|
||||||
name = "optimle"
|
members = [
|
||||||
version = "0.1.0"
|
"wordlelike",
|
||||||
edition = "2018"
|
"optimle",
|
||||||
|
"web-optimle",
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
]
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
rand = "0.8.5"
|
|
13
optimle/Cargo.toml
Normal file
13
optimle/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "optimle"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["ashastral"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["rlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rand = "0.8.5"
|
||||||
|
rand_chacha = "0.3.1"
|
||||||
|
wordlelike = { path = "../wordlelike" }
|
90
optimle/src/lib.rs
Normal file
90
optimle/src/lib.rs
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
use rand_chacha::ChaCha8Rng;
|
||||||
|
use rand_chacha::rand_core::SeedableRng;
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
|
|
||||||
|
use wordlelike::constraints;
|
||||||
|
use wordlelike::constraints::Constraints;
|
||||||
|
use wordlelike::evaluator;
|
||||||
|
use wordlelike::evaluator::Evaluator;
|
||||||
|
use wordlelike::game;
|
||||||
|
use wordlelike::guess::*;
|
||||||
|
use wordlelike::guesser;
|
||||||
|
|
||||||
|
pub struct Puzzle<const N: usize> {
|
||||||
|
pub history: GuessHistory<N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> Puzzle<N> {
|
||||||
|
pub fn new(dictionary: &Dictionary<N>, seed: u64) -> Self {
|
||||||
|
let mut rng: ChaCha8Rng = ChaCha8Rng::seed_from_u64(seed);
|
||||||
|
let mut history: Option<GuessHistory<N>> = None;
|
||||||
|
loop {
|
||||||
|
let initial_secret = dictionary.choose(&mut rng).unwrap();
|
||||||
|
let constraints = &mut constraints::Total::new();
|
||||||
|
let mut game = game::Game {
|
||||||
|
evaluator: &mut evaluator::SecretEvaluator::new(*initial_secret),
|
||||||
|
guesser: &mut guesser::RandomConstraintGuesser::new(dictionary, constraints, &mut rng),
|
||||||
|
};
|
||||||
|
let mut game_history = Vec::new();
|
||||||
|
loop {
|
||||||
|
let result = game.play_round();
|
||||||
|
if result.correct() { break; }
|
||||||
|
game.push(result);
|
||||||
|
game_history.push(result);
|
||||||
|
if game_history.len() > 2 {
|
||||||
|
if result.matches
|
||||||
|
.iter()
|
||||||
|
.filter(|&m| *m == LetterMatch::HERE)
|
||||||
|
.count() < 3 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let possible_words = game.guesser.get_possible_words();
|
||||||
|
if Puzzle::interesting(dictionary, &game_history, possible_words) {
|
||||||
|
history = Some(game_history);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if history.is_some() { break; }
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
history: history.unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interesting(dictionary: &Dictionary<N>, history: &GuessHistory<N>, possible_words: Dictionary<N>) -> bool {
|
||||||
|
let count = possible_words.len();
|
||||||
|
if count < 3 || count > 6 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let base_constraints = &mut constraints::Total::new();
|
||||||
|
let constraints = &mut constraints::Total::new();
|
||||||
|
let mut evaluator = evaluator::AdversarialEvaluator::new(dictionary);
|
||||||
|
for &result in history {
|
||||||
|
base_constraints.push(result);
|
||||||
|
evaluator.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut solutions = Vec::new();
|
||||||
|
for &word in dictionary {
|
||||||
|
constraints.clone_from(base_constraints);
|
||||||
|
let matches = evaluator.evaluate(word);
|
||||||
|
constraints.push(GuessResult { word, matches });
|
||||||
|
let remaining_words = possible_words
|
||||||
|
.iter()
|
||||||
|
.filter(|&word| constraints.check(*word))
|
||||||
|
.count();
|
||||||
|
if remaining_words == 1 {
|
||||||
|
solutions.push(word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let solution_count = solutions.len();
|
||||||
|
let interesting = solution_count > 0 && solution_count <= 100;
|
||||||
|
if interesting {
|
||||||
|
println!("Could be: {:?}", possible_words);
|
||||||
|
}
|
||||||
|
interesting
|
||||||
|
}
|
||||||
|
}
|
10
optimle/src/main.rs
Normal file
10
optimle/src/main.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
use optimle::Puzzle;
|
||||||
|
use wordlelike::guess::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let common_words: Vec<Word<5>> = wordlelike::load::load_words(include_str!("../../wordlelike/src/words/common.txt"));
|
||||||
|
let puzzle = Puzzle::new(&common_words, 0);
|
||||||
|
for result in puzzle.history {
|
||||||
|
println!("{}", result);
|
||||||
|
}
|
||||||
|
}
|
52
src/main.rs
52
src/main.rs
|
@ -1,52 +0,0 @@
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! word {
|
|
||||||
( $x:expr ) => {
|
|
||||||
{
|
|
||||||
$x.chars()
|
|
||||||
.collect::<Vec<char>>()
|
|
||||||
.as_slice()
|
|
||||||
.try_into()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod guess;
|
|
||||||
use guess::*;
|
|
||||||
|
|
||||||
mod constraints;
|
|
||||||
mod game;
|
|
||||||
mod guesser;
|
|
||||||
mod evaluator;
|
|
||||||
|
|
||||||
|
|
||||||
fn play<const N: usize>(dictionary: &Dictionary<N>) {
|
|
||||||
let mut constraints = constraints::Total::new();
|
|
||||||
let mut game = game::Game {
|
|
||||||
evaluator: &mut evaluator::AdversarialEvaluator::new(dictionary),
|
|
||||||
guesser: &mut guesser::RandomConstraintGuesser::new(dictionary, &mut constraints),
|
|
||||||
};
|
|
||||||
loop {
|
|
||||||
let result = game.next();
|
|
||||||
println!("{}", result);
|
|
||||||
if result.correct() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_words<const N: usize>(contents: &'static str) -> Vec<Word<N>> {
|
|
||||||
contents
|
|
||||||
.split("\n")
|
|
||||||
.map(|line| word!(line))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let common_words: Vec<Word<5>> = load_words(include_str!("words/common.txt"));
|
|
||||||
loop {
|
|
||||||
play(&common_words);
|
|
||||||
}
|
|
||||||
}
|
|
23
web-optimle/Cargo.toml
Normal file
23
web-optimle/Cargo.toml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
[package]
|
||||||
|
name = "web-optimle"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["ashastral"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["console_error_panic_hook"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
wasm-bindgen = "0.2.63"
|
||||||
|
console_error_panic_hook = { version = "0.1.6", optional = true }
|
||||||
|
wee_alloc = { version = "0.4.5", optional = true }
|
||||||
|
optimle = { path = "../optimle" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
wasm-bindgen-test = "0.3.13"
|
||||||
|
|
||||||
|
[profie.release]
|
||||||
|
opt-level = "s"
|
0
web-optimle/src/lib.rs
Normal file
0
web-optimle/src/lib.rs
Normal file
11
wordlelike/Cargo.toml
Normal file
11
wordlelike/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "wordlelike"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["ashastral"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["rlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rand = "0.8.5"
|
|
@ -1,4 +1,3 @@
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use crate::guess::*;
|
use crate::guess::*;
|
||||||
|
@ -14,6 +13,7 @@ pub struct SecretEvaluator<const N: usize> {
|
||||||
secret: Word<N>,
|
secret: Word<N>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluates guesses according to the provided secret word.
|
||||||
impl<const N: usize> SecretEvaluator<N> {
|
impl<const N: usize> SecretEvaluator<N> {
|
||||||
pub fn new(secret: Word<N>) -> Self {
|
pub fn new(secret: Word<N>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -28,6 +28,9 @@ impl<const N: usize> Evaluator<N> for SecretEvaluator<N> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Evaluates guesses "adversarially", that is, in a way that maximizes the
|
||||||
|
/// remaining search space after each guess.
|
||||||
pub struct AdversarialEvaluator<const N: usize> {
|
pub struct AdversarialEvaluator<const N: usize> {
|
||||||
possible_words: Dictionary<N>,
|
possible_words: Dictionary<N>,
|
||||||
constraints: constraints::Total<N>,
|
constraints: constraints::Total<N>,
|
||||||
|
@ -84,6 +87,7 @@ impl<const N: usize> Evaluator<N> for AdversarialEvaluator<N> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_evaluates_adversarially() {
|
fn it_evaluates_adversarially() {
|
|
@ -8,12 +8,15 @@ pub struct Game<'a, const N: usize> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, const N: usize> Game<'a, N> {
|
impl<'a, const N: usize> Game<'a, N> {
|
||||||
pub fn next(&mut self) -> GuessResult<N> {
|
pub fn play_round(&mut self) -> GuessResult<N> {
|
||||||
let word = self.guesser.guess();
|
let word = self.guesser.guess();
|
||||||
let matches = self.evaluator.evaluate(word);
|
let matches = self.evaluator.evaluate(word);
|
||||||
let result = GuessResult { word, matches };
|
let result = GuessResult { word, matches };
|
||||||
self.guesser.push(result);
|
|
||||||
self.evaluator.push(result);
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, result: GuessResult<N>) {
|
||||||
|
self.guesser.push(result);
|
||||||
|
self.evaluator.push(result);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,3 @@
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum LetterMatch {
|
pub enum LetterMatch {
|
||||||
NOWHERE,
|
NOWHERE,
|
||||||
|
@ -77,6 +75,7 @@ pub type Dictionary<const N: usize> = Vec<Word<N>>;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_matches_correct_guess() {
|
fn it_matches_correct_guess() {
|
|
@ -1,35 +1,39 @@
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
|
use rand::RngCore;
|
||||||
use crate::guess::*;
|
use crate::guess::*;
|
||||||
use crate::constraints;
|
use crate::constraints;
|
||||||
|
|
||||||
pub trait Guesser<const N: usize> {
|
pub trait Guesser<const N: usize> {
|
||||||
fn push(&mut self, _result: GuessResult<N>) {}
|
fn push(&mut self, _result: GuessResult<N>) {}
|
||||||
fn guess(&self) -> Word<N>;
|
fn guess(&mut self) -> Word<N>;
|
||||||
|
fn get_possible_words(&self) -> Dictionary<N>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RandomConstraintGuesser<'a, const N: usize> {
|
pub struct RandomConstraintGuesser<'a, const N: usize> {
|
||||||
possible_words: Dictionary<N>,
|
possible_words: Dictionary<N>,
|
||||||
history: GuessHistory<N>,
|
history: GuessHistory<N>,
|
||||||
constraints: &'a mut dyn constraints::Constraints<N>,
|
constraints: &'a mut dyn constraints::Constraints<N>,
|
||||||
|
rng: &'a mut dyn RngCore,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, const N: usize> RandomConstraintGuesser<'a, N> {
|
impl<'a, const N: usize> RandomConstraintGuesser<'a, N> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
dictionary: &Dictionary<N>,
|
dictionary: &Dictionary<N>,
|
||||||
constraints: &'a mut dyn constraints::Constraints<N>,
|
constraints: &'a mut dyn constraints::Constraints<N>,
|
||||||
|
rng: &'a mut dyn RngCore,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
possible_words: dictionary.to_vec(),
|
possible_words: dictionary.to_vec(),
|
||||||
history: GuessHistory::new(),
|
history: GuessHistory::new(),
|
||||||
constraints,
|
constraints,
|
||||||
|
rng,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, const N: usize> Guesser<N> for RandomConstraintGuesser<'a, N> {
|
impl<'a, const N: usize> Guesser<N> for RandomConstraintGuesser<'a, N> {
|
||||||
fn guess(&self) -> Word<N> {
|
fn guess(&mut self) -> Word<N> {
|
||||||
*self.possible_words.choose(&mut rand::thread_rng()).unwrap()
|
*self.possible_words.choose(&mut self.rng).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push(&mut self, result: GuessResult<N>) {
|
fn push(&mut self, result: GuessResult<N>) {
|
||||||
|
@ -38,4 +42,8 @@ impl<'a, const N: usize> Guesser<N> for RandomConstraintGuesser<'a, N> {
|
||||||
let constraints = &self.constraints;
|
let constraints = &self.constraints;
|
||||||
self.possible_words.retain(|&word| constraints.check(word));
|
self.possible_words.retain(|&word| constraints.check(word));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_possible_words(&self) -> Dictionary<N> {
|
||||||
|
return self.possible_words.to_vec();
|
||||||
|
}
|
||||||
}
|
}
|
8
wordlelike/src/lib.rs
Normal file
8
wordlelike/src/lib.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#[macro_use]
|
||||||
|
pub mod macros;
|
||||||
|
pub mod guess;
|
||||||
|
pub mod constraints;
|
||||||
|
pub mod game;
|
||||||
|
pub mod guesser;
|
||||||
|
pub mod evaluator;
|
||||||
|
pub mod load;
|
11
wordlelike/src/load.rs
Normal file
11
wordlelike/src/load.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use crate::guess::*;
|
||||||
|
|
||||||
|
|
||||||
|
pub fn load_words<const N: usize>(contents: &'static str) -> Vec<Word<N>> {
|
||||||
|
contents
|
||||||
|
.split("\n")
|
||||||
|
.map(|line| word!(line))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
12
wordlelike/src/macros.rs
Normal file
12
wordlelike/src/macros.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! word {
|
||||||
|
( $x:expr ) => {
|
||||||
|
{
|
||||||
|
$x.chars()
|
||||||
|
.collect::<Vec<char>>()
|
||||||
|
.as_slice()
|
||||||
|
.try_into()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
wordlelike/src/main.rs
Normal file
26
wordlelike/src/main.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use wordlelike::*;
|
||||||
|
use wordlelike::guess::*;
|
||||||
|
|
||||||
|
fn play<const N: usize>(dictionary: &Dictionary<N>) {
|
||||||
|
let mut constraints = constraints::Total::new();
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let mut game = game::Game {
|
||||||
|
evaluator: &mut evaluator::AdversarialEvaluator::new(dictionary),
|
||||||
|
guesser: &mut guesser::RandomConstraintGuesser::new(dictionary, &mut constraints, &mut rng),
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
let result = game.play_round();
|
||||||
|
println!("{}", result);
|
||||||
|
if result.correct() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
game.push(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let common_words: Vec<Word<5>> = load::load_words(include_str!("words/common.txt"));
|
||||||
|
loop {
|
||||||
|
play(&common_words);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue