Permalink
Browse files

Generate word chains

  • Loading branch information...
grahamrhay committed Jan 1, 2019
1 parent 8101d8c commit 5785a310123ada4c8d53d098169375e6dd07a955
Showing with 58 additions and 22 deletions.
  1. +35 −8 src/word_chains.erl
  2. +23 −14 test/prop_word_chains.erl
@@ -1,16 +1,43 @@
-module(word_chains).

-export([word_list/0, next_words/1, get_word_distance/2]).
-export([word_list/1, get_word_distance/2, all_chains/4]).

word_list() ->
word_list(N) ->
{ok, Data} = file:read_file("words.txt"),
lists:map(fun(W) -> binary_to_list(W) end, binary:split(Data, [<<"\n">>], [global])).
WordList = lists:map(fun(W) -> binary_to_list(W) end, binary:split(Data, [<<"\n">>], [global])),
lists:filter(fun(W) -> length(W) =:= N end, WordList).

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)).
all_chains(FirstWord, LastWord, Words, MaxLength) ->
lists:sort(fun(A, B) -> length(A) =< length(B) end, all_chains(FirstWord, LastWord, Words, MaxLength, [[FirstWord]])).

all_chains(FirstWord, LastWord, Words, MaxLength, Chains) ->
lists:append(lists:map(fun(Chain) ->
[CurrentWord | _Rest] = Chain,
case CurrentWord =:= LastWord of
true -> [Chain];
false ->
NextWords = next_words(CurrentWord, Words),
NewChains = compact(lists:map(fun(NewWord) ->
case lists:member(NewWord, Chain) of
false ->
NewChain = [NewWord | Chain],
case length(NewChain) > MaxLength of
true -> [];
false -> NewChain
end;
true -> []
end
end, NextWords)),
all_chains(FirstWord, LastWord, Words, MaxLength, NewChains)
end
end, Chains)).

compact(List) ->
lists:filter(fun(E) -> length(E) > 0 end, List).

next_words(Word, Words) ->
WordDistances = lists:map(fun(W) -> {W, get_word_distance(W, Word)} end, Words),
lists:map(fun({W, _}) -> W end, lists:filter(fun({_, Distance}) -> Distance =:= 1 end, WordDistances)).

get_word_distance(Word1, Word2) ->
Differences = lists:zipwith(fun(X, Y) -> case X =:= Y of true -> 0; false -> 1 end end, Word1, Word2),
@@ -3,41 +3,38 @@
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").

prop_next_words_should_be_near() ->
prop_all_chains_should_include_last_word() ->
?FORALL({FirstWord, LastWord}, valid_words(),
begin
io:format("First word: ~p, Last word: ~p", [FirstWord, LastWord]),
NextWords = word_chains:next_words(FirstWord),
io:format("Next words: ~p", [NextWords]),
InvalidWords = lists:filter(fun(W) -> word_chains:get_word_distance(W, FirstWord) =/= 1 end, NextWords),
io:format("Invalid words: ~p", [InvalidWords]),
length(InvalidWords) =:= 0
Words = word_chains:word_list(length(FirstWord)),
Chains = word_chains:all_chains(FirstWord, LastWord, Words, length(FirstWord)),
InvalidChains = lists:filter(fun([W|_]) -> W =/= LastWord end, Chains),
length(InvalidChains) =:= 0
end).

valid_words() ->
?SUCHTHAT({FirstWord, LastWord},
?LET(N, choose(2, 10),
begin
WordList = word_chains:word_list(),
SameLengthWords = lists:filter(fun(W) -> length(W) =:= N end, WordList),
{random_word(SameLengthWords), random_word(SameLengthWords)}
WordList = word_chains:word_list(N),
{random_word(WordList), random_word(WordList)}
end),
FirstWord =/= LastWord).

random_word(Words) ->
lists:nth(rand:uniform(length(Words)), Words).

word_list_test() ->
WordList = word_chains:word_list(),
?assertEqual(length(WordList), 274926).
WordList = word_chains:word_list(3),
?assertEqual(length(WordList), 1311).

word_list_first_word_test() ->
WordList = word_chains:word_list(),
WordList = word_chains:word_list(2),
FirstWord = lists:nth(1, WordList),
?assertEqual(FirstWord, "aa").

word_list_last_word_test() ->
WordList = word_chains:word_list(),
WordList = word_chains:word_list(4),
LastWord = lists:last(WordList),
?assertEqual(LastWord, "zzzs").

@@ -52,3 +49,15 @@ get_word_distance_near_word_test() ->
get_word_distance_far_word_test() ->
WordDistance = word_chains:get_word_distance("cat", "dog"),
?assertEqual(3, WordDistance).

all_chains_cat_cat_test() ->
Words = word_chains:word_list(3),
Chains = word_chains:all_chains("cat", "cat", Words, 10),
?assertEqual([["cat"]], Chains).

all_chains_cat_cot_test_() ->
{timeout, 5, fun() ->
Words = word_chains:word_list(3),
Chains = word_chains:all_chains("cat", "cot", Words, 3),
?assertEqual([["cot","cat"],["cot","cit","cat"],["cot","cut","cat"]], Chains)
end}.

0 comments on commit 5785a31

Please sign in to comment.