type RowIndex = isize;
type ColIndex = isize;
#[derive(Debug, Clone)]
struct WordSearch {
letters: FxHashMap< ( RowIndex, ColIndex) , char>,
bounds: ( RowIndex, ColIndex) ,
}
impl WordSearch {
fn from_str( input: & str) -> Self {
let mut letters = FxHashMap:: default ( ) ;
for ( row, line) in input.lines ( ) .enumerate ( ) {
for ( col, letter) in line.chars ( ) .enumerate ( ) {
letters.insert ( ( row as RowIndex, col as ColIndex) , letter) ;
}
}
Self {
letters,
bounds: (
input.lines ( ) .count ( ) as isize,
input.lines ( ) .next ( ) .unwrap ( ) .chars ( ) .count ( ) as isize,
) ,
}
}
fn search_at( & self, row_ind: RowIndex, col_ind: ColIndex, word: & str) -> usize {
let directions = vec! [
Direction:: Up ,
Direction:: Down ,
Direction:: Left ,
Direction:: Right ,
Direction:: UpLeft ,
Direction:: UpRight ,
Direction:: DownLeft ,
Direction:: DownRight ,
] ;
directions
.iter ( )
.map ( | dir| {
dir.get_indices ( row_ind, col_ind, word.len ( ) )
.iter ( )
.map ( |& ( row, col) | self.letters .get ( & ( row, col) ) )
.collect ::< Vec< Option<& char>>> ( )
} )
.filter ( | letters| {
letters.iter ( ) .all ( Option:: is_some )
&& letters
.into_iter ( )
.flatten ( )
.map ( | c| ** c)
.collect ::< String> ( )
== word
} )
.count ( )
}
fn search( & self, word: & str) -> usize {
( 0 ..self .bounds .0)
.flat_map ( | row| ( 0 ..self .bounds .1) .map ( move | col| self.search_at ( row, col, word) ) )
.sum ( )
}
fn convert_indices_to_vec(
& self,
indices: impl Iterator< Item = ( i32, i32) >,
) -> Vec< Option<& char>> {
indices
.map ( | ( row, col) | self.letters .get ( & ( row as isize, col as isize) ) )
.collect ::< Vec< Option<& char>>> ( )
}
fn cross_mas_search( & self) -> usize {
let mut count = 0 ;
for row in 0 ..self .bounds .0 {
for col in 0 ..self .bounds .1 {
if self.letters .get ( & ( row, col) ) == Some( & 'A' ) {
let cross_letters = vec! [
self.letters .get ( & ( row - 1 , col - 1 ) ) ,
self.letters .get ( & ( row - 1 , col + 1 ) ) ,
self.letters .get ( & ( row + 1 , col - 1 ) ) ,
self.letters .get ( & ( row + 1 , col + 1 ) ) ,
] ;
if cross_letters
.iter ( )
.all ( |& c| c.is_some ( ) && ( c.unwrap ( ) == & 'S' || c.unwrap ( ) == & 'M' ) )
{
let cross_word = cross_letters
.iter ( )
.map ( |& c| c.unwrap ( ) )
.collect ::< String> ( ) ;
if cross_word == "SMMS" || cross_word == "MSSM" {
} else {
let counts = cross_word.chars ( ) .counts ( ) ;
if counts.get ( & 'S' ) == Some( & 2 ) && counts.get ( & 'M' ) == Some( & 2 ) {
count += 1 ;
}
}
}
}
}
}
count
}
}
fn convert_indices_to_vec(
indices: impl Iterator< Item = ( i32, i32) >,
letters: & FxHashMap< ( RowIndex, ColIndex) , char>,
) -> Vec< Option<& char>> {
indices
.map ( | ( row, col) | letters.get ( & ( row as isize, col as isize) ) )
.collect ::< Vec< Option<& char>>> ( )
}
fn check_letters( letters: Vec< Option<& char>>, word: & str) -> bool {
letters.iter ( ) .all ( Option:: is_some ) && letters.into_iter ( ) .flatten ( ) .collect ::< String> ( ) == word
}
#[derive(Debug, Clone, Copy)]
enum Direction {
Up,
UpLeft,
UpRight,
Down,
DownLeft,
DownRight,
Left,
Right,
}
impl Direction {
fn get_indices( & self, row: RowIndex, col: ColIndex, word_len: usize) -> Vec< ( isize, isize) > {
let word_len = word_len as isize;
match self {
Direction:: Up => ( 0 ..word_len ) .map ( | i| ( row - i, col) ) .collect ( ) ,
Direction:: UpLeft => ( 0 ..word_len ) .map ( | i| ( row - i, col - i) ) .collect ( ) ,
Direction:: UpRight => ( 0 ..word_len ) .map ( | i| ( row - i, col + i) ) .collect ( ) ,
Direction:: Down => ( 0 ..word_len ) .map ( | i| ( row + i, col) ) .collect ( ) ,
Direction:: DownLeft => ( 0 ..word_len ) .map ( | i| ( row + i, col - i) ) .collect ( ) ,
Direction:: DownRight => ( 0 ..word_len ) .map ( | i| ( row + i, col + i) ) .collect ( ) ,
Direction:: Left => ( 0 ..word_len ) .map ( | i| ( row, col - i) ) .collect ( ) ,
Direction:: Right => ( 0 ..word_len ) .map ( | i| ( row, col + i) ) .collect ( ) ,
}
}
}
dHlwZSBSb3dJbmRleCA9IGlzaXplOwp0eXBlIENvbEluZGV4ID0gaXNpemU7CgojW2Rlcml2ZShEZWJ1ZywgQ2xvbmUpXQpzdHJ1Y3QgV29yZFNlYXJjaCB7CiAgICBsZXR0ZXJzOiBGeEhhc2hNYXA8KFJvd0luZGV4LCBDb2xJbmRleCksIGNoYXI+LAogICAgYm91bmRzOiAoUm93SW5kZXgsIENvbEluZGV4KSwKfQoKaW1wbCBXb3JkU2VhcmNoIHsKICAgIGZuIGZyb21fc3RyKGlucHV0OiAmc3RyKSAtPiBTZWxmIHsKICAgICAgICBsZXQgbXV0IGxldHRlcnMgPSBGeEhhc2hNYXA6OmRlZmF1bHQoKTsKICAgICAgICBmb3IgKHJvdywgbGluZSkgaW4gaW5wdXQubGluZXMoKS5lbnVtZXJhdGUoKSB7CiAgICAgICAgICAgIGZvciAoY29sLCBsZXR0ZXIpIGluIGxpbmUuY2hhcnMoKS5lbnVtZXJhdGUoKSB7CiAgICAgICAgICAgICAgICBsZXR0ZXJzLmluc2VydCgocm93IGFzIFJvd0luZGV4LCBjb2wgYXMgQ29sSW5kZXgpLCBsZXR0ZXIpOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIFNlbGYgewogICAgICAgICAgICBsZXR0ZXJzLAogICAgICAgICAgICBib3VuZHM6ICgKICAgICAgICAgICAgICAgIGlucHV0LmxpbmVzKCkuY291bnQoKSBhcyBpc2l6ZSwKICAgICAgICAgICAgICAgIGlucHV0LmxpbmVzKCkubmV4dCgpLnVud3JhcCgpLmNoYXJzKCkuY291bnQoKSBhcyBpc2l6ZSwKICAgICAgICAgICAgKSwKICAgICAgICB9CiAgICB9CgogICAgZm4gc2VhcmNoX2F0KCZzZWxmLCByb3dfaW5kOiBSb3dJbmRleCwgY29sX2luZDogQ29sSW5kZXgsIHdvcmQ6ICZzdHIpIC0+IHVzaXplIHsKICAgICAgICBsZXQgZGlyZWN0aW9ucyA9IHZlYyFbCiAgICAgICAgICAgIERpcmVjdGlvbjo6VXAsCiAgICAgICAgICAgIERpcmVjdGlvbjo6RG93biwKICAgICAgICAgICAgRGlyZWN0aW9uOjpMZWZ0LAogICAgICAgICAgICBEaXJlY3Rpb246OlJpZ2h0LAogICAgICAgICAgICBEaXJlY3Rpb246OlVwTGVmdCwKICAgICAgICAgICAgRGlyZWN0aW9uOjpVcFJpZ2h0LAogICAgICAgICAgICBEaXJlY3Rpb246OkRvd25MZWZ0LAogICAgICAgICAgICBEaXJlY3Rpb246OkRvd25SaWdodCwKICAgICAgICBdOwoKICAgICAgICBkaXJlY3Rpb25zCiAgICAgICAgICAgIC5pdGVyKCkKICAgICAgICAgICAgLm1hcCh8ZGlyfCB7CiAgICAgICAgICAgICAgICBkaXIuZ2V0X2luZGljZXMocm93X2luZCwgY29sX2luZCwgd29yZC5sZW4oKSkKICAgICAgICAgICAgICAgICAgICAuaXRlcigpCiAgICAgICAgICAgICAgICAgICAgLm1hcCh8Jihyb3csIGNvbCl8IHNlbGYubGV0dGVycy5nZXQoJihyb3csIGNvbCkpKQogICAgICAgICAgICAgICAgICAgIC5jb2xsZWN0Ojo8VmVjPE9wdGlvbjwmY2hhcj4+PigpCiAgICAgICAgICAgIH0pCiAgICAgICAgICAgIC5maWx0ZXIofGxldHRlcnN8IHsKICAgICAgICAgICAgICAgIGxldHRlcnMuaXRlcigpLmFsbChPcHRpb246OmlzX3NvbWUpCiAgICAgICAgICAgICAgICAgICAgJiYgbGV0dGVycwogICAgICAgICAgICAgICAgICAgICAgICAuaW50b19pdGVyKCkKICAgICAgICAgICAgICAgICAgICAgICAgLmZsYXR0ZW4oKQogICAgICAgICAgICAgICAgICAgICAgICAubWFwKHxjfCAqKmMpCiAgICAgICAgICAgICAgICAgICAgICAgIC5jb2xsZWN0Ojo8U3RyaW5nPigpCiAgICAgICAgICAgICAgICAgICAgICAgID09IHdvcmQKICAgICAgICAgICAgfSkKICAgICAgICAgICAgLmNvdW50KCkKICAgIH0KCiAgICBmbiBzZWFyY2goJnNlbGYsIHdvcmQ6ICZzdHIpIC0+IHVzaXplIHsKICAgICAgICAoMC4uc2VsZi5ib3VuZHMuMCkKICAgICAgICAgICAgLmZsYXRfbWFwKHxyb3d8ICgwLi5zZWxmLmJvdW5kcy4xKS5tYXAobW92ZSB8Y29sfCBzZWxmLnNlYXJjaF9hdChyb3csIGNvbCwgd29yZCkpKQogICAgICAgICAgICAuc3VtKCkKICAgIH0KCiAgICBmbiBjb252ZXJ0X2luZGljZXNfdG9fdmVjKAogICAgICAgICZzZWxmLAogICAgICAgIGluZGljZXM6IGltcGwgSXRlcmF0b3I8SXRlbSA9IChpMzIsIGkzMik+LAogICAgKSAtPiBWZWM8T3B0aW9uPCZjaGFyPj4gewogICAgICAgIGluZGljZXMKICAgICAgICAgICAgLm1hcCh8KHJvdywgY29sKXwgc2VsZi5sZXR0ZXJzLmdldCgmKHJvdyBhcyBpc2l6ZSwgY29sIGFzIGlzaXplKSkpCiAgICAgICAgICAgIC5jb2xsZWN0Ojo8VmVjPE9wdGlvbjwmY2hhcj4+PigpCiAgICB9CgogICAgZm4gY3Jvc3NfbWFzX3NlYXJjaCgmc2VsZikgLT4gdXNpemUgewogICAgICAgIGxldCBtdXQgY291bnQgPSAwOwogICAgICAgIGZvciByb3cgaW4gMC4uc2VsZi5ib3VuZHMuMCB7CiAgICAgICAgICAgIGZvciBjb2wgaW4gMC4uc2VsZi5ib3VuZHMuMSB7CiAgICAgICAgICAgICAgICBpZiBzZWxmLmxldHRlcnMuZ2V0KCYocm93LCBjb2wpKSA9PSBTb21lKCYnQScpIHsKICAgICAgICAgICAgICAgICAgICBsZXQgY3Jvc3NfbGV0dGVycyA9IHZlYyFbCiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYubGV0dGVycy5nZXQoJihyb3cgLSAxLCBjb2wgLSAxKSksCiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYubGV0dGVycy5nZXQoJihyb3cgLSAxLCBjb2wgKyAxKSksCiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYubGV0dGVycy5nZXQoJihyb3cgKyAxLCBjb2wgLSAxKSksCiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYubGV0dGVycy5nZXQoJihyb3cgKyAxLCBjb2wgKyAxKSksCiAgICAgICAgICAgICAgICAgICAgXTsKICAgICAgICAgICAgICAgICAgICBpZiBjcm9zc19sZXR0ZXJzCiAgICAgICAgICAgICAgICAgICAgICAgIC5pdGVyKCkKICAgICAgICAgICAgICAgICAgICAgICAgLmFsbCh8JmN8IGMuaXNfc29tZSgpICYmIChjLnVud3JhcCgpID09ICYnUycgfHwgYy51bndyYXAoKSA9PSAmJ00nKSkKICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgIGxldCBjcm9zc193b3JkID0gY3Jvc3NfbGV0dGVycwogICAgICAgICAgICAgICAgICAgICAgICAgICAgLml0ZXIoKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLm1hcCh8JmN8IGMudW53cmFwKCkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAuY29sbGVjdDo6PFN0cmluZz4oKTsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgY3Jvc3Nfd29yZCA9PSAiU01NUyIgfHwgY3Jvc3Nfd29yZCA9PSAiTVNTTSIgewogICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV0IGNvdW50cyA9IGNyb3NzX3dvcmQuY2hhcnMoKS5jb3VudHMoKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIGNvdW50cy5nZXQoJidTJykgPT0gU29tZSgmMikgJiYgY291bnRzLmdldCgmJ00nKSA9PSBTb21lKCYyKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY291bnQgKz0gMTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICBjb3VudAogICAgfQp9CgpmbiBjb252ZXJ0X2luZGljZXNfdG9fdmVjKAogICAgaW5kaWNlczogaW1wbCBJdGVyYXRvcjxJdGVtID0gKGkzMiwgaTMyKT4sCiAgICBsZXR0ZXJzOiAmRnhIYXNoTWFwPChSb3dJbmRleCwgQ29sSW5kZXgpLCBjaGFyPiwKKSAtPiBWZWM8T3B0aW9uPCZjaGFyPj4gewogICAgaW5kaWNlcwogICAgICAgIC5tYXAofChyb3csIGNvbCl8IGxldHRlcnMuZ2V0KCYocm93IGFzIGlzaXplLCBjb2wgYXMgaXNpemUpKSkKICAgICAgICAuY29sbGVjdDo6PFZlYzxPcHRpb248JmNoYXI+Pj4oKQp9CgpmbiBjaGVja19sZXR0ZXJzKGxldHRlcnM6IFZlYzxPcHRpb248JmNoYXI+Piwgd29yZDogJnN0cikgLT4gYm9vbCB7CiAgICBsZXR0ZXJzLml0ZXIoKS5hbGwoT3B0aW9uOjppc19zb21lKSAmJiBsZXR0ZXJzLmludG9faXRlcigpLmZsYXR0ZW4oKS5jb2xsZWN0Ojo8U3RyaW5nPigpID09IHdvcmQKfQoKI1tkZXJpdmUoRGVidWcsIENsb25lLCBDb3B5KV0KZW51bSBEaXJlY3Rpb24gewogICAgVXAsCiAgICBVcExlZnQsCiAgICBVcFJpZ2h0LAogICAgRG93biwKICAgIERvd25MZWZ0LAogICAgRG93blJpZ2h0LAogICAgTGVmdCwKICAgIFJpZ2h0LAp9CgppbXBsIERpcmVjdGlvbiB7CiAgICBmbiBnZXRfaW5kaWNlcygmc2VsZiwgcm93OiBSb3dJbmRleCwgY29sOiBDb2xJbmRleCwgd29yZF9sZW46IHVzaXplKSAtPiBWZWM8KGlzaXplLCBpc2l6ZSk+IHsKICAgICAgICBsZXQgd29yZF9sZW4gPSB3b3JkX2xlbiBhcyBpc2l6ZTsKICAgICAgICBtYXRjaCBzZWxmIHsKICAgICAgICAgICAgRGlyZWN0aW9uOjpVcCA9PiAoMC4ud29yZF9sZW4pLm1hcCh8aXwgKHJvdyAtIGksIGNvbCkpLmNvbGxlY3QoKSwKICAgICAgICAgICAgRGlyZWN0aW9uOjpVcExlZnQgPT4gKDAuLndvcmRfbGVuKS5tYXAofGl8IChyb3cgLSBpLCBjb2wgLSBpKSkuY29sbGVjdCgpLAogICAgICAgICAgICBEaXJlY3Rpb246OlVwUmlnaHQgPT4gKDAuLndvcmRfbGVuKS5tYXAofGl8IChyb3cgLSBpLCBjb2wgKyBpKSkuY29sbGVjdCgpLAogICAgICAgICAgICBEaXJlY3Rpb246OkRvd24gPT4gKDAuLndvcmRfbGVuKS5tYXAofGl8IChyb3cgKyBpLCBjb2wpKS5jb2xsZWN0KCksCiAgICAgICAgICAgIERpcmVjdGlvbjo6RG93bkxlZnQgPT4gKDAuLndvcmRfbGVuKS5tYXAofGl8IChyb3cgKyBpLCBjb2wgLSBpKSkuY29sbGVjdCgpLAogICAgICAgICAgICBEaXJlY3Rpb246OkRvd25SaWdodCA9PiAoMC4ud29yZF9sZW4pLm1hcCh8aXwgKHJvdyArIGksIGNvbCArIGkpKS5jb2xsZWN0KCksCiAgICAgICAgICAgIERpcmVjdGlvbjo6TGVmdCA9PiAoMC4ud29yZF9sZW4pLm1hcCh8aXwgKHJvdywgY29sIC0gaSkpLmNvbGxlY3QoKSwKICAgICAgICAgICAgRGlyZWN0aW9uOjpSaWdodCA9PiAoMC4ud29yZF9sZW4pLm1hcCh8aXwgKHJvdywgY29sICsgaSkpLmNvbGxlY3QoKSwKICAgICAgICB9CiAgICB9Cn0K