aboutsummaryrefslogtreecommitdiff
path: root/challenge-007/uzluisf/perl6/ModuleCH02.pm6
blob: 584641f98b79509ecd297478036feca5ad396d9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
unit module ModuleCH02;

=begin comment
Challenge 007#2: Word Ladder

A word ladder is a sequence of words [w0, w1, …, wn] such that each word wi
in the sequence is obtained by changing a single character in the word wi-1.
All words in the ladder must be valid English words.

Given two input words and a file that contains an ordered word list, implement
a routine (e.g., find_shortest_ladder(word1, word2, wordlist)) that finds
the shortest ladder between the two input words.
=end comment

sub find-shortest-ladder( 
    Str:D $start-word, 
    Str:D $end-word where *.chars == $start-word.chars,
    Str:D $file     where *.IO.e && *.IO.f,
) is export {
    my @lexicon = $file.IO.lines».lc;
    
    # check the input words have the same length as the first word
    # in the wordlist.
    return [] if @lexicon.first.chars != any ($start-word, $end-word)».chars;
   
    # check input words are in the wordlist.
    return [] if ($start-word | $end-word) ∉ @lexicon;

    my @partial-ladders;
    @partial-ladders.push: [$start-word];

    while @partial-ladders {
        my @first-ladder = |@partial-ladders.shift;            

        # if $end-word is found in the current ladder...
        return @first-ladder if $end-word@first-ladder;

        for ortographic-neighbors-of(@first-ladder.tail, @lexicon) -> $word {
            # if word hasn't been used in a ladder, add it to a copy 
            # of current ladder and add this new ladder to partial ladders.
            if not has-been-used($word, @partial-ladders) {
                my @copy-ladder = @first-ladder;
                @copy-ladder.push: $word;
                @partial-ladders.push: @copy-ladder;
            }
        }
    }

    return [];
}

#| Return all the words that differ from $word by a single character.
sub ortographic-neighbors-of( Str:D $word, @lexicon --> Array:D ) {
    my @result;
    for @lexicon -> $lword {
        my $different-letters = 0;
        for 0 ..^ $word.chars -> $idx {
            $different-letters++ if $word.comb[$idx] ne $lword.comb[$idx];
        }
        @result.unshift($lword) if $different-letters == 1;
    }
    return @result;
}

#| Check if $word has been used in a ladder already.
sub has-been-used( Str:D $word, @partial-ladders ) {
    for @partial-ladders -> $ladder {
        return True if $word$ladder;
    }
    return False;
}


=begin comment
sub binary-search( Str:D $word, @list --> Bool ) {
    my Int $lo-idx = 0;
    my Int $hi-idx = @list.end;

    while $lo-idx ≤ $hi-idx {
        my Int $mid-idx = $lo-idx + ($hi-idx - $lo-idx) div 2;
        if    $word lt @list[$mid-idx] { $hi-idx = $mid-idx - 1 }
        elsif $word gt @list[$mid-idx] { $lo-idx = $mid-idx + 1 }
        else                           { return True        }
    }

    return False;
}
=end comment