diff --git a/bin/anagram-finder.php b/bin/anagram-finder.php index 8bdcf8e..ccdfbae 100644 --- a/bin/anagram-finder.php +++ b/bin/anagram-finder.php @@ -2,43 +2,25 @@ require __DIR__ . '/../autoload.php'; -use Illuminate\Support\Collection; -use Illuminate\Support\LazyCollection; +use Myrmidex\AnagramFinder\ArgHandler; +use Myrmidex\AnagramFinder\Matcher; +use Myrmidex\AnagramFinder\Output; const DICTIONARY = __DIR__ . '/../assets/words.txt'; -echo "ANAGRAM FINDER\n"; +Output::line("ANAGRAM FINDER"); -$words = LazyCollection::make(function () { - $fh = fopen(DICTIONARY, 'rb'); - try { - while (($line = fgets($fh)) !== false) { - yield rtrim($line, "\r\n"); - } - } finally { - fclose($fh); - } -}); +$args = ArgHandler::make($argv); +$anagram = $args->get(1) ?: readline("Enter the anagram: "); +$minLength = $args->get(2) ?: 3; +$top10 = $args->hasOption('top'); -function output($message) -{ - $message = $message . PHP_EOL; - print($message); - flush(); -// ob_flush(); +Output::line(); +Output::line("Results for your anagram: " . $anagram); + +$matches = Matcher::findWords($anagram, $minLength); +if ($top10) { + $matches = $matches->slice(0, 10); } +$matches->each(fn (string $word) => Output::line($word . " (" . strlen($word) . ")")); - -$anagramBase = readline("Enter the anagram: "); -echo "\n"; -echo "Results for your anagram: " . $anagramBase . "\n"; - -$anagramChars = new Collection(explode($anagramBase, "")); -echo $words - ->filter(fn (string $word) => ctype_alpha($word)) - ->filter(fn (string $word) => strtolower($word) === $word) - ->map(fn (string $word) => new Collection(explode($word, ""))) - ->filter(fn (Collection $wordChars) => $wordChars->count() === $anagramChars->intersect($wordChars)->count()) -->count(); -// ->map(fn (Collection $wordChars) => $wordChars->join("")) -// ->each(fn (string $word) => output($word)); diff --git a/src/ArgHandler.php b/src/ArgHandler.php new file mode 100644 index 0000000..e0402a2 --- /dev/null +++ b/src/ArgHandler.php @@ -0,0 +1,33 @@ +args = new Collection($argv); + } + + public static function make(array $argv): self + { + return new self($argv); + } + + public function get(int $index) + { + return $this->args + ->reject(fn ($word) => substr($word, 0, 2) === '--') + ->get($index); + } + + public function hasOption(string $option) { + return $this->args + ->filter(fn ($word) => $word === '--' . $option) + ->isNotEmpty(); + } +} diff --git a/src/Matcher.php b/src/Matcher.php index 407a6f3..ac63b62 100644 --- a/src/Matcher.php +++ b/src/Matcher.php @@ -3,15 +3,49 @@ namespace Myrmidex\AnagramFinder; use Illuminate\Support\Collection; +use Illuminate\Support\LazyCollection; class Matcher { + public static function findWords(string $anagram, int $minLength = 3): Collection + { + $matches = self::getWordList() + ->filter(fn (string $word) => strlen($word) >= $minLength) + ->filter(fn (string $word) => ctype_alpha($word)) + ->filter(fn (string $word) => $word === strtolower($word)) + ->filter(fn (string $word) => self::check($anagram, $word)) + ->toArray(); + + return new Collection($matches) + ->sort(fn ($a, $b) => strlen($b) - strlen($a)); + } + public static function check(string $anagram, string $word): bool { - $available = (new Collection(str_split($anagram)))->countBy(); + $pool = array_count_values(str_split($anagram)); - return (new Collection(str_split($word))) - ->countBy() - ->every(fn (int $needed, string $letter) => $available->get($letter, 0) >= $needed); + foreach (str_split($word) as $char) { + if (($pool[$char] ?? 0) === 0) { + return false; + } + + $pool[$char]--; + } + + return true; + } + + private static function getWordList(): LazyCollection + { + return LazyCollection::make(function () { + $fh = fopen(DICTIONARY, 'rb'); + try { + while (($line = fgets($fh)) !== false) { + yield rtrim($line, "\r\n"); + } + } finally { + fclose($fh); + } + }); } } diff --git a/src/Output.php b/src/Output.php new file mode 100644 index 0000000..376475e --- /dev/null +++ b/src/Output.php @@ -0,0 +1,12 @@ +assertEquals($isExpectedMatch, Matcher::check($anagram, $word)); + } + + public static function checkProvider(): array + { + return [ + 'valid1' => [ "eamstoxil", "steam", true ], + 'valid2' => [ "wordneoxs", "downers", true ], + 'valid3' => [ "ioevpmoef", "movie", true ], + 'invalid_basic' => [ "eamstoxil", "smear", false], + 'invalid_double_e' => [ "eamstexil", "smear", false], + ]; } }