Previously, we laid some groundwork for generating word chains. Rather than arbitrarily returning one word, we might as well get all the words that are one letter different from the first word:
prop_next_words_should_be_near() ->
?FORALL({FirstWord, LastWord}, valid_words(),
begin
NextWords = word_chains:next_words(FirstWord),
InvalidWords = lists:filter(fun(W) -> word_chains:get_word_distance(W, FirstWord) =/= 1 end, NextWords),
length(InvalidWords) =:= 0
end).
We can calculate the “word distance” using map/reduce:
get_word_distance(Word1, Word2) ->
Differences = lists:zipwith(fun(X, Y) -> case X =:= Y of true -> 0; false -> 1 end end, Word1, Word2),
lists:foldl(fun(D, Acc) -> Acc + D end, 0, Differences).
For each letter in Word1, we compare it with the same (position) letter in Word2, and assign a 0 if it matches and a 1 if it differs. The sum of these values tells us the difference between the 2 words.
2> word_chains:get_word_distance("cat", "cat").
0
3> word_chains:get_word_distance("cat", "cot").
1
4> word_chains:get_word_distance("cat", "cog").
2
Using this helper function, we can easily find all the possible next words:
next_words(FirstWord) ->
WordList = word_list(),
SameLengthWords = lists:filter(fun(W) -> length(W) =:= length(FirstWord) end, WordList),
WordDistances = lists:map(fun(W) -> {W, get_word_distance(W, FirstWord)} end, SameLengthWords),
lists:map(fun({Word, _}) -> Word end, lists:filter(fun({_, Distance}) -> Distance =:= 1 end, WordDistances)).
Almost there! Next time, we will actually start generating some word chains.