Misc improvements

This commit is contained in:
Matthieu Jolimaitre 2025-06-12 16:46:11 +02:00
parent 5c56fc7540
commit 6e9f041f15
17 changed files with 977752 additions and 468873 deletions

2
.gitignore vendored
View file

@ -1 +1,3 @@
/target /target
/flamegraph.svg
/perf.data*

6
data/chars_of.sh Executable file
View file

@ -0,0 +1,6 @@
#!/usr/bin/bash
set -e
allowed="azertyuiopqsdfghjklmwxcvbn"
cat "$1" | tr '[:upper:]' '[:lower:]' | sed 's/\(.\)/\1\n/g' | sort | uniq | grep -v -E "[$allowed]"

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

78855
data/dict/pli07.txt Normal file

File diff suppressed because it is too large Load diff

386264
data/dict/scrable_2012.txt Normal file

File diff suppressed because it is too large Load diff

3
data/dict_specs.txt Normal file
View file

@ -0,0 +1,3 @@
- abriates
+ revisite
+ captes

View file

@ -21,7 +21,7 @@ struct Args {
fn main() { fn main() {
let args = Args::parse(); let args = Args::parse();
dbg!(&args); dbg!(&args);
let mut dict = dictionnary::scrable(7); let mut dict = dictionnary::scrable_2012(7);
// let dict = dictionnary::francais(7); // let dict = dictionnary::francais(7);
let count = if args.target.is_some() { 1 } else { args.count }; let count = if args.target.is_some() { 1 } else { args.count };
(0..count).for_each(|_| { (0..count).for_each(|_| {
@ -35,12 +35,14 @@ fn main() {
.collect(); .collect();
} }
dbg!(&target); dbg!(&target);
let solver = Grouping::new(dict.clone()); let first_char = target.chars().next().expect("Target has a first letter.");
let dict = dict.iter().filter(|w| w.starts_with(first_char)).cloned();
let solver = Grouping::new(dict);
let mut game = Simulation::new(target.clone()); let mut game = Simulation::new(target.clone());
let result = game.play_all(solver, Some(20)); let result = game.play_all(solver, Some(10));
match result { match result {
Ok((_, tries)) => println!("succ,{target},{tries}"), Ok((_, tries)) => println!("succ,{tries},{target}"),
Err(reason) => println!("fail,{target},{reason}"), Err(reason) => println!("fail,{reason},{target}"),
}; };
}); });
} }

View file

@ -1,26 +1,63 @@
use std::{fs, path::Path}; use std::{fs, ops::Not, path::Path};
use rand::{seq::SliceRandom, thread_rng}; use rand::{seq::SliceRandom, thread_rng};
const ALLOWED: &str = "azertyuiopqsdfghjklmwxcvbn";
const REPLACES: &[(char, &str)] = &[
('à', "a"),
('â', "a"),
('ä', "a"),
('ç', "c"),
('é', "e"),
('è', "e"),
('ê', "e"),
('ë', "e"),
('î', "i"),
('ï', "i"),
('ô', "o"),
('ö', "o"),
('ù', "u"),
('û', "u"),
('ü', "u"),
];
pub fn load(path: impl AsRef<Path>, width: usize) -> Vec<String> { pub fn load(path: impl AsRef<Path>, width: usize) -> Vec<String> {
let content = fs::read_to_string(path).expect("Can read file."); let content = fs::read_to_string(path).expect("Can read file.");
lines_of(width, &content) lines_of(width, &content)
} }
pub fn francais(width: usize) -> Vec<String> { macro_rules! decl_dict {
lines_of(width, include_str!("../data/francais.txt")) ($name:ident) => {
pub fn $name(width: usize) -> Vec<String> {
lines_of(
width,
include_str!(concat!("../data/dict/", stringify!($name), ".txt")),
)
} }
pub fn gutenberg(width: usize) -> Vec<String> { };
lines_of(width, include_str!("../data/gutenberg.txt"))
} }
pub fn scrable(width: usize) -> Vec<String> { decl_dict!(gutenberg);
lines_of(width, include_str!("../data/scrable.txt")) decl_dict!(liste_francais);
decl_dict!(ods4);
decl_dict!(ods5);
decl_dict!(ods6);
decl_dict!(pli07);
decl_dict!(scrable_2012);
fn translate_line(line: &str) -> Option<String> {
let mut content = line.to_lowercase();
for (f, t) in REPLACES {
content = content.replace(*f, t);
}
(content.chars())
.all(|c| ALLOWED.chars().any(|a| a == c))
.then_some(content)
} }
fn lines_of(width: usize, content: &str) -> Vec<String> { fn lines_of(width: usize, content: &str) -> Vec<String> {
let mut res = content let mut res = content
.lines() .lines()
.filter_map(translate_line)
.filter(|w| w.len() == width) .filter(|w| w.len() == width)
.map(|l| l.to_string()) .map(|l| l.to_string())
.collect::<Vec<_>>(); .collect::<Vec<_>>();

View file

@ -89,10 +89,10 @@ pub trait Game {
Some(guess) => match self.guess(dbg!(&guess)) { Some(guess) => match self.guess(dbg!(&guess)) {
Ok(()) => return Ok((guess, try_)), Ok(()) => return Ok((guess, try_)),
Err(infos) => { Err(infos) => {
let printable = Info::as_printable(infos.iter().cloned()) let printed = Info::as_printable(infos.iter().cloned())
.into_iter() .into_iter()
.collect::<String>(); .collect::<String>();
dbg!(printable); dbg!(printed);
solver.learn(infos); solver.learn(infos);
} }
}, },

View file

@ -1,5 +1,3 @@
use rayon::prelude::*;
use crate::game::Info; use crate::game::Info;
use super::{Game, InfoKind}; use super::{Game, InfoKind};

View file

@ -12,7 +12,7 @@ mod solve;
pub fn main() { pub fn main() {
let (mut game, infos) = Proxy::init(); let (mut game, infos) = Proxy::init();
let dict = dictionnary::scrable(infos.len()); let dict = dictionnary::scrable_2012(infos.len());
let mut solver = Grouping::new(dict.into_iter().filter(|w| matches(w, &infos))); let mut solver = Grouping::new(dict.into_iter().filter(|w| matches(w, &infos)));
solver.learn(infos); solver.learn(infos);
let result = game.play_all(solver, Some(6)); let result = game.play_all(solver, Some(6));

View file

@ -12,12 +12,14 @@ mod solve;
fn main() { fn main() {
let first_arg = args().nth(1); let first_arg = args().nth(1);
let dict = dictionnary::scrable(first_arg.clone().map(|w| w.len()).unwrap_or(7)); let dict = dictionnary::scrable_2012(first_arg.clone().map(|w| w.len()).unwrap_or(7));
let random_word = || dict.choose(&mut rand::thread_rng()).unwrap().to_string(); let random_word = || dict.choose(&mut rand::thread_rng()).unwrap().to_string();
let target = first_arg.unwrap_or_else(random_word); let target = first_arg.unwrap_or_else(random_word);
dbg!(&target); dbg!(&target);
let solver = Grouping::new(dict); let first_char = target.chars().next().expect("Target has a first letter.");
let dict = dict.into_iter().filter(|w| w.starts_with(first_char));
let mut game = Simulation::new(target); let mut game = Simulation::new(target);
let solver = Grouping::new(dict);
let result = game.play_all(solver, Some(20)); let result = game.play_all(solver, Some(20));
println!("{result:?}"); println!("{result:?}");
} }

View file

@ -7,7 +7,7 @@ use rand::{
use rayon::iter::{ParallelBridge, ParallelIterator}; use rayon::iter::{ParallelBridge, ParallelIterator};
use crate::{ use crate::{
dictionnary::{self, gutenberg}, dictionnary,
game::{ game::{
simulation::{wordle, wordle_buffs_for, wordle_inner}, simulation::{wordle, wordle_buffs_for, wordle_inner},
Info, InfoKind, Info, InfoKind,
@ -77,11 +77,13 @@ impl Solver for Grouping {
return self.candidates.first().cloned(); return self.candidates.first().cloned();
} }
self.dict.shuffle(&mut thread_rng());
let scored = self let scored = self
.dict .dict
.iter() .iter()
.take(100_000) .take(100_000)
.par_bridge() // .par_bridge()
.map(|word| (word, self.rank_word_avg_groups(word))) .map(|word| (word, self.rank_word_avg_groups(word)))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -150,7 +152,7 @@ fn test_misc() {
dbg!(grouping.rank_word_avg_groups("volitif")); dbg!(grouping.rank_word_avg_groups("volitif"));
dbg!(grouping.rank_word_avg_groups("amorcee")); dbg!(grouping.rank_word_avg_groups("amorcee"));
let mut gut = gutenberg(7); let mut gut = dictionnary::gutenberg(7);
dbg!(gut.len()); dbg!(gut.len());
let mut grouping = Grouping::new(gut.into_iter().filter(|w| w.starts_with('j'))); let mut grouping = Grouping::new(gut.into_iter().filter(|w| w.starts_with('j')));