generated from PlexSheep/rs-base
feat(naive): state building from responses works
cargo devel CI / cargo CI (push) Failing after 2m0s
Details
cargo devel CI / cargo CI (push) Failing after 2m0s
Details
This commit is contained in:
parent
29ec8bf219
commit
f511e158cd
|
@ -32,6 +32,11 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> {
|
||||||
/// * Discard all words that don't have the chars that we know from the last guess are in the
|
/// * Discard all words that don't have the chars that we know from the last guess are in the
|
||||||
/// word, but don't know the position of.
|
/// word, but don't know the position of.
|
||||||
fn guess_for(&self, game: &crate::game::Game<WL>) -> WResult<Word> {
|
fn guess_for(&self, game: &crate::game::Game<WL>) -> WResult<Word> {
|
||||||
|
if game.responses().is_empty() {
|
||||||
|
// TODO: come up with a proper way to make the first guess
|
||||||
|
return Ok(Word::from("which"));
|
||||||
|
}
|
||||||
|
|
||||||
let mut pattern: String = ".".repeat(game.length());
|
let mut pattern: String = ".".repeat(game.length());
|
||||||
// indexes we tried for that char and the number of occurences
|
// indexes we tried for that char and the number of occurences
|
||||||
let mut state: SolverState = SolverState::new();
|
let mut state: SolverState = SolverState::new();
|
||||||
|
@ -43,24 +48,33 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> {
|
||||||
Status::Matched => {
|
Status::Matched => {
|
||||||
pattern.replace_range(idx..idx + 1, &p.0.to_string());
|
pattern.replace_range(idx..idx + 1, &p.0.to_string());
|
||||||
|
|
||||||
state.char_map_mut().entry(p.0).or_default().found_at(idx);
|
state
|
||||||
}
|
|
||||||
Status::Exists => state
|
|
||||||
.char_map_mut()
|
.char_map_mut()
|
||||||
.entry(p.0)
|
.entry(p.0)
|
||||||
.or_default()
|
.or_insert(CharInfo::new(game.length()))
|
||||||
.at_least_n_occurences(
|
.found_at(idx);
|
||||||
|
}
|
||||||
|
Status::Exists => {
|
||||||
|
let cinfo = state
|
||||||
|
.char_map_mut()
|
||||||
|
.entry(p.0)
|
||||||
|
.or_insert(CharInfo::new(game.length()));
|
||||||
|
cinfo.tried_but_failed(idx);
|
||||||
|
cinfo.at_least_n_occurences(
|
||||||
response.guess().chars().filter(|c| *c == p.0).count(),
|
response.guess().chars().filter(|c| *c == p.0).count(),
|
||||||
),
|
)
|
||||||
|
}
|
||||||
Status::None => state
|
Status::None => state
|
||||||
.char_map_mut()
|
.char_map_mut()
|
||||||
.entry(p.0)
|
.entry(p.0)
|
||||||
.or_default()
|
.or_insert(CharInfo::new(game.length()))
|
||||||
.not_in_solution(),
|
.not_in_solution(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!("built state from responses: {state:#?}");
|
||||||
|
|
||||||
// get all words that have the correct chars on the same positions
|
// get all words that have the correct chars on the same positions
|
||||||
let mut matches: Vec<WordData> = game.wordlist().get_words_matching(&pattern)?;
|
let mut matches: Vec<WordData> = game.wordlist().get_words_matching(&pattern)?;
|
||||||
if matches.is_empty() {
|
if matches.is_empty() {
|
||||||
|
@ -72,11 +86,13 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> {
|
||||||
.filter(|p| !game.made_guesses().contains(&&p.0))
|
.filter(|p| !game.made_guesses().contains(&&p.0))
|
||||||
.filter(|solution_candidate| {
|
.filter(|solution_candidate| {
|
||||||
for (idx, c) in solution_candidate.0.char_indices() {
|
for (idx, c) in solution_candidate.0.char_indices() {
|
||||||
let cinfo = state.char_map_mut().entry(c).or_default();
|
let cinfo = state
|
||||||
// bad word if it uses a char thats not in the solution
|
.char_map_mut()
|
||||||
|
.entry(c)
|
||||||
|
.or_insert(CharInfo::new(game.length()));
|
||||||
if !cinfo.part_of_solution()
|
if !cinfo.part_of_solution()
|
||||||
|| cinfo.has_been_tried(idx)
|
|| cinfo.has_been_tried(idx)
|
||||||
|| cinfo.occurences_of_char_possible(&solution_candidate.0, c)
|
|| !cinfo.occurences_of_char_possible(&solution_candidate.0, c)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::ops::{Range, RangeBounds};
|
use std::ops::{Range, RangeBounds};
|
||||||
|
|
||||||
use crate::error::WResult;
|
use crate::error::WResult;
|
||||||
|
|
||||||
pub(crate) type CharMap = HashMap<char, CharInfo>;
|
pub(crate) type CharMap = HashMap<char, CharInfo>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub(crate) struct CharInfo {
|
pub(crate) struct CharInfo {
|
||||||
confirmed_indexes: HashSet<usize>,
|
confirmed_indexes: HashSet<usize>,
|
||||||
tried_indexes: HashSet<usize>,
|
tried_indexes: HashSet<usize>,
|
||||||
|
@ -34,8 +35,12 @@ impl SolverState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CharInfo {
|
impl CharInfo {
|
||||||
pub fn new() -> Self {
|
pub fn new(word_length: usize) -> Self {
|
||||||
Self::default()
|
Self {
|
||||||
|
confirmed_indexes: HashSet::new(),
|
||||||
|
tried_indexes: HashSet::new(),
|
||||||
|
occurences_amount: 0..word_length,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn found_at(&mut self, idx: usize) {
|
pub fn found_at(&mut self, idx: usize) {
|
||||||
|
@ -108,3 +113,17 @@ impl CharInfo {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for CharInfo {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if self.part_of_solution() {
|
||||||
|
f.debug_struct("CharInfo")
|
||||||
|
.field("indexes", &self.confirmed_indexes)
|
||||||
|
.field("amnt_occ", &self.occurences_amount)
|
||||||
|
.field("tried", &self.tried_indexes)
|
||||||
|
.finish()
|
||||||
|
} else {
|
||||||
|
write!(f, "(not in solution)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue