I recently started playing a game against my wife called anagrams. Since she is rather good at it, my first attempt was to use command line and the MacOS dictionary:
0 Johns-MBP$ grep '^[pcilas][pcilas][pcilas][pcilas][pcilas][pcilas][pcilas]$' /usr/share/dict/words | grep -v ll aplasia asialia classic ...
This wasn't very fast since it took too long to type the pattern, and also to guess what double-letter options should be avoided.
Next, created some Smalltalk code for possible matches:
| pattern matches stream | pattern := 'ufmets' asBag. matches := OrderedCollection new. stream := '/usr/share/dict/words' asFileReference readStream. [ stream atEnd ] whileFalse: [ | word boolean | word := stream nextLine. ( word size <= pattern size and: [ (word allSatisfy: [ :c | pattern includes: c ]) and: [ boolean := true. word asBag doWithOccurrences: [ :letter :number | (pattern occurrencesOf: letter) < number ifTrue: [ boolean := false ] ]. boolean ] ] ) ifTrue: [ matches add: word ] ]. stream close. matches sorted: [ :a :b | a size > b size ]
This takes care of the double-letter problem, and won my first game. Unfortunately it requires a change in the pattern (the text ufmets
above).
To make this faster, the next step is to allow the pattern as a command-line argument, this booklet explains the idea. Creating the class:
CommandLineHandler subclass: #WordCommandLineHandler instanceVariableNames: '' classVariableNames: '' package: 'Custom-CommandLine'
On the class side, created:
commandName ^ 'word'
Instance side has:
activate self activateHelp ifTrue: [ ^ self ]. self arguments size = 1 ifFalse: [ self printHelp. ^ self exitFailure: 'Missing Arguments' ]. self printAnagrams; quit
and:
printAnagrams | pattern matches stream | pattern := self arguments first asBag. matches := OrderedCollection new. stream := '/usr/share/dict/words' asFileReference readStream. [ stream atEnd ] whileFalse: [ | word boolean | word := stream nextLine. (word size <= pattern size and: [ (word allSatisfy: [ :c | pattern includes: c ]) and: [ boolean := true. word asBag doWithOccurrences: [ :letter :number | (pattern occurrencesOf: letter) < number ifTrue: [ boolean := false ] ]. boolean ] ]) ifTrue: [ matches add: word ] ]. stream close. (matches sorted: [ :a :b | a size > b size ]) do: [ :str | str size > 2 ifTrue: [ self stdout print: str; lf ] ]
After staving the image, this can be ran as:
0 Johns-MBP$ ./pharo ./anagram.image word pcilas 'spical' 'spica' 'scalp' 'salic' ...
As I was writing this, 3Blue1Brown released a wordle video that is related.