-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCrossSums.ahk
495 lines (370 loc) · 10 KB
/
CrossSums.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
/*
Cross Sums script!
This script simple generates a plain text list of all the groups of digits (1-9, no repeats) that add up to a given sum
Zero is not allowed as a digit. No blank spaces allowed (a 2-digit number must be full 2 digits).
Reference: https://teachinglondoncomputing.org/kriss-kross-puzzles/
Dell Penny puzzle books: https://www.pennydellpuzzles.com/?s=cross+sums
File export option prints a nice cheat sheet similar to the one PennyPress publishes on their website
*/
#SingleInstance force ; only one instance of script can run
SetWorkingDir, %A_ScriptDir% ; My Documents
debug := False ; debug switch to experiment, disables GUI
; libraries
#Include, %A_MyDocuments%\AutoHotkey\Lib\GuiButtonIcon\GuiButtonIcon.ahk
#Include, %A_MyDocuments%\AutoHotkey\Lib\AddTooltip\AddTooltip.ahk
#Include, %A_MyDocuments%\AutoHotkey\Lib\ScrollBox\ScrollBox.ahk
ScrollBoxSettings := "f{s9 cBlack, Arial} h400 w400 x400 p w d c b1"
;===============
; experimentation
;===============
If (debug)
{
numbers := GetCollection(2)
DisplayCollection(numbers, 2, True) ; include unused digits
numbers := GetCollection(3)
DisplayCollection(numbers, 3, True)
numbers := GetCollection(4)
DisplayCollection(numbers, 4)
numbers := GetCollection(5)
DisplayCollection(numbers, 5)
numbers := GetCollection(6)
DisplayCollection(numbers, 6)
numbers := GetCollection(7)
DisplayCollection(numbers, 7)
Pause
}
;===============
; GUI creation
;===============
;----------
; digit quantity drop down menu
Gui, Font, s14 norm bold, Arial
Gui, Add, Text, x50 y25 h20, Number of Digits
Gui, Font, s16 norm cBlack
Gui, Add, DropDownList, vDigitsDropdown x50 y65 w50 hwndPresetID
; make a drop down digit selector
Loop, 9
If (A_Index > 1)
optionString .= A_Index . "|"
optionString .= "All"
GuiControl, , DigitsDropdown, %optionString%
;----------
; unused digits checkbox
Gui, Font, s12 norm bold, Arial
Gui, Add, Checkbox, x50 y120 vUnusedCheckbox, Show unused digits?
;----------
; file export checkbox
Gui, Font, s12 norm bold, Arial
Gui, Add, Checkbox, x50 y160 vExportCheckbox, Save to file?
;----------
; Start button
Gui, Font, s14 norm bold, Arial
Gui, Add, Button, x100 y200 w100 h45 hwndStartButtonID vStartButton gStartButton, Start
GuiButtonIcon(StartButtonID, "shell32.dll", 300, "s32 a1 r2")
AddTooltip(StartButtonID, "Create the listing of sums and print to a file in your folder")
;----------
; start GUI
Gui, Show, w300 h275, Cross Sums Helper!
Return
GuiClose:
Quit:
ExitApp
;--------------
; Start!
StartButton:
; get all GUI variable values
Gui, Submit, NoHide
; test GUI variable collection
OutputDebug, % DigitsDropdown
OutputDebug, % UnusedCheckbox
OutputDebug, % ExportCheckbox
OutputDebug, % StartButton
If (DigitsDropdown = "")
{
MsgBox 0x30, Error, Select a number of digits first!
}
Else
{
If (ExportCheckbox)
{
FileSelectFile, OutputFile, S, %A_MyDocuments%\Cross Sums options.csv, Cross Sums Helper, *.csv
; trap for cancel
If (ErrorLevel = 1)
Return
Else IfExist, %OutputFile%
FileDelete, %OutputFile%
}
; disable other controls until done
GuiControl, Disable, ExportCheckbox
GuiControl, Disable, UnusedCheckbox
GuiControl, Disable, DigitsDropdown
GuiControl, Disable, StartButton
; specific number of digits?
If DigitsDropdown is digit
{
numbers := GetCollection(DigitsDropdown)
DisplayCollection(numbers, DigitsDropdown, UnusedCheckbox)
If (ExportCheckbox)
PrintCollection(numbers, DigitsDropdown, UnusedCheckbox)
}
; all digits?
Else If DigitsDropdown is alpha
{
; TODO: skip reporting?
Loop, 9
{
If (A_Index > 1)
{
numbers := GetCollection(A_Index)
; DisplayCollection(numbers, A_Index, UnusedCheckbox)
If (ExportCheckbox)
PrintCollection(numbers, A_Index, UnusedCheckbox)
}
}
}
}
; disable other controls until done
GuiControl, Enable, ExportCheckbox
GuiControl, Enable, UnusedCheckbox
GuiControl, Enable, DigitsDropdown
GuiControl, Enable, StartButton
Return
;===============
; Helper Functions
;===============
;--------------
GetCollection(numberDigits)
{
global ExportCheckbox
StartTime := A_TickCount
combinations := []
combination := []
GenerateCombinations(numberDigits, 1, combination, combinations)
If (ExportCheckbox = False)
{
; print out the results
DisplayCombinations(combinations, numberDigits)
}
ElapsedTime := (A_TickCount - StartTime) / 1000
OutputDebug, %ElapsedTime% seconds have elapsed.
; new approach here for sorting and summing
; first get all the sums of each combination
sums := []
For i In combinations
{
; these sums will largely be in order already, by design
sum := SumArray(combinations[i])
AddSumToArray(sums, sum)
}
; make an array of all the unique sums along with their combinations of digits
collection := []
For j In sums
{
; now make the grouping object
collection[j] := {}
; make a collection object
; put the combo into that sum bucket
collection[j].sum := sums[j]
collection[j].combinations := []
; parse through the combinations now
For k, combination In combinations
{
; these sums will largely be in order already, by design
sum := SumArray(combination)
If (sum = collection[j].sum)
{
collection[j].combinations.Push(combination)
}
}
}
Return collection
}
;--------------
GenerateCombinations(numberDigits, currentDigit, combination, combinations)
{
If (numberDigits = 0)
{
; copy the array manually
combinations.Push(CopyArray(combination))
Return
}
Loop, 9
{
if (A_Index >= currentDigit)
{
combination.Push(A_Index)
GenerateCombinations(numberDigits - 1, A_Index + 1, combination, combinations)
combination.Pop()
}
}
}
;--------------
; this function exists because "copying" an array into another array is done by reference in AHK, not true copying
CopyArray(arr)
{
result := []
For index, value in arr
result.Push(value)
Return result
}
;--------------
; sum the combination array
SumArray(arr)
{
For index, digit in arr
sum += digit
Return sum
}
;--------------
; searches for sum in sum array. If we can't find, it's new and is inserted at the end. If we find it, returns index. If sum should exist between two indexes, returns second index
AddSumToArray(arr, newSum)
{
foundIndex := 1
For index, sum in arr
{
If (sum = newSum)
Return ; sum already exists, bail out
Else If (newSum > sum)
foundIndex := index+1
Else If (newSum < sum)
{
If (foundIndex > 1)
Break
Else
foundIndex := 1
}
}
arr.InsertAt(foundIndex, newSum)
Return foundIndex
}
;--------------
; parse through the collection object and prints out the combinations only
DisplayCombinations(combinations, numberDigits)
{
global ScrollBoxSettings
size := combinations.MaxIndex()
ReportString = There are %size% combinations of %numberDigits% digits (no repeats, 1-9, order not important): `n
For index, combination in combinations
{
combinationString := ""
For i, value in combination
If (i < numberDigits)
combinationString .= value . ", "
Else
combinationString .= value
ReportString .= "combination #" . index . ": " . combinationString . "`n"
}
OutputDebug, % ReportString
ScrollBox(ReportString, ScrollBoxSettings, "Digit Combinations")
}
;--------------
; parse through the collection object and print out the options along with their sum
; option to show unused digits, default off
DisplayCollection(collection, numberDigits, showUnused := False)
{
global ScrollBoxSettings
size := collection.MaxIndex()
ReportString = There are %size% sums for %numberDigits% digits (no repeats, 1-9, order not important): `n
Loop, %size%
{
; init unused array of booleans (true @ index = used, false = unused)
usedDigits := []
collectionString := "sum=" . collection[A_Index].sum . ", " . collection[A_Index].combinations.MaxIndex() . " combinations of digits: `n"
For set, combination in collection[A_Index].combinations
{
Loop, %numberDigits%
{
; remove used digits
usedDigits[combination[A_Index]] := True
collectionString .= combination[A_Index]
If (A_Index < numberDigits)
collectionString .= "+"
}
collectionString .= "`n"
}
If (showUnused)
{
collectionString .= "Unused digits: "
firstUnused := False
; assume all used digits
allUsed := True
Loop, 9
{
If (usedDigits[A_Index] <> True)
{
allUsed := False
; comma placement
If (firstUnused = False)
{
collectionString .= A_Index
firstUnused := True
}
Else
collectionString .= ", " . A_Index
}
}
If (allUsed)
collectionString .= "(none)"
collectionString .= " `n"
}
ReportString .= collectionString
}
OutputDebug, % ReportString
ScrollBox(ReportString, ScrollBoxSettings, "Sum Collections")
}
;--------------
; parse through the collection object and print out the options along with their sum
; option to show unused digits, default off
PrintCollection(collection, numberDigits, showUnused := False)
{
global OutputFile
; header
ReportString := "`n" . numberDigits . " DIGITS`n"
size := collection.MaxIndex()
Loop, %size%
{
; init unused array of booleans (true @ index = used, false = unused)
usedDigits := []
collectionString := collection[A_Index].sum . " = `t"
For set, combination in collection[A_Index].combinations
{
Loop, %numberDigits%
{
; remove used digits
usedDigits[combination[A_Index]] := True
collectionString .= combination[A_Index]
If (A_Index < numberDigits)
collectionString .= "+"
}
collectionString .= "`t"
}
If (showUnused)
{
unusedString := "{"
firstUnused := False
; assume all used digits
allUsed := True
Loop, 9
{
If (usedDigits[A_Index] <> True)
{
allUsed := False
; comma placement
If (firstUnused = False)
{
unusedString .= A_Index
firstUnused := True
}
Else
unusedString .= ", " . A_Index
}
}
unusedString .= "}"
If (!allUsed)
collectionString .= unusedString
}
collectionString .= "`n"
ReportString .= collectionString
}
FileAppend, %ReportString%, %OutputFile%
}