diff options
| -rw-r--r-- | challenge-076/xkr47/README | 5 | ||||
| -rw-r--r-- | challenge-076/xkr47/rust/Cargo.toml | 14 | ||||
| -rw-r--r-- | challenge-076/xkr47/rust/ch-2.rs | 109 |
3 files changed, 128 insertions, 0 deletions
diff --git a/challenge-076/xkr47/README b/challenge-076/xkr47/README index 47ff27a9d5..3a820dfa73 100644 --- a/challenge-076/xkr47/README +++ b/challenge-076/xkr47/README @@ -1 +1,6 @@ Solution by Jonas Berlin. + +Install Rust from e.g. https://rustup.rs/ and run the rust solutions like follows: + +cd rust +cargo run --release --bin ch-2 -- grid.txt words.txt diff --git a/challenge-076/xkr47/rust/Cargo.toml b/challenge-076/xkr47/rust/Cargo.toml new file mode 100644 index 0000000000..160be07b12 --- /dev/null +++ b/challenge-076/xkr47/rust/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "pwd-076" +version = "0.1.0" +authors = ["Jonas Berlin <xkr47@outerspace.dyndns.org>"] +edition = "2018" + +[dependencies] +clap = "2.33" +itertools = "0.9" +regex = "1.3" + +[[bin]] +name = "ch-2" +path = "ch-2.rs" diff --git a/challenge-076/xkr47/rust/ch-2.rs b/challenge-076/xkr47/rust/ch-2.rs new file mode 100644 index 0000000000..ab6f8b75ae --- /dev/null +++ b/challenge-076/xkr47/rust/ch-2.rs @@ -0,0 +1,109 @@ +use std::fs; +use std::path::Path; + +use clap::{App, Arg, ArgMatches}; +use itertools::Itertools; +use regex::RegexSetBuilder; + +fn main() { + let args = cli_args(); + + let grid = SearchGrid::new(args.value_of("SEARCHGRID-FILE").unwrap()); + let words = read_words(args.value_of("WORDLIST-FILE").unwrap()); + + let found = grid.find_words(&words); + + found.into_iter().for_each(|word| println!("{}", word)); +} + +struct SearchGrid { + grid: Vec<Vec<char>>, + width: isize, + height: isize, +} + +impl SearchGrid { + fn new<P: AsRef<Path>>(file: P) -> SearchGrid { + let data = fs::read_to_string(file).expect("Failed to read search grid"); + let grid = data.lines() + .map(|line| line.chars() + .filter(|ch| !ch.is_whitespace()) + .collect::<Vec<_>>()) + .collect::<Vec<_>>(); + if 1 != grid.iter().map(|chars| chars.len()).dedup().count() { + panic!("Grid: Not all lines were of equal length"); + } + let height = grid.len() as isize; + if height == 0 { + panic!("Grid: There were no lines"); + } + let width = grid.first().unwrap().len() as isize; + if width == 0 { + panic!("Grid: All lines were empty"); + } + SearchGrid { + grid, + width, + height, + } + } + + fn find_words<'a>(&self, words: &'a[String]) -> Vec<&'a str> { + // a regex set that matches all words + let match_all_words = RegexSetBuilder::new(words.iter() + // for each word, construct a regexp that matches the word in both directions + .map(|word| format!("{}|{}", + regex::escape(word), + regex::escape(word.chars().rev().collect::<String>().as_str())))) + .case_insensitive(true) + .build() + .unwrap(); + + let left_coords = (0..self.height).map(|y| (0isize, y)).collect::<Vec<_>>(); + let top_coords = (0..self.width).map(|x| (x, 0isize)).collect::<Vec<_>>(); + let bottom_coords = (0..self.width).map(|x| (x, self.height - 1)).collect::<Vec<(isize, isize)>>(); + + let mut strings = left_coords.iter().skip(1).map(|coord| self.string_from(coord, (1, -1))).collect::<Vec<String>>(); + strings.extend(left_coords.iter().map(|coord| self.string_from(coord, (1, 0)))); + strings.extend(left_coords.iter().rev().skip(1).map(|coord| self.string_from(coord, (1, 1)))); + strings.extend(top_coords.iter().map(|coord| self.string_from(coord, (0, 1)))); + strings.extend(top_coords.iter().skip(1).map(|coord| self.string_from(coord, (1, 1)))); + strings.extend(bottom_coords.iter().skip(1).map(|coord| self.string_from(coord, (1, -1)))); + + strings.iter() + .flat_map(|str| match_all_words.matches(str)) + .unique() + .map(|idx| words[idx].as_ref()) + .collect() + } + + fn string_from(&self, (xstart, ystart): &(isize, isize), (xinc, yinc): (isize, isize)) -> String { + let x_coords = (0..self.width).map(|pos| xstart + (xinc * pos)); + let y_coords = (0..self.height).map(|pos| ystart + (yinc * pos)); + x_coords.zip(y_coords) + .filter(|(x,y)| *x >= 0 && *y >= 0 && *x < self.width && *y < self.height) + .map(|(x,y)| self.grid[y as usize][x as usize]) + .collect() + } +} + +fn read_words<P: AsRef<Path>>(file: P) -> Vec<String> { + let data = fs::read_to_string(file).expect("Failed to read word list"); + data.lines().map(|str| str.to_string()).collect() +} + +fn cli_args<'a>() -> ArgMatches<'a> { + App::new("pwc076 task 2") + .version("1.0") + .author("Jonas Berlin <xkr47@outerspace.dyndns.org>") + .about("https://perlweeklychallenge.org/blog/perl-weekly-challenge-076/#TASK2") + .arg(Arg::with_name("SEARCHGRID-FILE") + .help("Filename for search grid") + .required(true) + .index(1)) + .arg(Arg::with_name("WORDLIST-FILE") + .help("Filename for word list") + .required(true) + .index(2)) + .get_matches() +} |
