feat: adding a crossword puzzle generator to the game

This commit is contained in:
Lucas Oskorep 2024-09-02 01:59:39 -04:00
parent f9556804d7
commit 9ba6ae33d3
10 changed files with 94 additions and 26 deletions

22
Cargo.toml Normal file
View File

@ -0,0 +1,22 @@
[package]
name = "regexle"
version = "0.1.0"
edition = "2021"
[dependencies]
csv = "1.3.0"
fancy-regex = "0.13.0"
rayon = "1.10.0"
serde = "1.0.209"
serde_derive = "1.0.209"
wasm_crossword_generator = "0.0.4"
rand = "0.8.5"
[[bin]]
name = "checker"
path = "src/checker/bin/main.rs"
[[bin]]
name = "generator"
path = "src/generator/bin/main.rs"

View File

@ -1,11 +0,0 @@
[package]
name = "regex-checker"
version = "0.1.0"
edition = "2021"
[dependencies]
csv = "1.3.0"
fancy-regex = "0.13.0"
rayon = "1.10.0"
serde = "1.0.209"
serde_derive = "1.0.209"

View File

@ -3,11 +3,8 @@ use std::error::Error;
use fancy_regex::Regex; use fancy_regex::Regex;
use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator}; use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator};
use rayon::iter::ParallelIterator; use rayon::iter::ParallelIterator;
use crate::utils::{read_csv, read_lines, write_to_csv}; use regexle::utils::{read_csv, read_lines, write_to_csv};
use crate::word::{Answer, Word}; use regexle::word::{Answer, Word};
mod word;
mod utils;
fn map_to_answers(regex: String, words: &Vec<Word>) -> Vec<Answer> { fn map_to_answers(regex: String, words: &Vec<Word>) -> Vec<Answer> {
@ -41,7 +38,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let regexes = read_lines(regex_chart)?; let regexes = read_lines(regex_chart)?;
let word_freq_chart = "./word_freq.csv"; let word_freq_chart = "./word_freq.csv";
let mut words = read_csv(word_freq_chart)?; let mut words: Vec<Word> = read_csv(word_freq_chart)?;
words.truncate(10000); words.truncate(10000);
let processed_words: Vec<Word> = words.into_par_iter().filter(|word| word.word.len() > 2).collect(); let processed_words: Vec<Word> = words.into_par_iter().filter(|word| word.word.len() > 2).collect();

51
src/generator/bin/main.rs Normal file
View File

@ -0,0 +1,51 @@
use std::error::Error;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use wasm_crossword_generator::*;
use regexle::utils::read_csv;
use regexle::word::Answer;
fn print_puzzle(puzz: PerWordPuzzle) {
for word in puzz.puzzle.solution.words.iter() {
println!("{:?}", word);
}
puzz.puzzle.grid.iter().for_each(|row| {
row.row.iter().for_each(|cell|
print!("{:?}", cell)
);
println!();
});
}
fn main() -> Result<(), Box<dyn Error>> {
let records: Vec<Answer> = read_csv("processed_answers.csv")?;
let words: Vec<Word> = records.par_iter().map(|ans| Word { text: ans.answer.clone(), clue: Option::from(ans.question.clone()) }).collect();
// A real SolutionConf would probably want "requirements" to allow for retrying crossword
// generation. Because there is only one word, we know we'll get the world's simplest puzzle in
// one try.
let solution_conf = SolutionConf {
words,
max_words: 20,
width: 10,
height: 10,
requirements: None,
initial_placement: None,
};
// A PerWordPuzzle is one where the player only guesses words, not locations. The player is
// immediately informed if the guess is correct or not.
let mut puzzle = PerWordPuzzle::new(solution_conf)?;
// println!("{:?}", puzzle.puzzle.grid);
print_puzzle(puzzle);
// let guess = PlacedWord {
// // Because it is a PerWordPuzzle, the placement is ignored, unlike other Playmodes.
// placement: Placement { x: 0, y: 0, direction: rand::random() },
// // NOTE: you don't need to match the "clue" field, it is ignored for purposes of PartialEq
// word: Word { text: "library".to_string(), clue: None },
// };
// let guess_result = puzzle.guess_word(guess)?;
//
// // Because there is only one word, the guess will result in "Complete" instead of "Correct"
// assert_eq!(guess_result, GuessResult::Complete);
Ok(())
}

3
src/lib.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod utils;
pub mod word;

View File

@ -3,15 +3,19 @@ use std::fs::File;
use std::io::{self, BufRead}; use std::io::{self, BufRead};
use std::path::Path; use std::path::Path;
use csv::{QuoteStyle, ReaderBuilder, WriterBuilder}; use csv::{QuoteStyle, ReaderBuilder, WriterBuilder};
use crate::word::{Answer, Word}; use serde::de::DeserializeOwned;
use serde::{Serialize};
pub fn read_csv(file_path: &str) -> Result<Vec<Word>, Box<dyn Error>> { pub fn read_csv<T>(file_path: &str) -> Result<Vec<T>, Box<dyn Error>>
where
T: DeserializeOwned,
{
let file = File::open(file_path)?; let file = File::open(file_path)?;
let mut rdr = ReaderBuilder::new().has_headers(true).from_reader(file); let mut rdr = ReaderBuilder::new().has_headers(true).from_reader(file);
let mut records = Vec::new(); let mut records = Vec::new();
for result in rdr.deserialize() { for result in rdr.deserialize() {
let record: Word = result?; let record= result?;
records.push(record); records.push(record);
} }
@ -19,7 +23,9 @@ pub fn read_csv(file_path: &str) -> Result<Vec<Word>, Box<dyn Error>> {
} }
pub fn write_to_csv(records: Vec<Answer>, path: &str) -> Result<(), Box<dyn Error>> { pub fn write_to_csv<T>(records: Vec<T>, path: &str) -> Result<(), Box<dyn Error>>
where
T: Serialize,{
// Create a new CSV writer with the file path // Create a new CSV writer with the file path
let file = File::create(path)?; let file = File::create(path)?;
let mut wtr = WriterBuilder::new() let mut wtr = WriterBuilder::new()

View File

@ -2,13 +2,13 @@ use serde_derive::{Deserialize, Serialize};
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct Word { pub struct Word {
pub(crate) word: String, pub word: String,
pub(crate) count: i64, pub count: i64,
} }
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct Answer { pub struct Answer {
pub(crate) question: String, pub question: String,
pub(crate) answer: String, pub answer: String,
pub(crate) count: i64, pub count: i64,
} }

View File

Can't render this file because it is too large.