Code: Select all
;-------------------------------------------------------------------------------
; Anagrams.ahk
; by wolf_II
;-------------------------------------------------------------------------------
; simple Anagram searcher
; written with massive help by Helgef from AHK forum
; https://autohotkey.com/boards/viewtopic.php?p=158284#p158284
;-------------------------------------------------------------------------------
;-------------------------------------------------------------------------------
AutoExecute: ; auto-execute section of the script
;-------------------------------------------------------------------------------
#NoEnv ; don't check empty variables
#SingleInstance, Force ; kill old instance
;---------------------------------------------------------------------------
SetBatchLines, -1 ; run script at maximum speed
SetWorkingDir, %A_ScriptDir% ; consistent directory
SplitPath, A_ScriptName,,,, AppName ; get AppName from file name
;---------------------------------------------------------------------------
Version := "1.23"
;===========================================================================
global Max_Input_Length := 26, br
Loop, % Max_Input_Length - 1
SkipList .= A_Index "|"
SkipList .= "show all"
;---------------------------
; display GUI while loading
;---------------------------
Try Menu, Tray, Icon, %AppName%.ico
Menu, Tray, Icon ; show
Gui, New, , %AppName% v%Version% ; -MinimizeBox
Gui, Font, s11, Consolas ; xm=13, ym=8
Gui, Add, Text, w310, Search anagrams for this:
Gui, Add, Edit, wp vInput Disabled Center
Gui, Add, Text, xm y+12, Exclude words of length:
Gui, Add, ComboBox, x+m yp-4 w105 vSkip AltSubmit gShowAnagrams, %SkipList%
Gui, Add, ListBox, xm r15 w310 vLBox
Gui, Add, Progress, xp+1 yp+1 wp-2 hp-2 vLoading Vertical
Gui, Font ; default
Gui, Add, StatusBar
SB_SetParts(70, 70, 100)
SB_SetText("Length:", 1)
SB_SetText("Count:", 2)
Gui, Show,, Anagrams Wolf_II
;--------------------------
; make DICT from text file
;--------------------------
FileRead, Dictionary, WordList3.txt
;FileRead, Dictionary, c:\users\RRR\Besede SPA.txt
;FileRead, Dictionary, C:\Users\RRR\My Ahk-s\TypingAid\TypingAid_Eng\Wordlists\WordList English Unix.txt
StrReplace(Dictionary, "`n",, WordCount), WordCount++
WA := A_IsUnicode ? 2 : 1
DICT := []
if !FileExist("WordList_W.txt") {
Loop, Parse, Dictionary, `n, `r
{
If (Percent := Round(100 * A_Index / WordCount)) > Prev_Percent {
GuiControl,, Loading, %Percent%
GuiControl,, Input, %Percent% `%
}
Prev_Percent := Percent
WordLength := StrLen(A_LoopField)
If Not IsObject(DICT[WordLength])
DICT[WordLength] := []
; allow Bob, English and I in Words.txt
StringLower, Keyword, A_LoopField
Keyword := make_Keyword(Keyword)
If Not IsObject(DICT[WordLength, Keyword])
DICT[WordLength, Keyword] := []
;-----------------------------------------------------------------------
; Each word in Dictionary gets pushed into the respective DICT[L, K].
; Push() function returns the index of the most recently pushed value.
; We then use this index to correct the size downwards,
; i.e. save space or adjust the capacity of a field in DICT[L, K].
;-----------------------------------------------------------------------
; E.g. let's say we pushed the 7th word to DICT[L, K] not knowing the 7.
; Push() gives us back the index = 7, and we can adjust the 7th field
; of DICT[L, K] to ByteSize = # characters (respecting ANSI/Unicode)
;-----------------------------------------------------------------------
; We do this exercise on each word in Dictionary,
; impact on performance is negligible, but:
; impact on memory used is noticeable (129 MB vs. 96 MB) on 370 k words
; impact on memory used is noticeable ( 40 MB vs. 30 MB) on 110 k words
;-----------------------------------------------------------------------
index := DICT[WordLength, Keyword].Push(A_LoopField)
DICT[WordLength, Keyword].SetCapacity(index, (WordLength + 1) * WA)
}
; save more space
Dictionary := ""
For each, WordLength_Obj in DICT
WordLength_Obj.SetCapacity(0)
FileAppend, % txtArr(dict), WordList_W.txt
} else {
Dictionary:= ""
FileRead, DB, WordList_W.txt
StrReplace(DB, "`n",, DBCount), DBCount++
Loop Parse, DB, `n, `r
{
If (Percent := Round(100 * A_Index / DBCount)) > Prev_Percent {
GuiControl,, Loading, %Percent%
GuiControl,, Input, %Percent% `%
} Prev_Percent := Percent
RegExMatch(A_LoopField, "^(\d+):$", len)
if len1 {
dict[l:=len1]:= ""
Continue
}
RegExMatch(A_LoopField, "^ ([^ ]{1}.*):$", abc)
if abc1 {
dict[l]? "": dict[l]:=[], dict[l][aa:=abc1]:= ""
Continue
}
RegExMatch(A_LoopField, "^[ ]{4}(\d+): (.+)$", ww)
ww2? (dict[l][aa]? "": dict[l][aa]:=[], dict[l][aa].Push(ww2)): ""
}
}
txtArr(r, i:=" ", d:=0, b:="") {
For k, v in r
c.= IsObject(v)? b k ":`n" txtArr(v, i, d+1, b i): b k ": " v "`n", d>0? "": t:=c
Return d>0? c: t
}
;-----------------
; loading is done
;-----------------
SB_SetText("Words: " prettify_Number(WordCount), 3)
GuiControl, Hide, Loading
GuiControl, -Center -Disabled +gShowAnagrams, Input
GuiControl, Choose, Skip, 5
GuiControl, Focus, Input
GuiControl,, Input
Return ; end of auto-execute section
GuiClose:
ExitApp
;-------------------------------------------------------------------------------
ShowAnagrams: ; live display all anagrams of Input found in Dictionary
;-------------------------------------------------------------------------------
GuiControlGet, Input
GuiControlGet, Skip
GuiControlGet, LBox
InputLength := StrLen(Input), br:= ""
SB_SetText("Length: " InputLength, 1)
If (InputLength > 12) { ; reduce flashing
GuiControl, +Disabled, Input
GuiControl, +Disabled, Skip
GuiControl, +Disabled, LBox
}
If (InputLength > Max_Input_Length) {
GuiControl,, LBox, |Input too long
GuiControl, -Disabled, Input
GuiControl, -Disabled, Skip
GuiControl, -Disabled, LBox
GuiControl, Focus, Input
Return
}
;Clipboard:= TxtArr(Combinations(Input, Skip)) ; RRR
WordList := "", Count := 0
Start := QPC()
For each, Keyword in Combinations(Input, Skip)
For each, Anagram in DICT[StrLen(Keyword) // 2 + 1, Keyword]
if Br
Break, 2
else WordList .= WordList ? "|" Anagram : Anagram, Count++
Sort, WordList, D| F LengthSort
TimeTaken := QPC() - Start, Br:= "" ; time in s
GuiControl,, TimeTaken, %TimeTaken%
GuiControl, Hide, LBox
GuiControl,, LBox, % "|" (WordList ? WordList : "no anagrams for " Input)
GuiControl, Show, LBox
SB_SetText("Count: " Count, 2)
SB_SetText("Time: " Round(TimeTaken, 3) " s", 4)
GuiControl, -Disabled, Input
GuiControl, -Disabled, Skip
GuiControl, -Disabled, LBox
GuiControl, Focus, Input
Return
;-------------------------------------------------------------------------------
LengthSort(a, b) { ; word length specific custom sort
;-------------------------------------------------------------------------------
; Compacting this function to a one-liner, similar to the examples
; in the help file, runs about 20% faster than before.
;---------------------------------------------------------------------------
; primary criterion is string length, otherwise sort alphabetically
Return, StrLen(a) < StrLen(b) ? 1 : StrLen(a) > StrLen(b) ? -1
: a > b ? 1 : a < b ? -1 : 0
}
;-------------------------------------------------------------------------------
Combinations(String, Skip) { ; return an array with the sub-keywords
;-------------------------------------------------------------------------------
; sub-keywords are the keywords of all the substrings of String
;---------------------------------------------------------------------------
Keyword := make_Keyword(String) ; the only call here to make_Keyword()
Store := []
Store[0] := []
Store[0, Keyword] := True
Result := [Keyword]
N := StrLen(String) - 1 - (Skip = Max_Input_Length ? 0 : Skip)
Loop, % N {
Store[n := A_Index] := [] ; array of n* shortened strings
For ShortKey in Store[n - 1] {
Loop, % StrLen(ShortKey) // 2 + 1 {
if Br
Break, 3
; split the ShortKey and get next shorter keyword
Split1 := SubStr(ShortKey, 1, 2 * A_Index - 2) ; keep delim
Split3 := SubStr(ShortKey, 2 * A_Index + 1) ; drop delim
NextShorter := RTrim(Split1 Split3, ",") ; trim delim
If Not Store[n].HasKey(NextShorter) {
Store[n, NextShorter] := True
Result.Push(NextShorter)
}
}
}
}
Return, Result
}
;-------------------------------------------------------------------------------
make_Keyword(String) { ; return a sorted, comma separated string of chars
;-------------------------------------------------------------------------------
Keyword := LTrim(RegExReplace(String, "(.)", ",$1"), ",")
Sort, Keyword, D,
Return, Keyword
;Return, StrReplace(Keyword, ",")
}
;-------------------------------------------------------------------------------
prettify_Number(n, s := ",") { ; insert thousand separators into a number
;-------------------------------------------------------------------------------
; credit for the RegEx goes to AHK forum user infogulch
; https://autohotkey.com/board/topic/50019-add-thousands-separator/
;---------------------------------------------------------------------------
IfLess, n, 0, Return, "-" prettify_Number(SubStr(n, 2), s)
Return, RegExReplace(n, "\G\d+?(?=(\d{3})+(?:\D|$))", "$0" s)
}
;-------------------------------------------------------------------------------
QPC() { ; microseconds precision
;-------------------------------------------------------------------------------
static Freq, init := DllCall("QueryPerformanceFrequency", "Int64P", Freq)
DllCall("QueryPerformanceCounter", "Int64P", Count)
Return, Count / Freq
}
#IfWinActive Anagrams Wolf_II
Esc:: br:= 1
/*--------- end of file --------------------------------------------------------
[Edit]: Made subsequent loads a bit faster. I hope I didn't introduce bugs!