@@ -3,18 +3,6 @@ pub mod display;
33use display:: string_input:: StringInput ;
44use std:: collections:: { HashSet , VecDeque } ;
55
6- #[ derive( Eq , PartialEq , Hash , Clone ) ]
7- pub struct Word {
8- pub word : String ,
9- pub definition : String ,
10- }
11-
12- impl Word {
13- pub fn new ( word : String , definition : String ) -> Word {
14- Word { word, definition }
15- }
16- }
17-
186fn main ( ) {
197 let starting_word = StringInput :: new ( )
208 . message ( "Enter starting word" )
@@ -28,100 +16,100 @@ fn main() {
2816 . max ( starting_word. len ( ) as i32 )
2917 . ask ( ) ;
3018
31- println ! ( "start: {}" , starting_word) ;
32- println ! ( "end: {}" , ending_word) ;
19+ if starting_word == ending_word {
20+ eprintln ! ( "The starting word must be different from the ending word" ) ;
21+ std:: process:: exit ( 1 ) ;
22+ }
23+
24+ println ! ( "{} -> {}" , starting_word, ending_word) ;
3325
3426 let file = include_str ! ( "./words.txt" ) ;
35- let mut word_list: Vec < Word > = Vec :: new ( ) ;
27+ let mut word_list: Vec < String > = Vec :: new ( ) ;
3628
37- let mut start : Option < Word > = None ;
38- let mut end : Option < Word > = None ;
29+ let mut has_start = false ;
30+ let mut has_end = false ;
3931 for line in file. lines ( ) {
40- let split : Vec < & str > = line. split ( " :: " ) . collect ( ) ;
41- if starting_word == split [ 0 ] {
42- start = Some ( Word :: new (
43- split [ 0 ] . trim ( ) . to_string ( ) ,
44- split [ 1 ] . trim ( ) . to_string ( ) ,
45- ) ) ;
32+ if line. len ( ) != starting_word . len ( ) {
33+ continue ;
34+ }
35+
36+ if line == starting_word {
37+ has_start = true ;
4638 }
4739
48- if ending_word == split[ 0 ] {
49- end = Some ( Word :: new (
50- split[ 0 ] . trim ( ) . to_string ( ) ,
51- split[ 1 ] . trim ( ) . to_string ( ) ,
52- ) ) ;
40+ if line == ending_word {
41+ has_end = true ;
5342 }
5443
55- word_list. push ( Word :: new (
56- split[ 0 ] . trim ( ) . to_string ( ) ,
57- split[ 1 ] . trim ( ) . to_string ( ) ,
58- ) ) ;
44+ word_list. push ( line. to_string ( ) ) ;
5945 }
6046
61- if start . is_none ( ) {
47+ if has_start == false {
6248 eprintln ! ( "Failed to find start word in word list" ) ;
6349 std:: process:: exit ( 1 ) ;
6450 }
6551
66- if end . is_none ( ) {
52+ if has_end == false {
6753 eprintln ! ( "Failed to find end word in word list" ) ;
6854 std:: process:: exit ( 1 ) ;
6955 }
7056
71- let ladder = generate_word_ladder ( start . unwrap ( ) , end . unwrap ( ) , word_list) ;
57+ let ladder = generate_word_ladder ( starting_word , ending_word , word_list) ;
7258
7359 if ladder. is_empty ( ) {
7460 println ! ( "Failed to find path between words" ) ;
7561 return ;
7662 }
7763
7864 for word in ladder {
79- println ! ( "{} {} " , word. word , word . definition ) ;
65+ println ! ( "{}" , word) ;
8066 }
8167}
8268
83- fn generate_word_ladder ( start_word : Word , end_word : Word , word_list : Vec < Word > ) -> Vec < Word > {
84- let mut queue: VecDeque < Vec < Word > > = VecDeque :: new ( ) ;
85- let mut visited: HashSet < Word > = HashSet :: new ( ) ;
86-
87- queue. push_back ( vec ! [ start_word. clone( ) ] ) ;
88- visited. insert ( start_word) ;
69+ fn find_successors ( word : & str , word_set : & HashSet < String > ) -> Vec < String > {
70+ let mut successors = Vec :: new ( ) ;
71+ let word_chars: Vec < char > = word. chars ( ) . collect ( ) ;
8972
90- while let Some ( path) = queue. pop_front ( ) {
91- let current_word = path. last ( ) . unwrap ( ) ;
73+ for i in 0 ..word_chars. len ( ) {
74+ for ch in 'a' ..='z' {
75+ if ch != word_chars[ i] {
76+ let mut new_word = word_chars. clone ( ) ;
77+ new_word[ i] = ch;
78+ let candidate: String = new_word. into_iter ( ) . collect ( ) ;
9279
93- if current_word. word == end_word. word {
94- return path;
95- }
96-
97- for word in & word_list {
98- if differs_by_one_letter ( current_word, & word) && !visited. contains ( & word) {
99- visited. insert ( word. clone ( ) ) ;
100- let mut new_path = path. clone ( ) ;
101- new_path. push ( word. clone ( ) ) ;
102- queue. push_back ( new_path) ;
80+ if word_set. contains ( & candidate) {
81+ successors. push ( candidate) ;
82+ }
10383 }
10484 }
10585 }
106-
107- Vec :: new ( )
86+ successors
10887}
10988
110- fn differs_by_one_letter ( word1 : & Word , word2 : & Word ) -> bool {
111- if word1. word . len ( ) != word2. word . len ( ) {
112- return false ;
113- }
89+ fn generate_word_ladder ( start : String , end : String , word_list : Vec < String > ) -> Vec < String > {
90+ let word_set: HashSet < String > = word_list. iter ( ) . cloned ( ) . collect ( ) ;
91+ let mut queue = VecDeque :: new ( ) ;
92+ let mut visited = HashSet :: new ( ) ;
93+
94+ queue. push_back ( vec ! [ start. clone( ) ] ) ;
95+ visited. insert ( start. clone ( ) ) ;
11496
115- let mut diff_count = 0 ;
97+ while let Some ( ladder) = queue. pop_front ( ) {
98+ let last_word = ladder. last ( ) . unwrap ( ) ;
99+
100+ if last_word == & end {
101+ return ladder;
102+ }
116103
117- for ( c1, c2) in word1. word . chars ( ) . zip ( word2. word . chars ( ) ) {
118- if c1 != c2 {
119- diff_count += 1 ;
120- if diff_count > 1 {
121- return false ;
104+ for successor in find_successors ( last_word, & word_set) {
105+ if !visited. contains ( & successor) {
106+ let mut new_ladder = ladder. clone ( ) ;
107+ new_ladder. push ( successor. clone ( ) ) ;
108+ queue. push_back ( new_ladder) ;
109+ visited. insert ( successor) ;
122110 }
123111 }
124112 }
125113
126- diff_count == 1
114+ Vec :: new ( ) // Return an empty vector if no ladder is found
127115}
0 commit comments