#Requires AutoHotkey v1.1.33+ ; https://www.autohotkey.com/boards/viewtopic.php?t=15291 ; CONTENTS: ; ; Auto-execute section: ; Basic variables and arrays ; Main GUI = Sudoku board and menu ; ; Main GUI events ; Subroutines and hotkeys for the main GUI's menu, in the menu's order ; Other hotkeys for the main GUI ; Mouse buttons ; ; Main solving subroutines: ; fill ; GetPossibleNumbers/Cells ; ContinueEliminating ; Other subroutines A-Z #NoEnv #singleinstance off SendMode input ; for click SetTitleMatchMode, 1 ;======================================================================================== ; Basic variables and arrays ;======================================================================================== AllCells := "" loop, 9 ; rows { r := a_index loop, 9 ; columns { c := a_index AllCells .= "-" r c } } AllCells := LTrim(AllCells, "-") letter := ["A", "B", "C", "D", "E", "F", "G", "H", "I"] unit := [] loop, 27 { if (a_index <= 9) unit[a_index] := "row " a_index else if (a_index <= 18) unit[a_index] := "column " letter[a_index-9] else unit[a_index] := "block " (a_index-18) } UnitCells := [] loop, 9 { i := a_index UnitCells[i] := "" loop, 9 UnitCells[i] .= i a_index "-" UnitCells[i] := SubStr(UnitCells[i], 1, -1) ; rows UnitCells[i+9] := "" loop, 9 UnitCells[i+9] .= a_index i "-" UnitCells[i+9] := SubStr(UnitCells[i+9], 1, -1) ; columns } UnitRows := [] ; blocks UnitColumns := [] intersection(19, 1, 2, 3, 1, 2, 3) intersection(20, 1, 2, 3, 4, 5, 6) intersection(21, 1, 2, 3, 7, 8, 9) intersection(22, 4, 5, 6, 1, 2, 3) intersection(23, 4, 5, 6, 4, 5, 6) intersection(24, 4, 5, 6, 7, 8, 9) intersection(25, 7, 8, 9, 1, 2, 3) intersection(26, 7, 8, 9, 4, 5, 6) intersection(27, 7, 8, 9, 7, 8, 9) number := {} PencilMark := {} block := {} coord := {} ConnectedCells := {} loop, parse, AllCells, - { rc := a_loopfield r := SubStr(rc, 1, 1) c := SubStr(rc, 2, 1) number[rc] := 0 ; 0 if the cell is empty, else the number in the cell loop, 9 PencilMark[rc a_index] := 0 ; 0 if the pencil mark is not set, 1 if it is set For u, cells in UnitCells if (u >= 19 and InStr(cells, rc)) { block[rc] := u break } coord[rc] := letter[c] r ConnectedCells[rc] := "" loop, parse, % UnitCells[r] "-" UnitCells[c+9] "-" UnitCells[block[rc]], - if (a_loopfield != rc and not InStr(ConnectedCells[rc], a_loopfield)) ConnectedCells[rc] .= "-" a_loopfield ConnectedCells[rc] := LTrim(ConnectedCells[rc], "-") } ;---- colors ---- colors := [] colors[1] := ["white", "0xFFFFFF"] colors[2] := ["yellow", "0xFFFF00"] colors[3] := ["orange", "0xFF8040"] colors[4] := ["red", "0xFF0000"] colors[5] := ["purple", "0x800080"] colors[6] := ["blue", "0x0000FF"] colors[7] := ["light blue", "0x00FFFF"] colors[8] := ["green", "0x00A000"] colors[9] := ["black", "0x202020"] ; Without quote marks, 0xABCDEF would be stored as decimal, that is, as 10*16**5+11*16**4+12*16**3+13*16**2+14*16+15=11259375, ; and when it is used in gui,font, only the last six digits would be used, that is, the color would be 0x259375. ColorName := [] ColorNumber := {} ColorValue := {} loop, 9 { ColorName[a_index] := colors[a_index][1] ; ColorName[1] = white, ColorName[2] = yellow etc. ColorNumber[colors[a_index][1]] := a_index ; ColorNumber["white"] = 1, ColorNumber["yellow"] = 2 etc. ColorValue[colors[a_index][1]] := colors[a_index][2] ; ColorValue["white"] = 0xFFFFFF, ColorValue["yellow"] = 0xFFFF00 etc. } ;---- pale colors to highlight cells ---- HighlightValue := {red: "0xFFCCCC", green: "0xCCFFCC", blue: "0xCCCCFF", orange: "0xFFCCA0"} ;---- starting values ---- cNumber := {} cPencilMark := {} highlight := {} loop, parse, AllCells, - { cNumber[a_loopfield] := "default" loop, 9 cPencilMark[a_loopfield a_index] := "default" } display := "numbers" background("white") cBackgroundForColors := "0xFFEECC" ;======================================================================================== ; Main GUI = Sudoku board and menu ;======================================================================================== ; The Sudoku board is actually a bunch of text controls: ; - GreySquare11 to 81 completely cover the Sudoku board. ; - WhiteSquare11 to 81 are on top of the grey squares and somewhat smaller than the grey squares, so they appear as the ; white squares of the Sudoku board, and the grey squares behind them appear to be the grid. ; - number11 to 81 contain the numbers of the Sudoku: number[11] to number[81] are the numbers set in number11 to number81. ; number11 to 81 are transparent and exactly on top of WhiteSquare11 to 81, so that numbers appear to be set in the white ; squares. ; - PencilMark111 to 819 contain the pencil marks of the Sudoku: PencilMark[111] to PencilMark[819] are the pencil marks set ; in PencilMark111 to 819. PencilMark111 to 819 are transparent and there are 9 pencil mark text controls on top of each ; white square. ; g in Webdings: ; A g in Webdings is a square, and when it completely covers its text control, it "colors" the text control. ; - Dark grey squares in the grey squares are used to make the grid more distinct. (Without Webdings, the option -background ; for the grey squares uses the standard background color rather than the one set by the gui color command, so there is still ; a grid but paler.) ; - Red or green or blue squares in the white squares are used to highlight the squares. (=> Without Webdings, there will be ; little red and green and blue g-s in the "highlighted" squares.) highlight[11] to highlight[81] are the highlighting colors ; in WhiteSquare11 to 81. Numbers and pencil marks in highlighted squares must be on top of the highlighting squares in order ; to not be covered, too! gui, 1:-DPIScale loop, parse, AllCells, - { gui, 1:add, text, vGreySquare%a_loopfield% -background, g gui, 1:add, text, vWhiteSquare%a_loopfield% gui, 1:add, text, vnumber%a_loopfield% +center backgroundtrans loop, 9 gui, 1:add, text, vPencilMark%a_loopfield%%a_index% +center backgroundtrans } caption123 := "1-2-3-4-5-6-7-8-9" loop, parse, caption123, - gui, 1:add, text, vcaption123%a_loopfield% +center, %a_loopfield% captionABC := "A-B-C-D-E-F-G-H-I" loop, parse, captionABC, - gui, 1:add, text, vcaptionABC%a_loopfield% +center, %a_loopfield% ; Arrays for the parameters of the text controls' text: tTextControl := {} ; text cTextControl := {} ; color sTextControl := {} ; size wTextControl := {} ; weight fTextControl := {} ; font ; cTextControl["number" cell] is cNumber[cell] if display="numbers", or ColorName[n] if display="colors". ; If cNumber[cell] is "default", its value is cDefault, or cFixed if the number is fixed. ; cTextControl["PencilMark" cell n] is cPencilMark[cell n] if display="numbers", or ColorName[n] if display="colors". ; If cPencilMark[cell n] is "default", its value is cDefault. ; The values of cDefault and cFixed are set and changed by background(). ; cTextControl is always black in highlighted cells. menu, SudokuMenu, add, &Easy and symmetrical, EasyAndSymmetrical menu, SudokuMenu, add, &Difficult but not symmetrical, DifficultButNotSymmetrical menu, SudokuMenu, add, Super d&ifficult, SuperDifficult menu, SudokuMenu, add, &Open..., open menu, SudokuMenu, add, &Fix, fix menu, SudokuMenu, add, &Save as..., SaveAs menu, SudokuMenu, add ; separator line menu, SudokuMenu, add, hut menu, SudokuMenu, add, tree menu, SudokuMenu, add, autumn tree, AutumnTree menu, SudokuMenu, add, spiral menu, SudokuMenu, add, heart menu, SudokuMenu, add, smiley menu, SudokuMenu, add, star menu, SudokuMenu, add, Christmas tree, ChristmasTree menu, SudokuMenu, add, crown menu, SudokuMenu, add, sun menu, SudokuMenu, add ; separator line menu, SudokuMenu, add, Create image..., CreateImage menu, ViewMenu, add, Larger +, larger menu, ViewMenu, add, Smaller -, smaller menu, ViewMenu, add, Normal size N, NormalSize menu, ViewMenu, add, Mix a color... Ctrl+C, MixColor menu, ViewMenu, add, S&witch from numbers to colors, switch menu, PlayMenu, add, Back page up, back menu, PlayMenu, add, Forward page down, forward menu, PlayMenu, add, &All pencil marks, AllPencilMarks menu, PlayMenu, add, &Pencil mark singles or pairs, PencilMarkSinglesOrPairs menu, PlayMenu, add, &No pencil marks, NoPencilMarks menu, PlayMenu, add, &Clear board, ClearBoard menu, SolveMenu, add, Set and explain one Ctrl+page down, SetOne menu, SolveMenu, add, Set &all, SetAll menu, MenuBar, add, &Sudoku, :SudokuMenu menu, MenuBar, add, &View, :ViewMenu menu, MenuBar, add, &Play, :PlayMenu menu, MenuBar, add, S&olve, :SolveMenu menu, MenuBar, add, &?, ? gui, 1:menu, MenuBar menu, LeftMouseMenu, add menu, RightMouseMenu, add loop { WinTitle := "Sudoku " A_Index if not WinExist(WinTitle) break } GroupAdd, SudokuWindows, %WinTitle% zoom := 1 SizeAndPosition() gui, 1:show, w%wGui% h%wGui%, %WinTitle% ; GuiSize is launched today := A_MM A_DD if (today >= 1201 and today <= 1224) gosub star else if (today >= 1225 or today <= 0106) gosub ChristmasTree history := [] HistoryIndex := 0 history() AutoPencil := 0 return ;======================================================================================== ; Main GUI events ;======================================================================================== GuiSize: wingetpos,,, winwidth, winheight, %WinTitle% ahk_class AutoHotkeyGUI wBorder := (winwidth-a_guiwidth)/2 hTitleMenu := winheight-a_guiheight-wBorder return GuiClose: gui, 1:+OwnDialogs something := 0 loop, parse, AllCells, - { if (number[a_loopfield] != 0) something += 1 else loop, 9 if (PencilMark[a_loopfield a_index] = 1) something += 1 } if something > 3 { msgbox, 0x2003,, Do you want to save the current situation? IfMsgBox, Yes gosub SaveAs IfMsgBox, Cancel return } exitapp ;======================================================================================== ; Subroutines and hotkeys for the main GUI's menu, in the menu's order ;======================================================================================== #ifwinactive ahk_group SudokuWindows ahk_class AutoHotkeyGUI ;---- generate an easy and symmetrical Sudoku ------------------------------------------- EasyAndSymmetrical: gosub PleaseWait gosub ClearBoard fill("random") RandomCells := AllCells sort, RandomCells, random d- AlreadyLooped := "" loop, parse, RandomCells, - if not InStr(AlreadyLooped, a_loopfield) { sym1 := a_loopfield r := SubStr(sym1, 1, 1) c := SubStr(sym1, 2, 1) sym2 := r 10-c sym3 := 10-r c sym4 := 10-r 10-c sym5 := c r sym6 := c 10-r sym7 := 10-c r sym8 := 10-c 10-r SymCells := sym1 "-" sym2 "-" sym3 "-" sym4 "-" sym5 "-" sym6 "-" sym7 "-" sym8 AlreadyLooped .= "-" SymCells loop, 8 number[sym%a_index%] := 0 context := "EasyAndSymmetrical" fill("minimum") context := "" StillOneSolution := 0 if (bifurcation.length() = 0) StillOneSolution := 1 else if (AllValues(minimum) = AllValues(random)) { context := "EasyAndSymmetrical" fill("maximum") context := "" if (AllValues(maximum) = AllValues(minimum)) StillOneSolution := 1 } loop, parse, SymCells, - if StillOneSolution set(a_loopfield, 0) else number[a_loopfield] := random[a_loopfield] loop, parse, AllCells, - if not InStr(SymCells, a_loopfield) number[a_loopfield] := reset[a_loopfield] } if (display = "numbers") loop, parse, AllCells, - if (number[a_loopfield] != 0) { r := SubStr(a_loopfield, 1, 1) c := SubStr(a_loopfield, 2, 1) GreenAdd := abs(r-5)*48 BlueAdd := abs(c-5)*48 RedShade := Format("{:#X}", 255*16**4+GreenAdd*16**2+BlueAdd) cNumber[a_loopfield] := RedShade font("number" a_loopfield, RedShade, sNumber, "Ubuntu", "Arial") } gosub fix history() gui, 1:-disabled gui, PleaseWait:destroy return ;---- generate a difficult (but not symmetrical) Sudoku --------------------------------- DifficultButNotSymmetrical: gosub PleaseWait gosub ClearBoard fill("random") RandomCells := AllCells sort, RandomCells, random d- loop, parse, RandomCells, - { if a_index < 4 { set(a_loopfield, 0) continue } NextOmit := a_loopfield number[NextOmit] := 0 context := "DifficultButNotSymmetrical" fill("minimum") context := "" StillOneSolution := 0 if (bifurcation.length() = 0) StillOneSolution := 1 else if (AllValues(minimum) = AllValues(random)) { context := "DifficultButNotSymmetrical" fill("maximum") context := "" if (AllValues(maximum) = AllValues(minimum)) StillOneSolution := 1 } if StillOneSolution set(NextOmit, 0) else number[NextOmit] := random[NextOmit] loop, parse, AllCells, - if (a_loopfield != NextOmit) number[a_loopfield] := reset[a_loopfield] } gosub fix history() gui, 1:-disabled gui, PleaseWait:destroy return ;---- generate a super difficult Sudoku ------------------------------------------------- SuperDifficult: CancelSuperDifficult := 0 loop { gosub DifficultButNotSymmetrical sleep 400 SuperDifficultString := GuiToString() TotalLineCount := 0 SuperDifficult := 0 TryAndError := 0 loop, 40 { if CancelSuperDifficult exit caller := "SuperDifficult" gosub setone caller := "" sleep 100 if (next["action"] = "several possible numbers") { TryAndError := 1 break } else if not SuperDifficult { if (LineCount >= 3) TotalLineCount += LineCount if (LineCount >= 7 or TotalLineCount >= 20) SuperDifficult := 1 ; No break, there might still be a TryAndError. } } if SuperDifficult and not TryAndError { StringToGui(SuperDifficultString) gosub fix history() return } } ;---- open ------------------------------------------------------------------------------ open: gui, 1:+OwnDialogs if not FileExist(a_desktop "\Sudoku") FileCreateDir, %a_desktop%\Sudoku FileSelectFile, Sudoku,, %a_desktop%\Sudoku\ if (errorlevel = 0) { fileread, string, %Sudoku% StringToGui(string) history() AutoPencil := 0 } return ;---- fix/unfix ------------------------------------------------------------------------- fix: FixedCells := "" loop, parse, AllCells, - if (number[a_loopfield] != 0) FixedCells .= "-" a_loopfield FixedCells := LTrim(FixedCells, "-") if (FixedCells != "") { loop, parse, FixedCells, - if (cNumber[a_loopfield] = "default") set(a_loopfield, number[a_loopfield]) menu, SudokuMenu, rename, 5&, &Unfix menu, SudokuMenu, add, &Unfix, unfix history() } return unfix: if (FixedCells != "") { copy := FixedCells FixedCells := "" ; FixedCells influences set() loop, parse, copy, - if (cNumber[a_loopfield] = "default") set(a_loopfield, number[a_loopfield]) menu, SudokuMenu, rename, 5&, &Fix menu, SudokuMenu, add, &Fix, fix history() } return ;---- save as --------------------------------------------------------------------------- SaveAs: gui, 1:+OwnDialogs if not FileExist(a_desktop "\Sudoku") FileCreateDir, %a_desktop%\Sudoku FileSelectFile, Sudoku, S16, %a_desktop%\Sudoku\ if (errorlevel = 0) { string := GuiToString() if not InStr(SubStr(Sudoku, -4), ".") ; if the user didn't write an extension Sudoku .= ".txt" if FileExist(Sudoku) filedelete, %Sudoku% fileappend, %string%, %Sudoku% } return ;---- hut ------------------------------------------------------------------------------- hut: string := "numbers/FFFFFF/1528B5B37,2448B5B37,2618B603D,3348B5B37,3718B603D,4248B5B37," . "4818B603D,528A7815D,56778563A,579916946,584916946,622A7815D,6338B5B37,6498B5B37,66878563A," . "685A27757,723A7815D,7428A6440,769BC9B7A,778BC9B7A,786A27757,825A7815D,8468A6440,883A27757," . "929A7815D,9438A6440,982A27757" StringToGui(string) gosub fix AutoPencil := 0 history() return ;---- tree ------------------------------------------------------------------------------ tree: string := "numbers/FFFFFF/137green,149green,151green,164green,223green,271green,316green," . "388green,418green,485green,512green,589green,625green,674green,731green,743804000," . "754804000,767green,842804000,855804000,941804000,956804000" StringToGui(string) gosub fix AutoPencil := 0 history() return ;---- autumn tree ----------------------------------------------------------------------- AutumnTree: string := "numbers/FFFFFF/133FF6400,144FFB700,1597D7327,165FFB700,226FF4000,275FF6400," . "317FF6400,381FF4000,412FFB700,483FF6400,5117D7327,588FFB700,628FFB700,6797D7327,735FF6400," . "743654000,756654000,769FFB700,842654000,855654000,941654000,954654000" StringToGui(string) gosub fix AutoPencil := 0 history() return ;---- spiral ---------------------------------------------------------------------------- spiral: string := "numbers/FFFFFF/2420024FF,257006DFF,26500B6FF,3322500FF,37900FFFF,4276E00FF," . "456FFB700,48400FFB7,525B700FF,543FFFF00,561FF6E00,58600FF6E,621FF00FF,662FF2500,68800FF25," . "733FF00B6,748FF006D,759FF0024,78224FF00,8756DFF00,967B6FF00" StringToGui(string) gosub fix AutoPencil := 0 history() return ;---- heart ----------------------------------------------------------------------------- heart: string := "numbers/FFFFFF/226CC0000,233CC0000,277CC0000,282CC0000,311CC0000,348CC0000," . "364CC0000,395CC0000,414CC0000,456CC0000,492CC0000,519CC0000,591CC0000,621CC0000,688CC0000," . "738CC0000,776CC0000,841CC0000,862CC0000,957CC0000" StringToGui(string) gosub fix AutoPencil := 0 history() return ;---- smiley ---------------------------------------------------------------------------- smiley: string := "numbers/FFFF77/147FF2C00,155FF2C00,161FF2C00,231FF2C00,278FF2C00,327FF2C00,346FF2C00," . "369FF2C00,381FF2C00,428FF2C00,484FF2C00,525FF2C00,536FF2C00,573FF2C00,587FF2C00,621FF2C00," . "642FF2C00,657FF2C00,664FF2C00,685FF2C00,733FF2C00,771FF2C00,845FF2C00,853FF2C00,862FF2C00" StringToGui(string) gosub fix AutoPencil := 0 history() return ;---- Christmas tree -------------------------------------------------------------------- ChristmasTree: string := "colors/FFEECC/114,152,196,251,343,364,448,465,532,578,637,673,721,788,828,835,842," . "857,863,871,884,959" StringToGui(string) gosub fix AutoPencil := 0 history() return ;---- crown ----------------------------------------------------------------------------- crown: string := "colors/FFEECC/216,254,298,312,323,345,368,384,396,413,432,476,495,514,592,618,651," . "694,717,791,811,828,834,842,859,866,873,885,897" StringToGui(string) gosub fix AutoPencil := 0 history() return ;---- sun ------------------------------------------------------------------------------- sun: string := "colors/DDEEFF/136,168,193,242,263,287,351,372,411,426,435,443,482,498,552,571,644," . "661,683,734,762,797,823,866,912,967" StringToGui(string) gosub fix AutoPencil := 0 history() return ;---- star ------------------------------------------------------------------------------ star: string := "numbers/70/151FFFFFF,221FFFFFF,254FFFFFF,287FFFFFF,342FFFFFF,365FFFFFF,431FFFFFF," . "449FFFFFF,468FFFFFF,472FFFFFF,512FFFFFF,528FFFFFF,584FFFFFF,597FFFFFF,633FFFFFF,644FFFFFF," . "667FFFFFF,675FFFFFF,745FFFFFF,769FFFFFF,827FFFFFF,858FFFFFF,881FFFFFF,957FFFFFF/" StringToGui(string) gosub fix AutoPencil := 0 history() return ;---- create image ---------------------------------------------------------------------- CreateImage: ; CONTENTS: ; ; Paint an image ; ; Check the image: ; 1. Less than 17 cells? ; 2. No pencil marks? ; 3. Two numbers missing completely? ; 4. Two rows or columns in the same blocks completely empty? ; 5. Less than n possible numbers in n cells in one unit? ; ; Create a Sudoku from the image: ; loop ; { ; Fill PencilImage with pencilled (and possible) numbers. ; Start over when the second loop tried all found images and doesn't find any more images. ; Give up after three iterations. ; loop ; { ; loop PencilImage ; { ; Try all pencilled (and possible) numbers in a_loopfield. ; If the resulting image is already in AllImages, skip the number. ; Solve the Sudoku with fill("minimum") and fill("maximum"). ; If the solutions are identical, then there is only one solution and the Sudoku is ready. ; Otherwise add the image to AllImages and SortedImages. ; } ; Take the best image from SortedImages and continue the second loop. ; If there are no SortedImages, break the second loop and start over in the first loop. ; } ; } if GuiCreateImage { gui, CreateImage:show return } if GuiPaintImage { gui, PaintImage:show return } ContinueLastCreating := 0 if FileExist(a_desktop "\Sudoku\Create image\PencilImage.txt") { msgbox, 0x2104,, The last creating was canceled. Do you want to continue it? IfMsgBox, Yes { fileread, PencilImage, %a_desktop%\Sudoku\Create image\PencilImage.txt StringToGui(PencilImage) history() ContinueLastCreating := 1 gosub PaintImageButtonCreateImage return } } gosub unfix AutoPencil := 0 loop, parse, AllCells, - highlight(a_loopfield, "") tooltip context := "CreateImage_PaintImage" gui, 1:-disabled gui, PaintImage:font, s12 gui, PaintImage:add, text,, ( Paint an image with pencil marks and %display%: Set/delete numbers and pencil marks as usual. The P key sets all possible pencil marks in a cell. The program will try to create a Sudoku from your image: Empty cells will be left empty, preset %display% will remain unchanged, cells with pencil marks will be filled with one of the pencil marked %display%. For a reasonable chance to create a Sudoku from the image, there should be preset %display% or pencil marks in at least 20 cells. ) if (display = "colors") { gui, PaintImage:add, text, xs, % "1 = " ColorName[1] gui, PaintImage:add, text, xp+120, % "2 = " ColorName[2] gui, PaintImage:add, text, xp+120, % "3 = " ColorName[3] gui, PaintImage:add, text, xp+120, % "4 = " ColorName[4] gui, PaintImage:add, text, xp+120, % "5 = " ColorName[5] gui, PaintImage:add, text, xs, % "6 = " ColorName[6] gui, PaintImage:add, text, xp+120, % "7 = " ColorName[7] gui, PaintImage:add, text, xp+120, % "8 = " ColorName[8] gui, PaintImage:add, text, xp+120, % "9 = " ColorName[9] } gui, PaintImage:add, button, xs y+20, Create image gui, PaintImage:add, button, x+20, Cancel gui, PaintImage:show,, Paint image ... GuiPaintImage := 1 ArrangeWindows("PaintImage") return PaintImageGuiSize: PaintImageWidth := a_guiwidth PaintImageHeight := a_guiheight+30 ; +30 for the title bar return PaintImageButtonCancel: PaintImageGuiClose: PaintImageGuiEscape: gui, PaintImage:destroy GuiPaintImage := 0 if (context = "CreateImage") CancelWait := 1 else context := "" return PaintImageButtonCreateImage: HistoryIndex0 := HistoryIndex AutoPencil := 0 gui, 1:+OwnDialogs winactivate, %WinTitle% context := "CreateImage" image := "" PresetImage := "" PencilImage := "" loop, parse, AllCells, - { if (number[a_loopfield] != 0) image .= a_loopfield "-" else loop, 9 if (PencilMark[a_loopfield a_index] = 1) { image .= a_loopfield "-" break } } image := RTrim(image, "-") if (StrLen(image) < 50) ;---- Less than 17 cells? ---- { msgbox, There have to be preset %display% or pencil marks in at least 17 cells. (The fewest %display% possible for a proper Sudoku is 17.) context := "CreateImage_PaintImage" return } loop, parse, image, - if (number[a_loopfield] != 0) PresetImage .= a_loopfield "-" else PencilImage .= a_loopfield "-" PresetImage := RTrim(PresetImage, "-") PencilImage := RTrim(PencilImage, "-") if (PencilImage = "") ;---- No pencil marks? ---- { msgbox, ( There are no pencil marks. There have to be pencil marks so that the Sudoku creator can try different %display%. ) context := "CreateImage_PaintImage" return } missing := "123456789" ; ---- Two numbers missing completely? ---- loop, parse, image, - { missing := StrReplace(missing, number[a_loopfield]) loop, 9 { if (PencilMark[a_loopfield a_index] = 1) missing := StrReplace(missing, a_index) } } if (StrLen(missing) >= 2) { missing1 := SubStr(missing, 1, 1) missing2 := SubStr(missing, 2, 1) msgbox, % "There can be no unique solution because " item(missing1) " and " item(missing2) " are missing completely and could be swapped." context := "CreateImage_PaintImage" return } loop, 6 ;---- Two rows or columns in the same blocks completely empty? ---- { i0 := a_index*3-3 ; i1 and i2 must be in the same blocks = both in unit 1, 2, 3 or 4, 5, 6 or 7, 8, 9 etc. i0 serves as starting point to find i1 and i2. i1 := "" loop, 3 { i2 := i0+a_index ; i0 = 0 => i2 = 1, 2, 3 etc. empty := 1 loop, parse, % UnitCells[i2], - if InStr(image, a_loopfield) { empty := 0 break } if (empty = 1) { if (i1 = "") i1 := i2 else { msgbox, % "There can be no unique solution because " unit[i1] " and " unit[i2] " are completely empty and could be swapped." context := "CreateImage_PaintImage" return } } } } loop, 27 ;---- Less than n possible numbers in n cells in one unit? ---- { u := a_index PencilCellsInU := [] loop, parse, % UnitCells[u], - if InStr(PencilImage, a_loopfield) loop, 9 if (PencilMark[a_loopfield a_index] = 0) ; at least one pencil mark not set { PencilCellsInU.push(a_loopfield) break } if (PencilCellsInU.length() < 2) continue ; next unit patterns := [12, 123, 1234, 12345, 123456, 1234567, 12345678, 123456789] ; The patterns will be permutated: e.g. 1234 -> 1235, 1236, 1237, 1238, 1239, 1245, 1246, 1247 etc. ; The permutated patterns will be compared with PencilCellsInU, e.g. pattern 1247: are there less than four pencil marked numbers in cells 1, 2, 4, 7? loop, 7 if (patterns.length()+1 > PencilCellsInU.length()) ; patterns.length()+1 = length of the last (longest) pattern patterns.Pop() ; Longer patterns than PencilCellsInU are not needed. for i, pattern in patterns { loop ; for permutations of pattern { PencilCellsInPattern := [] PencilMarksInPattern := "" loop, parse, pattern { PencilCell := PencilCellsInU[a_loopfield] ; a_loopfield, not a_index: if the pattern is 1235, a_index 4 would get cell 4 of PencilCellsInU, but a_loopfield 5 gets cell 5 of PencilCellsInU. PencilCellsInPattern.push(PencilCell) loop, 9 if (PencilMark[PencilCell a_index] = 1 and not InStr(PencilMarksInPattern, a_index)) PencilMarksInPattern .= a_index } if (StrLen(PencilMarksInPattern) < StrLen(pattern)) { PencilCellsText := "" for i, PencilCell in PencilCellsInPattern { PencilCellsText .= coord[PencilCell] " and " highlight(PencilCell, "blue") } PencilCellsText := SubStr(PencilCellsText, 1, -5) if (StrLen(PencilMarksInPattern) = 1) PencilMarksText := " there is only one possible " SubStr(display, 1, -1) else PencilMarksText := " there are only " StrLen(PencilMarksInPattern) " possible " display msgbox, % "In " PencilCellsText " " PencilMarksText ", so one cell can't be filled." ; It's always "one cell" because shorter patterns are looped first. context := "CreateImage_PaintImage" return } pattern := NextPermutation(pattern, PencilCellsInU.length()) if (pattern = "") break ; exit the loop for permutations } } } PencilledNumbers := {} loop, parse, PencilImage, - loop, 9 if (PencilMark[a_loopfield a_index] = 1) PencilledNumbers[a_loopfield] .= a_index AllImages := [] SortedImages := [] loop, 60 if (a_index >= 4) SortedImages[a_index] := [] if ContinueLastCreating { fileread, AllImagesString, %a_desktop%\Sudoku\Create image\AllImages.txt examined := 0 loop, parse, AllImagesString, - { AllImages.push(a_loopfield) examined += 1 } BestDifferences := "" loop, 60 if (a_index >= 4) { SortedImagesIndex := a_index fileread, SortedImagesString, %a_desktop%\Sudoku\Create image\SortedImages%SortedImagesIndex%.txt if (BestDifferences = "" and SortedImagesString != "") BestDifferences := SortedImagesIndex loop, parse, SortedImagesString, - SortedImages[SortedImagesIndex].push(a_loopfield) } } CancelWait := 0 ;---- Create a Sudoku from the image ---- loop { fill("random") if not AnySolution { msgbox, There is no solution. context := "CreateImage_PaintImage" HistoryIndex := HistoryIndex0 StringToGui(history[HistoryIndex]) return } if (a_index = 1) { gui, PaintImage:destroy GuiPaintImage := 0 gui, 1:+disabled gui, CreateImage:font, s12 gui, CreateImage:add, text, vCreateImageText w500 r7 gui, CreateImage:add, button,, Cancel gui, CreateImage:show,, Create image ... GuiCreateImage := 1 ArrangeWindows("CreateImage") } else if (a_index = 4) { guicontrol, CreateImage:text, CreateImageText, CAN'T CREATE A SUDOKU WITH A UNIQUE SOLUTION. context := "" AllImages := "" SortedImages := "" FileRemoveDir, %a_desktop%\Sudoku\Create image, 1 return } gosub LeaveImage if not ContinueLastCreating { examined := 0 BestDifferences := "" } loop { LoopIndex := a_index CopyArray(PencilledNumbers, "PencilledAndPossibleNumbers", 1) loop, parse, PencilImage, - { rc := a_loopfield loop, parse, % ConnectedCells[rc], - if (number[a_loopfield] != 0) PencilledAndPossibleNumbers[rc] := StrReplace(PencilledAndPossibleNumbers[rc], number[a_loopfield]) } loop, parse, PencilImage, - { PencilImageIndex := a_index PencilImageLoopfield := a_loopfield StartingValue := number[PencilImageLoopfield] if (HighlightedLoopfield != "") highlight(HighlightedLoopfield, "") highlight(PencilImageLoopfield, "orange") HighlightedLoopfield := PencilImageLoopfield loop, parse, % PencilledAndPossibleNumbers[PencilImageLoopfield] if (a_loopfield != StartingValue or LoopIndex = 1 and PencilImageIndex = 1) { set(PencilImageLoopfield, a_loopfield) PencilImageString := "" loop, parse, PencilImage, - PencilImageString .= number[a_loopfield] if InValues(AllImages, PencilImageString) continue examined += 1 fill("minimum") if AnySolution { fill("maximum") gosub LeaveImage differences := 0 loop, parse, AllCells, - if (maximum[a_loopfield] != minimum[a_loopfield]) differences += 1 if (differences = 0) { gui, 1:-disabled guicontrol, CreateImage:text, CreateImageText, ( Loops: %LoopIndex% Examined images: %examined% Ready! You can improve the image by swapping numbers and by coloring, see View and ? in the menu. ) gui, CreateImage:add, button, x+20, Save highlight(HighlightedLoopfield, "") HighlightedLoopfield := "" history() context := "" AllImages := "" SortedImages := "" FileRemoveDir, %a_desktop%\Sudoku\Create image, 1 return } else { AllImages.push(PencilImageString) SortedImages[differences].push(PencilImageString) guicontrol, CreateImage:text, CreateImageText, ( Loop %LoopIndex% Examined images: %examined% Cells with no unique solution - in the last examined image: %differences% - in the best not yet looped image: %BestDifferences% Please wait, this can take some time ... ) } } if CancelWait gosub CancelNow } set(PencilImageLoopfield, StartingValue) } PencilImageString := "" loop, 60 if (a_index >= 4 and SortedImages[a_index].length() != 0) { PencilImageString := SortedImages[a_index].pop() BestDifferences := a_index loop, parse, PencilImage, - set(a_loopfield, SubStr(PencilImageString, a_index, 1)) break } if (PencilImageString = "") { HistoryIndex := HistoryIndex0 StringToGui(history[HistoryIndex]) guicontrol, CreateImage:text, CreateImageText, Start over ... sleep 2000 break } } } return CreateImageGuiSize: CreateImageWidth := a_guiwidth CreateImageHeight := a_guiheight+30 ; +30 for the title bar return CreateImageButtonCancel: CreateImageGuiClose: ; no CreateImageGuiEscape to avoid canceling by mistake if (context = "CreateImage") { CancelWait := 1 return } CancelNow: gui, 1:-disabled gui, CreateImage:destroy GuiCreateImage := 0 if (HighlightedLoopfield != "") { highlight(HighlightedLoopfield, "") HighlightedLoopfield := "" } if (context = "CreateImage") { HistoryIndex := HistoryIndex0 StringToGui(history[HistoryIndex]) gosub CreateImageSave } context := "" AllImages := "" SortedImages := "" exit CreateImageButtonSave: gui, CreateImage:destroy GuiCreateImage := 0 gosub SaveAs return ;---- larger ---------------------------------------------------------------------------- larger: +:: NumpadAdd:: MouseGetCell() ; returns rm and cm gui, 1:hide SizeAndPosition(0.1) gui, 1:show, w%wGui% h%wGui% if (rm != "" and cm != "") MouseClickCell(rm cm) return ;---- smaller --------------------------------------------------------------------------- smaller: -:: NumpadSub:: MouseGetCell() ; returns rm and cm gui, 1:hide SizeAndPosition(-0.1) gui, 1:show, w%wGui% h%wGui% if (rm != "" and cm != "") MouseClickCell(rm cm) return ;---- normal size ----------------------------------------------------------------------- NormalSize: N:: MouseGetCell() ; returns rm and cm gui, 1:hide SizeAndPosition(1-zoom) gui, 1:show, w%wGui% h%wGui% if (rm != "" and cm != "") MouseClickCell(rm cm) return ;---- mix color ------------------------------------------------------------------------- MixColor: ^c:: if (GuiMixColor = 1) { gui, MixColor:show return } gui, MixColor:font, s12 gui, MixColor:add, Text,, Mix a color: gui, MixColor:add, text, xm, Red: gui, MixColor:add, slider, xp+60 vrDecimal gSlider AltSubmit range0-255 gui, MixColor:add, text, xm, Green: gui, MixColor:add, slider, xp+60 vgDecimal gSlider AltSubmit range0-255 gui, MixColor:add, text, xm, Blue: gui, MixColor:add, slider, xp+60 vbDecimal gSlider AltSubmit range0-255 gui, MixColor:font, s60 c000000, Webdings gui, MixColor:add, text, xm w80 h80 vMixColor, g gui, MixColor:font gui, MixColor:font, s12 gui, MixColor:add, text, x+20 w140 vMixValue Gui, MixColor:add, Radio, xm vRadios gRadios checked, Background color Gui, MixColor:add, Radio, xm gRadios, Custom color for numbers and pencil marks gui, MixColor:add, button, xm default, OK gui, MixColor:add, button, x+20, Cancel gui, MixColor:add, text, xm, ( Apply the color to a number or pencil mark with the C key. If there is more than one pencil mark in the cell, press the key repeatedly. ) gui, MixColor:show,, Mix a color ... GuiMixColor := 1 ArrangeWindows("MixColor") return MixColorGuiSize: MixColorWidth := a_guiwidth MixColorHeight := a_guiheight+30 ; +30 for the title bar return Slider: gui, MixColor:Submit, NoHide mix := Format("{:#X}", rDecimal*16**4+gDecimal*16**2+bDecimal) gui, MixColor:font, s60 c%mix%, Webdings GuiControl, font, MixColor GuiControl, text, MixValue, %mix% return Radios: gui, MixColor:Submit, NoHide return MixColorButtonOK: if (Radios = 1) { if (mix != cBackground) { background(mix) loop, parse, AllCells, - { if (number[a_loopfield] != 0) set(a_loopfield, number[a_loopfield]) else loop, 9 if (PencilMark[a_loopfield a_index] = 1) PencilMark(a_loopfield, a_index, 1) } history() } } else { cCustom := "xyz" . mix ; Without "xyz", cCustom would be stored as decimal, and when it is used in gui,font, only the last six digits would be used. ; "xyz" will be removed by hex(). } return MixColorButtonCancel: MixColorGuiClose: MixColorGuiEscape: gui, MixColor:destroy GuiMixColor := 0 return ;---- switch from numbers to colors/from colors to numbers ------------------------------ switch: if (display = "colors") { display := "numbers" background(cBackgroundForNumbers) menu, ViewMenu, rename, 5&, S&witch from numbers to colors } else { display := "colors" background(cBackgroundForColors) menu, ViewMenu, rename, 5&, S&witch from colors to numbers } loop, parse, AllCells, - { if (number[a_loopfield] != 0) set(a_loopfield, number[a_loopfield]) else loop, 9 if (PencilMark[a_loopfield a_index] = 1) PencilMark(a_loopfield, a_index, 1) } history() return ;---- back ------------------------------------------------------------------------------ back: pgup:: if (HistoryIndex > 1) { HistoryIndex -= 1 StringToGui(history[HistoryIndex]) } return ;---- forward --------------------------------------------------------------------------- forward: pgdn:: if (HistoryIndex < history.MaxIndex()) { HistoryIndex += 1 StringToGui(history[HistoryIndex]) } return ;---- all pencil marks ------------------------------------------------------------------ AllPencilMarks: AutoPencil := "AllPossibleNumbers" AutoPencil() history() return ;---- pencil mark singles or pairs ------------------------------------------------------ PencilMarkSinglesOrPairs: AutoPencil := "SinglesOrPairs" AutoPencil() history() return ;---- no pencil marks ------------------------------------------------------------------- NoPencilMarks: AutoPencil := 0 for rcn, x in PencilMark if (x = 1) { rc := SubStr(rcn, 1, 2) n := SubStr(rcn, 3, 1) PencilMark(rc, n, 0) } history() return ;---- clear board ----------------------------------------------------------------------- ClearBoard: string := display "/" cBackgroundFor%display% StringToGui(string) history() AutoPencil := 0 return ;---- set and explain one --------------------------------------------------------------- SetOne: ^pgdn:: tooltip gui, explain:destroy loop, parse, AllCells, - highlight(a_loopfield, "") GetPossibleNumbers("SetOne") gui, 1:-disabled gui, PleaseWait:destroy text := "" KeepExplanationAndHighlightings := "" loop, parse, AllCells, - KeepExplanationAndHighlightings .= a_loopfield number[a_loopfield] "-" if (next["action"] = "solved") return else if (next["action"] = "no possible number") { rc := next["rc"] highlight(rc, "red") MouseClickCell(rc) text := "This cell can't be filled." concat := "" loop, 9 concat .= explanation[rc a_index] if (concat != "") text := concat chr(8594) " " text if (SubStr(text, 1, 1) = chr(8594)) ; no explanations added text := SubStr(text, 3) } else if (next["action"] = "no possible cell") { u := next["u"] n := next["n"] loop, parse, % UnitCells[u], - if (number[a_loopfield] = 0) { highlight(a_loopfield, "red") MouseClickCell(a_loopfield) sleep 400 } text := n " can't be set anywhere in " unit[u] "." concat := "" loop, parse, % UnitCells[u], - concat .= explanation[a_loopfield n] if (concat != "") text := concat chr(8594) " " text if (SubStr(text, 1, 1) = chr(8594)) text := SubStr(text, 3) } else if (next["action"] = "one possible number") { rc := next["rc"] n := next["n"] highlight(rc, "green") ; Highlight after set would reset n and cause a flicker. KeepExplanationAndHighlightings .= rc n ; Set before KeepExplanationAndHighlightings would delete KeepExplanationAndHighlightings. set(rc, n) if (caller != "SuperDifficult") MouseClickCell(rc) loop, parse, % ConnectedCells[rc], - PencilMark(a_loopfield, n, 0) AutoPencil() history() text := n " is the only possible number in " rc "." concat := "" loop, 9 if (a_index != n) concat .= explanation[rc a_index] if (concat != "") text := concat chr(8594) " " text if (SubStr(text, 1, 1) = chr(8594)) text := SubStr(text, 3) } else if (next["action"] = "one possible cell") { u := next["u"] rc := next["rc"] n := next["n"] highlight(rc, "green") KeepExplanationAndHighlightings .= rc n set(rc, n) if (caller != "SuperDifficult") MouseClickCell(rc) loop, parse, % ConnectedCells[rc], - PencilMark(a_loopfield, n, 0) AutoPencil() history() text := rc " is the only possible cell for " n " in " unit[u] "." concat := "" loop, parse, % UnitCells[u], - if (a_loopfield != rc) concat .= explanation[a_loopfield n] if (concat != "") text := concat chr(8594) " " text if (SubStr(text, 1, 1) = chr(8594)) text := SubStr(text, 3) } else if (next["action"] = "several possible numbers") { if (caller = "SuperDifficult") return rc := next["rc"] SeveralPossibleNumbers := next["n"] MouseClickCell(rc) ProvedNumbers := "" loop, parse, SeveralPossibleNumbers { set(rc, a_loopfield) fill("minimum") if AnySolution ProvedNumbers .= a_loopfield history() gosub back } if (StrLen(ProvedNumbers) = 0) { highlight(rc, "red") text := "This cell could be filled with " loop, parse, SeveralPossibleNumbers text .= a_loopfield " or " text := SubStr(text, 1, -4) text .= ", but there is no solution with any of them." } else { if (StrLen(ProvedNumbers) = 1) { highlight(rc, "green") KeepExplanationAndHighlightings .= rc ProvedNumbers set(rc, ProvedNumbers) loop, parse, % ConnectedCells[rc], - PencilMark(a_loopfield, ProvedNumbers, 0) AutoPencil() history() } else highlight(rc, "blue") text := "Possible numbers: " loop, parse, SeveralPossibleNumbers text .= a_loopfield ", " text := SubStr(text, 1, -2) ". There is a solution with: " loop, parse, ProvedNumbers text .= a_loopfield ", " text := SubStr(text, 1, -2) "." } } TextElements := [] loop, parse, text, $ TextElements.push(a_loopfield) ;---- omit repetitions ---- TextElements2 := [] for i, element in TextElements if not InValues(TextElements2, element) TextElements2.push(element) CopyArray(TextElements2, "TextElements", 1) ;-------- TextElements := merge(TextElements, "n") TextElements := merge(TextElements, "rc") ;---- rc -> coord[rc], n -> ColorName[n], "number" -> "color" ---- text := "" for i, element in TextElements text .= element "`n" text := SubStr(text, 1, -1) text2 := "" loopfield1 := "" loopfield2 := "" delimiter1 := "" delimiter2 := "" loop, parse, text { if (a_loopfield = a_space or a_loopfield = "/" or a_loopfield = "." or a_loopfield = ":" or a_loopfield = "," or a_loopfield = ";" or a_loopfield = "`n" or a_loopfield = "`r") { delimiter1 := delimiter2 delimiter2 := a_loopfield if (loopfield2 = "") text2 .= delimiter2 else if loopfield2 is digit { if (StrLen(loopfield2) = 1) { if (display = "colors") { if (delimiter1 = "" or delimiter1 = "`n" or delimiter1 = "`r" or delimiter1 = " " and loopfield1 = chr(8594)) text2 .= format("{:T}", ColorName[loopfield2]) delimiter2 else if (loopfield1 != "row" and loopfield1 != "column" and loopfield1 != "block") text2 .= ColorName[loopfield2] delimiter2 else text2 .= loopfield2 delimiter2 } else text2 .= loopfield2 delimiter2 } else if (StrLen(loopfield2) = 2) text2 .= coord[loopfield2] delimiter2 } else text2 .= loopfield2 delimiter2 loopfield1 := loopfield2 loopfield2 := "" } else loopfield2 .= a_loopfield } if (display = "colors") text2 := StrReplace(text2, "number", "color") text := text2 ;---- highlight cells ---- loop, parse, AllCells, - if (highlight[a_loopfield] != "green") if InStr(text, coord[a_loopfield] " ") highlight(a_loopfield, "red") else if InStr(text, coord[a_loopfield] " ") highlight(a_loopfield, "blue") text := StrReplace(text, " ") text := StrReplace(text, " ") ;---- wrap text ---- text2 := "" LineLenMax := 120 loop, parse, text, `n`r if (a_loopfield != "") { line := a_loopfield if (StrLen(line) > LineLenMax) { line2 := "" Line2Len := 0 loop, parse, line, %a_space% if (a_loopfield != "") { Line2Len += StrLen(a_loopfield) if (Line2Len > LineLenMax) { line2 .= "`n" a_loopfield Line2Len := 0 } else line2 .= " " a_loopfield } line := LTrim(line2) } text2 .= "`n" line } text := SubStr(text2, 2) ;---- tooltip or gui? ---- loop, parse, text, `n`r LineCount := a_index if (caller != "SuperDifficult") { if (LineCount <= 4) tooltip, %text% else { gui, explain:-caption +border gui, explain:color, FFFFE1 gui, explain:font, s12 gui, explain:add, text,, %text% gui, explain:show ArrangeWindows("explain") } sleep 100 gui, 1:show MouseClickCell(rm cm) } return ExplainGuiSize: ExplainWidth := a_guiwidth ExplainHeight := a_guiheight return ExplainGuiEscape: gui, explain:destroy loop, parse, AllCells, - highlight(a_loopfield, "") return ;---- set all --------------------------------------------------------------------------- SetAll: gui, 1:+OwnDialogs loop, parse, AllCells, - highlight(a_loopfield, "") fill("minimum") if not AnySolution msgbox, There is no solution! else if AnySolution { history() sleep 1000 fill("maximum") if (AllValues(maximum) = AllValues(minimum)) msgbox, There is only one solution! else { history() msgbox, There is a second solution! } } return ;---- ? --------------------------------------------------------------------------------- ?: if (GuiHelp = 1) { gui, help:show return } gui, help:font, s12 gui, help:add, text,, ( Set numbers with the left mouse button or the number keys. Delete numbers with the left mouse button. Swap numbers with the S key. Set/delete pencil marks with the right mouse button or with Shift+number key. Set all possible pencil marks in a cell with the P key. Delete everything in a cell with the space bar. Color the just set number/pencil mark or (three seconds later) any number/pencil mark with the R, G, B, D or C key. If there is more than one pencil mark in a cell, press the key repeatedly. R, G, B, D, C = red, green, blue, default color, custom color (see "Mix a color..." in the View menu). Colors can be used to highlight numbers and pencil marks or to improve a Sudoku image. "All pencil marks" and "Pencil mark singles or pairs" in the Play menu highlight one or two possible cells in a row/column/block with red/green/blue pencil marks. "Switch from numbers to colors" in the View menu is something different: nine colors REPLACE the nine numbers, and the Sudoku must be solved with colors instead of numbers. ) gui, help:add, text, xs, % "1 = " ColorName[1] gui, help:add, text, xp+120, % "2 = " ColorName[2] gui, help:add, text, xp+120, % "3 = " ColorName[3] gui, help:add, text, xp+120, % "4 = " ColorName[4] gui, help:add, text, xp+120, % "5 = " ColorName[5] gui, help:add, text, xs, % "6 = " ColorName[6] gui, help:add, text, xp+120, % "7 = " ColorName[7] gui, help:add, text, xp+120, % "8 = " ColorName[8] gui, help:add, text, xp+120, % "9 = " ColorName[9] gui, help:add, text, xs y+10, ( Highlight a cell/delete the highlighting with Shift/Ctrl/Alt+left click. Highlight all cells with the same number/delete the highlightings with Shift/Ctrl/Alt+right click on one of the cells. The arrow keys also move the mouse cursor. The Esc key deletes the tooltip and all highlightings and cancels "Super difficult". See the menus for more hotkeys. The compiled program (that is, the .exe file but not the original .ahk file) also works with Wine in Linux, but without the font Webdings colors will appear as "=", and highlighted cells will appear as "g". To fix this, install ttf-mscorefonts-installer or copy Webdings.ttf from the Windows fonts folder (C:\Windows\Fonts) to the Linux truetype folder (probably /usr/share/fonts/truetype). Open the Linux fonts folder "as Administrator"/"as Root"! ) gui, help:show,, ? GuiHelp := 1 ArrangeWindows("help") return HelpGuiSize: HelpWidth := a_guiwidth HelpHeight := a_guiheight+30 ; +30 for the title bar return HelpGuiClose: HelpGuiEscape: gui, help:destroy GuiHelp := 0 return ;======================================================================================== ; Other hotkeys for the main GUI ;======================================================================================== ~Esc:: ; cancel tooltip gui, explain:destroy loop, parse, AllCells, - highlight(a_loopfield, "") swap1 := "" CancelSuperDifficult := 1 return space:: ; delete everything in a cell if (MouseGetCell() and not InStr(FixedCells, rm cm)) { set(rm cm, 0, "default") loop, 9 PencilMark(rm cm, a_index, 0, "default") AutoPencil() history() } return ;---- hotkey letters -------------------------------------------------------------------- r::color("red") g::color("green") b::color("blue") d::color("default") c:: if (cCustom != "") color(cCustom) return p:: ; set all pencil marks if MouseGetCell() and not InStr(FixedCells, rm cm) { set(rm cm, 0) loop, 9 { i := a_index valid := 1 loop, parse, % ConnectedCells[rm cm], - if (number[a_loopfield] = i) { valid := 0 break } if valid PencilMark(rm cm, i, 1) } history() } return s:: ; swap numbers if MouseGetCell() and not InStr(FixedCells, rm cm) { if (number[rm cm] != 0 and swap1 = "") { swap1 := number[rm cm] tooltip, % "Swap " item(swap1) " and ..." } else if (number[rm cm] != 0 and number[rm cm] = swap1) { swap1 := "" tooltip } else if (number[rm cm] != 0 and swap1 != "" and number[rm cm] != swap1) { swap2 := number[rm cm] loop, parse, AllCells, - { if (number[a_loopfield] = swap1) set(a_loopfield, swap2) else if (number[a_loopfield] = swap2) set(a_loopfield, swap1) } history() swap1 := "" swap2 := "" tooltip } } return ;---- set numbers ----------------------------------------------------------------------- 1:: 2:: 3:: 4:: 5:: 6:: 7:: 8:: 9:: numpad1:: numpad2:: numpad3:: numpad4:: numpad5:: numpad6:: numpad7:: numpad8:: numpad9:: if (NextInputWait != 1 and MouseGetCell()) { NextInputWait := 1 n := SubStr(a_thislabel, 0) if not InStr(FixedCells, rm cm) { valid := 1 loop, parse, % ConnectedCells[rm cm], - if (number[a_loopfield] = n) { valid := 0 break } if valid { set(rm cm, n, "default") loop, parse, % ConnectedCells[rm cm], - PencilMark(a_loopfield, n, 0) AutoPencil() LastAction := {action: "number", cell: rm cm, n: n, c: "", time: A_TickCount} history() } else { set(rm cm, n) history() sleep 200 gosub back history.pop() } } NextInputWait := 0 } return ;---- set/delete pencil marks ----------------------------------------------------------- +1:: ; set/delete a single pencil mark +2:: +3:: +4:: +5:: +6:: +7:: +8:: +9:: +numpad1:: +numpad2:: +numpad3:: +numpad4:: +numpad5:: +numpad6:: +numpad7:: +numpad8:: +numpad9:: if (NextInputWait != 1 and MouseGetCell()) { NextInputWait := 1 n := SubStr(a_thislabel, 0) if (number[rm cm] = 0) { if (PencilMark[rm cm n] = 0) { valid := 1 loop, parse, % ConnectedCells[rm cm], - if (number[a_loopfield] = n) { valid := 0 break } if valid { PencilMark(rm cm, n, 1) LastAction := {action: "PencilMark", cell: rm cm, n: n, c: "", time: A_TickCount} history() } else { PencilMark(rm cm, n, 1) sleep 200 PencilMark(rm cm, n, 0) } } else { PencilMark(rm cm, n, 0, "default") history() } } NextInputWait := 0 } return ;---- move the mouse cursor ------------------------------------------------------------- ~left:: ~right:: ~up:: ~down:: key := LTrim(a_thishotkey, "~") if (key = "left") { hor := -1 ver := 0 } else if (key = "right") { hor := 1 ver := 0 } else if (key = "up") { hor := 0 ver := -1 } else if (key = "down") { hor := 0 ver := 1 } if MouseGetCell() { if (cm + hor >= 1 and cm + hor <= 9) cm += hor if (rm + ver >= 1 and rm + ver <= 9) rm += ver } else { if (cm = "") cm := xm<=wBorder ? 1 : 9 if (rm = "") rm := ym<=hTitleMenu ? 1 : 9 } MouseClickCell(rm cm) return ;======================================================================================== ; Mouse buttons ;======================================================================================== ~lbutton:: if MouseGetCell() and not InStr(FixedCells, rm cm) { menu, LeftMouseMenu, deleteall loop, 9 { n := a_index valid := 1 loop, parse, % ConnectedCells[rm cm], - if (number[a_loopfield] = n) { valid := 0 break } if valid { if (display = "colors") item := ColorName[n] else item := n menu, LeftMouseMenu, add, %item%, SetDeleteNumber if (number[rm cm] = n) menu, LeftMouseMenu, check, %item% } } menu, LeftMouseMenu, show } return SetDeleteNumber: if (display = "numbers") n := a_thismenuitem else n := ColorNumber[a_thismenuitem] if (number[rm cm] != n) { set(rm cm, n) loop, parse, % ConnectedCells[rm cm], - PencilMark(a_loopfield, n, 0) AutoPencil() LastAction := {action: "number", cell: rm cm, n: n, c: "", time: A_TickCount} } else { set(rm cm, 0, "default") AutoPencil() } history() return ;---------------------------------------------------------------------------------------- ~rbutton:: if (MouseGetCell() and number[rm cm] = 0) { menu, RightMouseMenu, deleteall loop, 9 { n := a_index valid := 1 loop, parse, % ConnectedCells[rm cm], - if (number[a_loopfield] = n) { valid := 0 break } if valid { if (display = "colors") item := "pencil mark " ColorName[a_index] else item := "pencil mark " a_index menu, RightMouseMenu, add, %item%, SetDeletePencilMark if (PencilMark[rm cm a_index] = 1) menu, RightMouseMenu, check, %item% } } menu, RightMouseMenu, show } return SetDeletePencilMark: if (display = "numbers") n := SubStr(a_thismenuitem, 13) else n := ColorNumber[SubStr(a_thismenuitem, 13)] if (PencilMark[rm cm n] = 0) { PencilMark(rm cm, n, 1) LastAction := {action: "PencilMark", cell: rm cm, n: n, c: "", time: A_TickCount} } else PencilMark(rm cm, n, 0, "default") history() return ;---------------------------------------------------------------------------------------- +lbutton::UserHighlight("red") ^lbutton::UserHighlight("green") !lbutton::UserHighlight("blue") +rbutton::UserHighlightAllOfOne("red") ^rbutton::UserHighlightAllOfOne("green") !rbutton::UserHighlightAllOfOne("blue") UserHighlight(color) { global if MouseGetCell() { if (highlight[rm cm] != color) highlight(rm cm, color) else highlight(rm cm, "") } } UserHighlightAllOfOne(color) { global if (MouseGetCell() and number[rm cm] != 0) { AllHighlighted := 1 loop, parse, AllCells, - if (number[a_loopfield] = number[rm cm] and highlight[a_loopfield] != color) AllHighlighted := 0 if not AllHighlighted { loop, parse, AllCells, - if (number[a_loopfield] = number[rm cm]) highlight(a_loopfield, color) else if (highlight[a_loopfield] = color) highlight(a_loopfield, "") } else { loop, parse, AllCells, - if (highlight[a_loopfield] = color) highlight(a_loopfield, "") } } } ;======================================================================================== ; Main solving subroutines ;======================================================================================== fill(FillWith) { local rc, n %FillWith% := {} bifurcation := [] if (FillWith = "minimum") { reset := {} loop, parse, AllCells, - reset[a_loopfield] := number[a_loopfield] } else if (FillWith = "maximum") { loop, parse, AllCells, - set(a_loopfield, reset[a_loopfield]) } loop { GetPossibleNumbers() if (next["action"] = "solved") { loop, parse, AllCells, - %FillWith%[a_loopfield] := number[a_loopfield] AnySolution := 1 return } if (next["action"] = "no possible number" or next["action"] = "no possible cell") { if (bifurcation.length() = 0) { AnySolution := 0 return } LastBifurcation := bifurcation.pop() loop, parse, AllCells, - set(a_loopfield, SubStr(LastBifurcation["string"], a_index, 1)) rc := LastBifurcation["rc"] if (FillWith = "minimum") n := SubStr(LastBifurcation["PossibleNumbers"], 1, 1) else if (FillWith = "maximum") n := SubStr(LastBifurcation["PossibleNumbers"], 0) else if (FillWith = "random") { n_random := "" loop, parse, % LastBifurcation["PossibleNumbers"] n_random .= a_loopfield "-" sort, n_random, random d- n := SubStr(n_random, 1, 1) } set(rc, n) if (StrReplace(LastBifurcation["PossibleNumbers"], n) != "") bifurcation.push({string: LastBifurcation["string"], rc: LastBifurcation["rc"], PossibleNumbers: StrReplace(LastBifurcation["PossibleNumbers"], n)}) } else if (next["action"] = "one possible number" or next["action"] = "one possible cell") { rc := next["rc"] n := next["n"] set(rc, n) } else if (next["action"] = "several possible numbers") { rc := next["rc"] if (FillWith = "minimum") n := SubStr(next["n"], 1, 1) else if (FillWith = "maximum") n := SubStr(next["n"], 0) else if (FillWith = "random") { n_random := "" loop, parse, % next["n"] n_random .= a_loopfield "-" sort, n_random, random d- n := SubStr(n_random, 1, 1) } set(rc, n) string := "" loop, parse, AllCells, - string .= number[a_loopfield] bifurcation.push({string: string, rc: rc, PossibleNumbers: StrReplace(next["n"], n)}) } if (FillWith = "minimum") { if (context = "EasyAndSymmetrical" and bifurcation.length() = 0 and number[sym1] != 0 and number[sym2] != 0 and number[sym3] != 0 and number[sym4] != 0 and number[sym5] != 0 and number[sym6] != 0 and number[sym7] != 0 and number[sym8] != 0) return if (context = "DifficultButNotSymmetrical" and bifurcation.length() = 0 and number[NextOmit] != 0) return } } } ;---------------------------------------------------------------------------------------- GetAllPossibleNumbers() { local rc AllPossibleNumbers := {} loop, parse, AllCells, - if (number[a_loopfield] = 0) { rc := a_loopfield AllPossibleNumbers[rc] := "123456789" loop, parse, % ConnectedCells[rc], - if (number[a_loopfield] != 0) AllPossibleNumbers[rc] := StrReplace(AllPossibleNumbers[rc], number[a_loopfield]) } } ;---------------------------------------------------------------------------------------- GetPossibleCells(all := "") { local u, n %all%PossibleCells := {} loop, 27 { u := a_index AlreadySet := "" loop, parse, % UnitCells[u], - if (number[a_loopfield] != 0) AlreadySet .= number[a_loopfield] loop, 9 if not InStr(AlreadySet, a_index) { n := a_index %all%PossibleCells[u n] := [] loop, parse, % UnitCells[u], - if (number[a_loopfield] = 0 and InStr(%all%PossibleNumbers[a_loopfield], n)) %all%PossibleCells[u n].push(a_loopfield) } } } ;---------------------------------------------------------------------------------------- GetPossibleNumbers(caller := "") { local rc, main_index ; CONTENTS: ; get possible numbers for every cell ; get possible cells for every number in every unit ; possible numbers and possible cells are step by step eliminated by the following loops and solving techniques: ; loop 4 ; { ; - two cells, two possible numbers ; - two numbers, two possible cells ; - three cells, three possible numbers ; - three possible numbers, three cells ; - two or three possible cells, in the same row or column AND block ; - X-wing ; - Empty rectangle ; - Y-wing chains ; - Swordfish/Jellyfish ; } solved := 1 loop, parse, AllCells, - if (number[a_loopfield] = 0) { solved := 0 break } if solved { next := {action: "solved"} return } explanation := {} ; explanation[rc n] will be a text string, explaining why n can't be set in cell rc. GetAllPossibleNumbers() CopyArray(AllPossibleNumbers, "PossibleNumbers", 1) if (context = "CreateImage") loop, parse, PencilImage, - if (number[a_loopfield] = 0) loop, 9 if not InStr(PencilledNumbers[a_loopfield], a_index) PossibleNumbers[a_loopfield] := StrReplace(PossibleNumbers[a_loopfield], a_index) loop, parse, AllCells, - if (number[a_loopfield] = 0) { if (StrLen(PossibleNumbers[a_loopfield]) = 0) { next := {rc: a_loopfield, action: "no possible number"} return } else if (StrLen(PossibleNumbers[a_loopfield]) = 1) { next := {rc: a_loopfield, n: PossibleNumbers[a_loopfield], action: "one possible number"} return } } GetPossibleCells() for un, cells in PossibleCells { u := SubStr(un, 1, -1) n := SubStr(un, 0) if (PossibleCells[u n].length() = 0) { next := {u: u, n: n, action: "no possible cell"} return } else if (PossibleCells[u n].length() = 1) { rc := PossibleCells[u n][1] next := {u: u, rc: rc, n: n, action: "one possible cell"} return } } EmptyCells := {} loop, 27 { u := a_index EmptyCells[u] := 0 loop, parse, % UnitCells[u], - if (number[a_loopfield] = 0) EmptyCells[u] += 1 } loop, 9 { main_index := a_index if (caller = "SetOne" and main_index = 2) gosub PleaseWait CellsWith2PossibleNumbers := "" loop, parse, AllCells, - if (number[a_loopfield] = 0 and StrLen(PossibleNumbers[a_loopfield]) = 2) CellsWith2PossibleNumbers .= a_loopfield "-" CellsWith2PossibleNumbers := RTrim(CellsWith2PossibleNumbers, "-") ;---- two cells with the same two possible numbers ---- loop, parse, CellsWith2PossibleNumbers, - { cell1 := a_loopfield i := a_index loop, parse, CellsWith2PossibleNumbers, - if (a_index > i and InStr(ConnectedCells[cell1], a_loopfield)) { cell2 := a_loopfield if (PossibleNumbers[cell1] = PossibleNumbers[cell2]) { n1 := SubStr(PossibleNumbers[cell1], 1, 1) n2 := SubStr(PossibleNumbers[cell1], 2, 1) loop, parse, % ConnectedCells[cell1], - if (InStr(ConnectedCells[cell2], a_loopfield) and number[a_loopfield] = 0) for i, n in [n1, n2] if InStr(PossibleNumbers[a_loopfield], n) { rc := a_loopfield if (caller = "SetOne") { explanation[rc n] := n " can't be in " rc " because " . n1 " and " n2 " are the only possible numbers in " . cell1 " and " cell2 " , so they must be there.$" concat := "" loop, parse, % cell1 "-" cell2, - loop, 9 if (a_index != n1 and a_index != n2) ; no other possible numbers ExplConcat(explanation[a_loopfield a_index]) if (concat != "") explanation[rc n] := concat explanation[rc n] } if (eliminate(rc, n) = "one left") return } break } } } ;---- two numbers with the same two possible cells ---- for un1, cells1 in PossibleCells { u1 := SubStr(un1, 1, -1) n1 := SubStr(un1, 0) if (PossibleCells[u1 n1].length() = 2 and EmptyCells[u1] > 2) for un2, cells2 in PossibleCells { u2 := SubStr(un2, 1, -1) n2 := SubStr(un2, 0) if (n2 != n1 and PossibleCells[u2 n2].length() = 2 and EmptyCells[u2] > 2 and PossibleCells[u2 n2][1] = PossibleCells[u1 n1][1] and PossibleCells[u2 n2][2] = PossibleCells[u1 n1][2]) { for i, cell in PossibleCells[u1 n1] loop, parse, % PossibleNumbers[cell] if (a_loopfield != n1 and a_loopfield != n2) { n3 := a_loopfield if (caller = "SetOne") { rc1 := PossibleCells[u1 n1][1] rc2 := PossibleCells[u1 n1][2] if (u2 = u1) explanation[cell n3] := "In " unit[u1] ", " n1 " and " n2 else explanation[cell n3] := n1 " in " unit[u1] " and " n2 " in " unit[u2] explanation[cell n3] .= " can only be in " rc1 " and " rc2 " , so they will be there, and other numbers can't be there.$" concat := "" loop, parse, % UnitCells[u1], - if (a_loopfield != rc1 and a_loopfield != rc2) ExplConcat(explanation[a_loopfield n1]) loop, parse, % UnitCells[u2], - if (a_loopfield != rc1 and a_loopfield != rc2) ExplConcat(explanation[a_loopfield n2]) if (concat != "") explanation[cell n3] := concat explanation[cell n3] } if (eliminate(cell, n3) = "one left") return } break } } } ;---- three cells with together three possible numbers ---- if (caller = "SetOne") loop, 27 { u := a_index loop, parse, % UnitCells[u], - if (number[a_loopfield] = 0 and StrLen(PossibleNumbers[a_loopfield]) <= 3) { cell1 := a_loopfield i := a_index loop, parse, % UnitCells[u], - if (a_index > i and number[a_loopfield] = 0 and PossibleNumbersLen(cell1, a_loopfield) <= 3) { cell2 := a_loopfield i := a_index loop, parse, % UnitCells[u], - if (a_index > i and number[a_loopfield] = 0 and PossibleNumbersLen(cell1, cell2, a_loopfield) = 3) { cell3 := a_loopfield loop, parse, % UnitCells[u], - if (a_loopfield != cell1 and a_loopfield != cell2 and a_loopfield != cell3 and number[a_loopfield] = 0) for i, n in [n1, n2, n3] if InStr(PossibleNumbers[a_loopfield], n) { rc := a_loopfield if (caller = "SetOne") { explanation[rc n] := n " can't be in " rc " because " . n1 " and " n2 " and " n3 " are the only possible numbers in " . cell1 " and " cell2 " and " cell3 " " . ", so they must be there.$" concat := "" loop, parse, % cell1 "-" cell2 "-" cell3, - loop, 9 if (a_index != n1 and a_index != n2 and a_index != n3) ; no other possible numbers ExplConcat(explanation[a_loopfield a_index]) if (concat != "") explanation[rc n] := concat explanation[rc n] } if (eliminate(rc, n) = "one left") return } } } } } ;---- three numbers with together three possible cells ---- if (caller = "SetOne" and main_index > 1) for un, cells1 in PossibleCells { u := SubStr(un, 1, -1) n1 := SubStr(un, 0) if (cells1.length() <= 3 and EmptyCells[u] > 3) loop, 9 { n2 := a_index if (n2 != n1 and (PossibleCells[u n2].length() = 2 or PossibleCells[u n2].length() = 3)) { cells2 := ArrayAdd(cells1, PossibleCells[u n2]) if (cells2.length() = 3) loop, 9 { n3 := a_index if (n3 != n1 and n3 != n2 and (PossibleCells[u n3].length() = 2 or PossibleCells[u n3].length() = 3)) { cells3 := ArrayAdd(cells2, PossibleCells[u n3]) if (cells3.length() = 3) for i, cell in cells3 loop, parse, % PossibleNumbers[cell] if (a_loopfield != n1 and a_loopfield != n2 and a_loopfield != n3) { n4 := a_loopfield if (caller = "SetOne") { CellsString := "" for i2, cell2 in cells3 CellsString .= cell2 "-" sort, CellsString, d- rc1 := SubStr(CellsString, 1, 2) rc2 := SubStr(CellsString, 4, 2) rc3 := SubStr(CellsString, 7, 2) explanation[cell n4] := "In " unit[u] ", " n1 " and " n2 " and " n3 " must be in " rc1 " and " rc2 . " and " rc3 " . That's three cells for three numbers, so there can't be other numbers in these cells.$" concat := "" loop, parse, % UnitCells[u], - if (not InValues(cells3, a_loopfield) and number[a_loopfield] = 0) ; no other possible cells loop, 3 ExplConcat(explanation[a_loopfield n%a_index%]) if (concat != "") explanation[cell n4] := concat explanation[cell n4] } if (eliminate(cell, n4) = "one left") return } } } } } } ;---- one number, two or three possible cells, all in the same row or column AND block ---- if (caller = "SetOne") for un, cells in PossibleCells { u := SubStr(un, 1, -1) n := SubStr(un, 0) if (PossibleCells[u n].length() = 2 or PossibleCells[u n].length() = 3) loop, 27 if (a_index != u and InStr(UnitCells[a_index], PossibleCells[u n][1]) and InStr(UnitCells[a_index], PossibleCells[u n][2]) and (PossibleCells[u n].length() = 2 or InStr(UnitCells[a_index], PossibleCells[u n][3]))) { u2 := a_index loop, parse, % UnitCells[u2], - if (not InStr(UnitCells[u], a_loopfield) and number[a_loopfield] = 0 and InStr(PossibleNumbers[a_loopfield], n)) { rc := a_loopfield if (caller = "SetOne") { text := n " can't be in " rc " because in " unit[u] " it must be in " loop, parse, % UnitCells[u], - if (InStr(UnitCells[u2], a_loopfield) and InStr(AllPossibleNumbers[a_loopfield], n)) text .= a_loopfield " or " text := SubStr(text, 1, -4) explanation[rc n] := text ".$" concat := "" loop, parse, % UnitCells[u], - if not InStr(UnitCells[u2], a_loopfield) ExplConcat(explanation[a_loopfield n]) if (concat != "") explanation[rc n] := concat explanation[rc n] } if (eliminate(rc, n) = "one left") return } break } } ;---- X-wing ---- for un, cells in PossibleCells { u := SubStr(un, 1, -1) n := SubStr(un, 0) if (PossibleCells[u n].length() = 2) { A := PossibleCells[u n][1] B := PossibleCells[u n][2] loop, % u-1 { u2 := a_index C := PossibleCells[u2 n][1] D := PossibleCells[u2 n][2] if (PossibleCells[u2 n].length() = 2 and A != C and A != D and B != C and B != D) { for i, pattern in [A B C D, B A C D, A B D C, B A D C] { uCell1 := SubStr(pattern, 1, 2) uCell2 := SubStr(pattern, 3, 2) u2Cell1 := SubStr(pattern, 5, 2) u2Cell2 := SubStr(pattern, 7) if (InStr(ConnectedCells[uCell1], u2Cell1) and not InStr(ConnectedCells[uCell2], u2Cell1) and not InStr(ConnectedCells[u2Cell2], uCell1)) { loop, parse, % ConnectedCells[uCell2], - if (InStr(ConnectedCells[u2Cell2], a_loopfield) and number[a_loopfield] = 0 and InStr(PossibleNumbers[a_loopfield], n)) { rc := a_loopfield if (caller = "SetOne") { explanation[rc n] := n . " can't be in " rc " because of the ""X-wing"" " u2Cell1 " - " u2Cell2 "/" uCell1 " - " uCell2 ":" . "`n- If in " unit[u2] " " n " is in " u2Cell1 " , then in " unit[u] " it must be in " uCell2 " ." . "`n- If in " unit[u] " " n " is in " uCell1 " , then in " unit[u2] " it must be in " u2Cell2 " ." . "`nEither way " n " can't be in " rc " because " rc " is connected with both " uCell2 " and " u2Cell2 ".$" concat := "" loop, parse, % UnitCells[u], - if (a_loopfield != PossibleCells[u n][1] and a_loopfield != PossibleCells[u n][2]) ExplConcat(explanation[a_loopfield n]) loop, parse, % UnitCells[u2], - if (a_loopfield != PossibleCells[u2 n][1] and a_loopfield != PossibleCells[u2 n][2]) ExplConcat(explanation[a_loopfield n]) if (concat != "") explanation[rc n] := concat explanation[rc n] } if (eliminate(rc, n) = "one left") return } } } } } } } ;---- Empty rectangle ---- if (caller = "SetOne") for un, cells in PossibleCells { u := SubStr(un, 1, -1) n := SubStr(un, 0) if (u > 18 and PossibleCells[u n].length() >= 3 and PossibleCells[u n].length() <= 5) for i1, road1 in UnitRows[u] for i2, road2 in UnitColumns[u] if (i2 > i1) { crossroads := UnitCells[road1] "-" UnitCells[road2] AllInCrossroads := 1 for i, cell in PossibleCells[u n] if not InStr(crossroads, cell) { AllInCrossroads := 0 break } if AllInCrossroads { InRoad1 := 0 InRoad2 := 0 for i, cell in PossibleCells[u n] { if InStr(UnitCells[road1], cell) InRoad1 += 1 if InStr(UnitCells[road2], cell) InRoad2 += 1 } if (InRoad1 >= 2 and InRoad2 >= 2) { loop, 2 { i_1 := a_index i_2 := i_1=1 ? 2 : 1 AllEliminations := [] QueuedEliminations := [] IfThen := {} IfThenElement := [] IfThenBefore := {} IfThenBeforeElement := [] ExplBefore := {} ExplBeforeElement := [] CopyArray(PossibleNumbers, "CopyPossibleNumbers", 1) CopyArray(PossibleCells, "CopyPossibleCells", 2) loop, parse, % UnitCells[road%i_1%] "-" UnitCells[road%i_2%], - if (InStr(UnitCells[road%i_1%], a_loopfield) and not InStr(UnitCells[u], a_loopfield) or InStr(UnitCells[road%i_2%], a_loopfield) and InStr(UnitCells[u], a_loopfield) and not InStr(UnitCells[road%i_1%], a_loopfield)) if number[a_loopfield] = 0 and InStr(PossibleNumbers[a_loopfield], n) AllEliminations[1] .= "-" a_loopfield n AllEliminations[1] := LTrim(AllEliminations[1], "-") QueuedEliminations[1] := AllEliminations[1] loop, parse, % AllEliminations[1], - { rc := SubStr(a_loopfield, 1, 2) eliminate(rc, n, 0) IfThenElement[1] := "If " n " is in " unit[u] " and " unit[road%i_1%] ", then " rc " can't be " n IfThen[rc n] := IfThenElement[1] ".`n" IfThenBeforeElement[1] := "" ExplBeforeElement[1] := "" ContinueEliminating(rc, n, 2) } AllEliminations%i_1% := "" loop, 9 if (AllEliminations[a_index] != "") AllEliminations%i_1% .= AllEliminations[a_index] AllEliminations%i_1% := LTrim(AllEliminations%i_1%, "-") CopyArray(IfThen, "IfThen" i_1, 1) CopyArray(IfThenBefore, "IfThenBefore" i_1, 1) CopyArray(ExplBefore, "ExplBefore" i_1, 1) CopyArray(CopyPossibleNumbers, "PossibleNumbers", 1) CopyArray(CopyPossibleCells, "PossibleCells", 2) } TextCrossRoads := "" for i, cell in PossibleCells[u n] TextCrossRoads .= " or " cell " " TextCrossRoads := SubStr(TextCrossRoads, 5) ; Concatenate TextCrossRoads before loop, parse, AllEliminations1. ; Loop, parse, AllEliminations1 might eliminate cells in PossibleCells[u n]. loop, parse, AllEliminations1, - if InStr(AllEliminations2, a_loopfield) { EliminateWhat := SubStr(a_loopfield, 3, 1) EliminateIn := SubStr(a_loopfield, 1, 2) if (caller = "SetOne") { explanation[EliminateIn EliminateWhat] := EliminateWhat " can't be in " EliminateIn " because of the ""Empty rectangle"" in " unit[u] ": " . "In " unit[u] ", " n " must be in " TextCrossRoads ", that is, in " unit[road1] " or " unit[road2] ".`n" loop, 2 explanation[EliminateIn EliminateWhat] .= IfThenBefore%a_index%[EliminateIn EliminateWhat] IfThen%a_index%[EliminateIn EliminateWhat] MergeIfThen(6) ; MergeIfThen(6) still allows MergeIfThen(4) etc. MergeIfThen(4) MergeIfThen(2) explanation[EliminateIn EliminateWhat] .= "$" concat := "" loop, parse, % UnitCells[u], - if (number[a_loopfield] = 0 and not InStr(TextCrossRoads, a_loopfield)) ; no other possible cells ExplConcat(explanation[a_loopfield n]) explanation[EliminateIn EliminateWhat] := concat ExplBefore1[EliminateIn EliminateWhat] ExplBefore2[EliminateIn EliminateWhat] explanation[EliminateIn EliminateWhat] } if (eliminate(EliminateIn, EliminateWhat) = "one left") return } } } } } ;---- Y-wing chains ---- if (caller = "SetOne" and main_index > 2) loop, parse, CellsWith2PossibleNumbers, - { rc := a_loopfield n1 := SubStr(PossibleNumbers[rc], 1, 1) n2 := SubStr(PossibleNumbers[rc], 2, 1) loop, 2 { i_1 := a_index AllEliminations := [] QueuedEliminations := [] IfThen := {} IfThenElement := [] IfThenBefore := {} IfThenBeforeElement := [] ExplBefore := {} ExplBeforeElement := [] CopyArray(PossibleNumbers, "CopyPossibleNumbers", 1) CopyArray(PossibleCells, "CopyPossibleCells", 2) loop, parse, % ConnectedCells[rc], - if number[a_loopfield] = 0 and InStr(PossibleNumbers[a_loopfield], n%i_1%) AllEliminations[1] .= "-" a_loopfield n%i_1% AllEliminations[1] := LTrim(AllEliminations[1], "-") QueuedEliminations[1] := AllEliminations[1] loop, parse, % AllEliminations[1], - { rc2 := SubStr(a_loopfield, 1, 2) eliminate(rc2, n%i_1%, 0) IfThenElement[1] := "If " rc " is " n%i_1% ", then " rc2 " can't be " n%i_1% IfThen[rc2 n%i_1%] := IfThenElement[1] ".`n" IfThenBeforeElement[1] := "" ExplBeforeElement[1] := "" ContinueEliminating(rc2, n%i_1%, 2) } AllEliminations%i_1% := "" loop, 9 if (AllEliminations[a_index] != "") AllEliminations%i_1% .= AllEliminations[a_index] AllEliminations%i_1% := LTrim(AllEliminations%i_1%, "-") CopyArray(IfThen, "IfThen" i_1, 1) CopyArray(IfThenBefore, "IfThenBefore" i_1, 1) CopyArray(ExplBefore, "ExplBefore" i_1, 1) CopyArray(CopyPossibleNumbers, "PossibleNumbers", 1) CopyArray(CopyPossibleCells, "PossibleCells", 2) } loop, parse, AllEliminations1, - if InStr(AllEliminations2, a_loopfield) { EliminateWhat := SubStr(a_loopfield, 3, 1) EliminateIn := SubStr(a_loopfield, 1, 2) if (caller = "SetOne") { explanation[EliminateIn EliminateWhat] := EliminateWhat " can't be in " EliminateIn " " . "because of the Y-wing chains starting with " n1 " and " n2 " in " rc " :`n" loop, 2 explanation[EliminateIn EliminateWhat] .= IfThenBefore%a_index%[EliminateIn EliminateWhat] IfThen%a_index%[EliminateIn EliminateWhat] MergeIfThen(6) MergeIfThen(4) MergeIfThen(2) explanation[EliminateIn EliminateWhat] .= "$" concat := "" loop, 9 if (a_index != n1 and a_index != n2) ; no other possible numbers in rc ExplConcat(explanation[rc a_index]) explanation[EliminateIn EliminateWhat] := concat ExplBefore1[EliminateIn EliminateWhat] ExplBefore2[EliminateIn EliminateWhat] explanation[EliminateIn EliminateWhat] } if (eliminate(EliminateIn, EliminateWhat) = "one left") return } } ;---- Swordfish/Jellyfish ---- if (caller = "SetOne") loop, 9 { n := a_index for i, A in ["A", "B"] { B := A="A" ? "B" : "A" line%A% := "row" pos%A% := 1 plus%A% := 0 letter%A% := [1,2,3,4,5,6,7,8,9] line%B% := "column" pos%B% := 2 plus%B% := 9 letter%B% := letter %lineA% := [] %lineA%[1] := "" %lineB% := [] %lineB%[1] := "" if fishnet(2) return } } if (main_index > 3) { change := 0 for rc, n in PossibleNumbers if (PossibleNumbers[rc] != vorher[rc]) { change := 1 break } if not change break } if (main_index > 2) CopyArray(PossibleNumbers, "vorher", 1) } min := 10 for rc, numbers in AllPossibleNumbers { if (StrLen(numbers) = 2) { next := {rc: rc, n: numbers, action: "several possible numbers"} break } else if (StrLen(numbers) < min) { next := {rc: rc, n: numbers, action: "several possible numbers"} min := StrLen(numbers) } } } ;---------------------------------------------------------------------------------------- ContinueEliminating(rc, n, index) { local i, i2, u, u2, rc2, n1, n2, n_i, un, cell, cells ; ---- one possible number ---- if (StrLen(PossibleNumbers[rc]) = 1) { n2 := PossibleNumbers[rc] NextElements(index) IfThenElement[index] .= ", " rc " must be " n2 loop, 9 if (a_index != n and a_index != n2) ; no other possible numbers ExplCollect(index, rc, a_index) ; ---- eliminate n2 in connected cells ---- NewEliminations := "" loop, parse, % ConnectedCells[rc], - if (not InStr(IfThenElement[index], a_loopfield) and not InStr(QueuedEliminations[index-1], a_loopfield n2) and number[a_loopfield] = 0 and InStr(PossibleNumbers[a_loopfield], n2)) { NewEliminations .= "-" a_loopfield AllEliminations[index] .= "-" a_loopfield n2 QueuedEliminations[index] .= "-" a_loopfield n2 } NewEliminations := LTrim(NewEliminations, "-") loop, parse, NewEliminations, - { eliminate(a_loopfield, n2, 0) NextElements(index+1) IfThenElement[index+1] .= ", " a_loopfield " can't be " n2 IfThen[a_loopfield n2] := IfThenElement[index+1] ".`n" IfThenBefore[a_loopfield n2] := IfThenBeforeElement[index+1] ExplBefore[a_loopfield n2] := ExplBeforeElement[index+1] if (index = 2) ContinueEliminating(a_loopfield, n2, index+2) } } ; ---- one possible cell ---- for i, u in [SubStr(rc, 1, 1), SubStr(rc, 2, 1)+9, block[rc]] if (PossibleCells[u n].length() = 1 and not InStr(IfThenElement[index-1], PossibleCells[u n][1])) { rc2 := PossibleCells[u n][1] NextElements(index) IfThenElement[index] .= ", " rc2 " is the only possible cell for " n " in " unit[u] loop, parse, % UnitCells[u], - if (a_loopfield != rc and a_loopfield != rc2 and number[a_loopfield] = 0) ; no other possible cells ExplCollect(index, a_loopfield, n) ; ---- eliminate n in connected cells ---- NewEliminations := "" loop, parse, % ConnectedCells[rc2], - if (not InStr(UnitCells[u], a_loopfield) and not InStr(IfThenElement[index], a_loopfield) and not InStr(QueuedEliminations[index-1], a_loopfield n) and number[a_loopfield] = 0 and InStr(PossibleNumbers[a_loopfield], n)) { NewEliminations .= "-" a_loopfield AllEliminations[index] .= "-" a_loopfield n QueuedEliminations[index] .= "-" a_loopfield n } NewEliminations := LTrim(NewEliminations, "-") loop, parse, NewEliminations, - { eliminate(a_loopfield, n, 0) NextElements(index+1) IfThenElement[index+1] .= ", " a_loopfield " can't be " n IfThen[a_loopfield n] := IfThenElement[index+1] ".`n" IfThenBefore[a_loopfield n] := IfThenBeforeElement[index+1] ExplBefore[a_loopfield n] := ExplBeforeElement[index+1] if (index = 2) ContinueEliminating(a_loopfield, n, index+2) } } ; ---- two possible numbers in rc ---- if (StrLen(PossibleNumbers[rc]) = 2) { n1 := SubStr(PossibleNumbers[rc], 1, 1) n2 := SubStr(PossibleNumbers[rc], 2, 1) NextElements(index) IfThenElement[index] .= ", " n1 " and " n2 " are the only possible numbers in " rc " " loop, 9 if (a_index != n and a_index != n1 and a_index != n2) ; no other possible numbers ExplCollect(index, rc, a_index) ; ---- same two possible numbers in rc2 ---- loop, parse, % ConnectedCells[rc], - if (not InStr(IfThenElement[index], a_loopfield) and number[a_loopfield] = 0 and PossibleNumbers[a_loopfield] = PossibleNumbers[rc]) { rc2 := a_loopfield loop, 9 if (a_index != n1 and a_index != n2) ; no other possible numbers ExplCollect(index, rc2, a_index) ; ---- eliminate both numbers in connected cells ---- NewEliminations1 := "" NewEliminations2 := "" loop, parse, % ConnectedCells[rc], - if (InStr(ConnectedCells[rc2], a_loopfield) and not InStr(IfThenElement[index], a_loopfield) and number[a_loopfield] = 0 and InStr(PossibleNumbers[a_loopfield], n1) and InStr(PossibleNumbers[a_loopfield], n2) and not InStr(QueuedEliminations[index-1], a_loopfield n1) and not InStr(QueuedEliminations[index-1], a_loopfield n2)) { NewEliminations1 .= "-" a_loopfield NewEliminations2 .= "-" a_loopfield AllEliminations[index] .= "-" a_loopfield n1 "-" a_loopfield n2 QueuedEliminations[index] .= "-" a_loopfield n1 "-" a_loopfield n2 } NewEliminations1 := LTrim(NewEliminations1, "-") NewEliminations2 := LTrim(NewEliminations2, "-") for i, n_i in [n1, n2] loop, parse, NewEliminations%i%, - { eliminate(a_loopfield, n_i, 0) NextElements(index+1) IfThenElement[index+1] .= " and " rc2 " , " a_loopfield " can't be " n_i IfThen[a_loopfield n_i] := IfThenElement[index+1] ".`n" IfThenBefore[a_loopfield n_i] := IfThenBeforeElement[index+1] ExplBefore[a_loopfield n_i] := ExplBeforeElement[index+1] if (index = 2) ContinueEliminating(a_loopfield, n_i, index+2) } break } } ; ---- two possible cells for n ---- for i, u in [SubStr(rc, 1, 1), SubStr(rc, 2, 1)+9, block[rc]] if (PossibleCells[u n].length() = 2 and EmptyCells[u] > 2 and not InStr(IfThenElement[index-1], PossibleCells[u n][1]) and not InStr(IfThenElement[index-1], PossibleCells[u n][2])) { NextElements(index) IfThenElement[index] .= ", " PossibleCells[u n][1] " and " PossibleCells[u n][2] " are the only possible cells for " n loop, parse, % UnitCells[u], - if (a_loopfield != rc and a_loopfield != PossibleCells[u n][1] and a_loopfield != PossibleCells[u n][2] and number[a_loopfield] = 0) ; no other possible cells ExplCollect(index, a_loopfield, n) ; ---- same two possible cells for n2 ---- loop, 9 if (a_index != n and PossibleCells[u a_index].length() = 2 and PossibleCells[u a_index][1] = PossibleCells[u n][1] and PossibleCells[u a_index][2] = PossibleCells[u n][2]) { n2 := a_index IfThenElement[index] .= " and " n2 " in " unit[u] loop, parse, % UnitCells[u], - if (a_loopfield != PossibleCells[u n2][1] and a_loopfield != PossibleCells[u n2][2] and number[a_loopfield] = 0) ; no other possible cells ExplCollect(index, a_loopfield, n2) ; ---- eliminate other numbers in the two possible cells ---- for i2, cell in PossibleCells[u n] { NewEliminations := "" loop, parse, % PossibleNumbers[cell] if (a_loopfield != n and a_loopfield != n2 and not InStr(QueuedEliminations[index-1], cell a_loopfield)) { NewEliminations .= "-" a_loopfield AllEliminations[index] .= "-" cell a_loopfield QueuedEliminations[index] .= "-" cell a_loopfield } NewEliminations := LTrim(NewEliminations, "-") loop, parse, NewEliminations, - { n3 := a_loopfield eliminate(cell, n3, 0) NextElements(index+1) IfThenElement[index+1] .= ", " cell " can't be " n3 IfThen[cell n3] := IfThenElement[index+1] ".`n" IfThenBefore[cell n3] := IfThenBeforeElement[index+1] ExplBefore[cell n3] := ExplBeforeElement[index+1] if (index = 2) ContinueEliminating(cell, n3, index+2) } } break } } ; ---- two or three possible cells for n in u ---- for i, u in [SubStr(rc, 1, 1), SubStr(rc, 2, 1)+9, block[rc]] if (PossibleCells[u n].length() = 2 or PossibleCells[u n].length() = 3 and not InStr(IfThenElement[index-1], PossibleCells[u n][1]) and not InStr(IfThenElement[index-1], PossibleCells[u n][2]) and (PossibleCells[u n].length() = 2 or not InStr(IfThenElement[index-1], PossibleCells[u n][3]))) { PossibleCellsText := PossibleCells[u n][1] " and " PossibleCells[u n][2] " " if (PossibleCells[u n].length() = 3) PossibleCellsText .= " and " PossibleCells[u n][3] " " NextElements(index) IfThenElement[index] .= ", that leaves " PossibleCellsText " as possible cells for " n " in " unit[u] loop, parse, % UnitCells[u], - if (not InValues(PossibleCells[u n], a_loopfield) and number[a_loopfield] = 0) ; no other possible cells ExplCollect(index, a_loopfield, n) ; ---- all of them also in u2 ---- loop, 27 if (a_index != u and InStr(UnitCells[a_index], PossibleCells[u n][1]) and InStr(UnitCells[a_index], PossibleCells[u n][2]) and (PossibleCells[u n].length() = 2 or InStr(UnitCells[a_index], PossibleCells[u n][3]))) { u2 := a_index ; ---- eliminate n in other cells in u2 ---- NewEliminations := "" loop, parse, % UnitCells[u2], - if (not InStr(IfThenElement[index], a_loopfield) and not InStr(QueuedEliminations[index-1], a_loopfield n) and number[a_loopfield] = 0 and InStr(PossibleNumbers[a_loopfield], n)) { NewEliminations .= "-" a_loopfield AllEliminations[index] .= "-" a_loopfield n QueuedEliminations[index] .= "-" a_loopfield n } NewEliminations := LTrim(NewEliminations, "-") loop, parse, NewEliminations, - { eliminate(a_loopfield, n, 0) NextElements(index+1) IfThenElement[index+1] .= ", " a_loopfield " can't be " n IfThen[a_loopfield n] := IfThenElement[index+1] ".`n" IfThenBefore[a_loopfield n] := IfThenBeforeElement[index+1] ExplBefore[a_loopfield n] := ExplBeforeElement[index+1] if (index = 2) ContinueEliminating(a_loopfield, n, index+2) } break } } } ;======================================================================================== ; Other subroutines A-Z ;======================================================================================== AllValues(array) ; for arrays with cells as keys { global string := "" loop, parse, AllCells, - string .= array[a_loopfield] "-" return string } ;---------------------------------------------------------------------------------------- ArrangeWindows(win2) { global SysGet, screen, MonitorWorkArea wingetpos, win1left, win1top, win1width, win1height, %WinTitle% ahk_class AutoHotkeyGUI win1right := win1left+win1width win1bottom := win1top+win1height win2width := %win2%width ; Win2GuiSize gets width and height of win2 win2height := %win2%height if (win1right+10+win2width < screenright) { if (win1top+win2height < screenbottom) gui, %win2%:show, % "x" win1right+10 "y" win1top else gui, %win2%:show, % "x" win1right+10 "y" screenbottom-win2height } else if (win1bottom+10+win2height < screenbottom) { if (win1left+win2width < screenright) gui, %win2%:show, % "x" win1left "y" win1bottom+10 else gui, %win2%:show, % "x" screenright-win2width "y" win1bottom+10 } else if (win1left-10-win2width > 0) { if (win1top+win2height < screenbottom) gui, %win2%:show, % "x" win1left-10-win2width "y" win1top else gui, %win2%:show, % "x" win1left-10-win2width "y" screenbottom-win2height } else if (win1top-10-win2height > 0) { if (win1left+win2width < screenright) gui, %win2%:show, % "x" win1left "y" win1top-10-win2height else gui, %win2%:show, % "x" screenright-win2width "y" win1top-10-win2height } else if (win1width+10+win2width < screenright) { gui, 1:show, % "x" screenright-(win1width+10+win2width) if (win1top+win2height < screenbottom) gui, %win2%:show, % "x" screenright-win2width "y" win1top else gui, %win2%:show, % "x" screenright-win2width "y" screenbottom-win2height } else if (win1height+10+win2height < screenbottom) { gui, 1:show, % "y" screenbottom-(win1height+10+win2height) if (win1left+win2width < screenright) gui, %win2%:show, % "x" win1left "y" screenbottom-win2height else gui, %win2%:show, % "x" screenright-win2width "y" screenbottom-win2height } else { ZoomFitWidth := zoom*(screenright-win2width-10-(win1width-9.4*wWhiteSquare))/(9.4*wWhiteSquare) ZoomFitHeight := zoom*(screenbottom-win2height-10-(win1height-9.4*wWhiteSquare))/(9.4*wWhiteSquare) ; 9.4*wWhiteSquare = 9*wWhiteSquare+wCaption, win1width-9.4*wWhiteSquare = grid lines+borders, win1height-9.4*wWhiteSquare = title bar+menu bar+grid lines+borders if (ZoomFitWidth < 0.48 and ZoomFitHeight < 0.48) { gui, 1:hide SizeAndPosition(0.5-zoom) gui, 1:show, y0 w%wGui% h%wGui% gui, %win2%:show, % "y" screenbottom-win2height } else if (ZoomFitWidth > ZoomFitHeight) { gui, 1:hide SizeAndPosition(ZoomFitWidth-zoom) gui, 1:show, x0 w%wGui% h%wGui% if (win1top+win2height < screenbottom) gui, %win2%:show, % "x" wGui+10 "y" win1top else gui, %win2%:show, % "x" wGui+10 "y" screenbottom-win2height } else { gui, 1:hide SizeAndPosition(ZoomFitHeight-zoom) gui, 1:show, y0 w%wGui% h%wGui% if (win1left+win2width < screenright) gui, %win2%:show, % "x" win1left "y" hTitleMenu+wGui+10 else gui, %win2%:show, % "x" screenright-win2width "y" hTitleMenu+wGui+10 } } } ;---------------------------------------------------------------------------------------- ArrayAdd(array1, array2) { local i, element array1_2 := [] loop, 2 for i, element in array%a_index% if not InValues(array1_2, element) array1_2.push(element) return array1_2 } ;---------------------------------------------------------------------------------------- AutoColor(u) { if u <= 9 return "red" else if u <= 18 return "green" else return "blue" } ;---------------------------------------------------------------------------------------- AutoPencil() { local rc, u, n, un, cells if not AutoPencil return GetAllPossibleNumbers() GetPossibleCells("All") ;---- reset PencilMark and cPencilMark ---- loop, parse, AllCells, - if (number[a_loopfield] = 0) loop, 9 { PencilMark[a_loopfield a_index] := 0 cPencilMark[a_loopfield a_index] := "default" } ;---- get singles/pairs and set cPencilMark ---- SinglesOrPairs := {} for un, cells in AllPossibleCells if (cells.length() = 1) { u := SubStr(un, 1, -1) n := SubStr(un, 0) if not InStr(SinglesOrPairs[cells[1]], n) ; Red overwrites cDefault, green doesn't overwrite red, blue doesn't overwrite green. { SinglesOrPairs[cells[1]] .= n cPencilMark[cells[1] n] := AutoColor(u) } } for rc, n in AllPossibleNumbers if (StrLen(n) = 1) if not InStr(SinglesOrPairs[rc], n) SinglesOrPairs[rc] .= n if (SinglesOrPairs.length() = 0) { for un, cells in AllPossibleCells if (cells.length() = 2) { u := SubStr(un, 1, -1) n := SubStr(un, 0) loop, 2 if not InStr(SinglesOrPairs[cells[a_index]], n) { SinglesOrPairs[cells[a_index]] .= n cPencilMark[cells[a_index] n] := AutoColor(u) } } for rc, n in AllPossibleNumbers if (StrLen(n) = 2) loop, parse, n if not InStr(SinglesOrPairs[rc], a_loopfield) SinglesOrPairs[rc] .= a_loopfield } ;---- set PencilMark ---- for rc, n in %AutoPencil% ; AutoPencil = "AllPossibleNumbers" or "SinglesOrPairs" loop, parse, n PencilMark[rc a_loopfield] := 1 ;---- apply PencilMark and cPencilMark ---- loop, parse, AllCells, - if (number[a_loopfield] = 0) loop, 9 PencilMark(a_loopfield, a_index, PencilMark[a_loopfield a_index], cPencilMark[a_loopfield a_index]) } ;---------------------------------------------------------------------------------------- background(color) { global color := hex(color) if (cBackground != color) { cBackground := color cBackgroundFor%display% := color gui, 1:color, %cBackground% color := SubStr(color, 3) ; omit "0x" loop, % 6-StrLen(color) ; add leading zeros color := "0" . color red := "0x" . SubStr(color, 1, 2) ; split into red, green, blue green := "0x" . SubStr(color, 3, 2) blue := "0x" . SubStr(color, 5, 2) if (floor(sqrt(2*red**2+6*green**2+blue**2)) > 1.3*floor(sqrt(2*(255-red)**2+6*(255-green)**2+(255-blue)**2))) { cDefault := "black" cFixed := "0x007000" } else { cDefault := "0xDFDFDF" cFixed := "0xFFE070" } captions(cDefault) } } ;---------------------------------------------------------------------------------------- captions(color) { global loop, parse, caption123, - font("caption123" a_loopfield, color, sCaption, "Ubuntu", "Arial") loop, parse, captionABC, - font("captionABC" a_loopfield, color, sCaption, "Ubuntu", "Arial") } ;---------------------------------------------------------------------------------------- color(c) { local i, p MouseGetCell() if (LastAction["action"] = "number" and A_TickCount - LastAction["time"] < 3000) { set(LastAction["cell"], LastAction["n"], c) history() } else if (LastAction["action"] = "PencilMark" and A_TickCount - LastAction["time"] < 3000) { PencilMark(LastAction["cell"], LastAction["n"], 1, c) history() } else if (LastAction["action"] = "ColorPencilMark" and LastAction["c"] = c and A_TickCount - LastAction["time"] < 1000) { OtherPencilMarks := [] loop, 9 if (PencilMark[LastAction["cell"] a_index] = 1 and cPencilMark[LastAction["cell"] a_index] != c) OtherPencilMarks.push(a_index) for i, p in OtherPencilMarks if (p > LastAction["n"]) { KeepLastAction := 1 gosub back PencilMark(LastAction["cell"], p, 1, c) KeepLastAction := 0 LastAction["n"] := p LastAction["time"] := A_TickCount history() break } else if (i = OtherPencilMarks.length()) { KeepLastAction := 1 gosub back PencilMark(LastAction["cell"], OtherPencilMarks[1], 1, c) KeepLastAction := 0 LastAction["n"] := OtherPencilMarks[1] LastAction["time"] := A_TickCount history() } } else if (number[rm cm] != 0) { set(rm cm, number[rm cm], c) history() } else loop, 9 if (PencilMark[rm cm a_index] = 1 and cPencilMark[rm cm a_index] != c) { PencilMark(rm cm, a_index, 1, c) LastAction := {action: "ColorPencilMark", cell: rm cm, n: a_index, c: c, time: A_TickCount} history() break } } ;---------------------------------------------------------------------------------------- CopyArray(array, CopyName, dimension) { local key, index, element1, element2, array2 %CopyName% := {} for key, element1 in array { if (dimension = 1) %CopyName%[key] := element1 else if (dimension = 2) { array2 := [] for index, element2 in element1 array2.push(element2) %CopyName%[key] := array2 } } } ;---------------------------------------------------------------------------------------- CreateImageSave: if not FileExist(a_desktop "\Sudoku\Create image") FileCreateDir, %a_desktop%\Sudoku\Create image if FileExist(a_desktop "\Sudoku\Create image\PencilImage.txt") filedelete, %a_desktop%\Sudoku\Create image\PencilImage.txt fileappend, % history[HistoryIndex], %a_desktop%\Sudoku\Create image\PencilImage.txt AllImagesString := "" for i, image in AllImages AllImagesString .= image "-" AllImagesString := RTrim(AllImagesString, "-") if FileExist(a_desktop "\Sudoku\Create image\AllImages.txt") filedelete, %a_desktop%\Sudoku\Create image\AllImages.txt fileappend, %AllImagesString%, %a_desktop%\Sudoku\Create image\AllImages.txt loop, 60 if (a_index >= 4) { SortedImagesString := "" for i, image in SortedImages[a_index] SortedImagesString .= image "-" SortedImagesString := RTrim(SortedImagesString, "-") if FileExist(a_desktop "\Sudoku\Create image\SortedImages" a_index ".txt") filedelete, %a_desktop%\Sudoku\Create image\SortedImages%a_index%.txt fileappend, %SortedImagesString%, %a_desktop%\Sudoku\Create image\SortedImages%a_index%.txt } return ;---------------------------------------------------------------------------------------- eliminate(rc, n, checkout := 1) { local i1, i2, u, cell PossibleNumbers[rc] := StrReplace(PossibleNumbers[rc], n) if (checkout and StrLen(PossibleNumbers[rc]) = 1) { next := {rc: rc, n: PossibleNumbers[rc], action: "one possible number"} return "one left" } for i1, u in [SubStr(rc, 1, 1), SubStr(rc, 2, 1)+9, block[rc]] for i2, cell in PossibleCells[u n] if (cell = rc) { PossibleCells[u n].RemoveAt(i2) if (checkout and PossibleCells[u n].length() = 1) { next := {u: u, rc: PossibleCells[u n][1], n: n, action: "one possible cell"} return "one left" } break } } ;---------------------------------------------------------------------------------------- ExplCollect(index, rc, n) { global if (IfThenBefore[rc n] != "" and not InStr(IfThenBeforeElement[index], IfThenBefore[rc n])) IfThenBeforeElement[index] .= IfThenBefore[rc n] if (IfThen[rc n] != "" and not InStr(IfThenBeforeElement[index], IfThen[rc n])) IfThenBeforeElement[index] .= IfThen[rc n] if (ExplBefore[rc n] != "" and not InStr(ExplBeforeElement[index], ExplBefore[rc n])) ExplBeforeElement[index] .= ExplBefore[rc n] if (explanation[rc n] != "" and not InStr(ExplBeforeElement[index], explanation[rc n])) ExplBeforeElement[index] .= explanation[rc n] } ;---------------------------------------------------------------------------------------- ExplConcat(expl) { global concat if (expl != "" and not InStr(concat, expl)) concat .= expl } ;---------------------------------------------------------------------------------------- fishnet(index) { local i, rc loop, 9 { %lineA%[index] := %lineA%[index-1] %lineB%[index] := %lineB%[index-1] if ((index = 2 or a_index > SubStr(%lineA%[index], 0)) and PossibleCells[a_index+plusA n].length() >= 2) { for i, rc in PossibleCells[a_index+plusA n] if not InStr(%lineB%[index], SubStr(rc, posB, 1)) %lineB%[index] .= SubStr(rc, posB, 1) if (StrLen(%lineB%[index]) <= 4) { %lineA%[index] .= a_index if (index = 4 and StrLen(%lineB%[index]) = 3 or index = 5 and StrLen(%lineB%[index]) = 4) { loop, parse, % %lineB%[index] loop, parse, % UnitCells[a_loopfield+plusB], - if (not InStr(%lineA%[index], SubStr(a_loopfield, posA, 1)) and number[a_loopfield] = 0 and InStr(PossibleNumbers[a_loopfield], n)) { rc := a_loopfield if (index = 4) fish := " ""Swordfish"" " else if (index = 5) fish := " ""Jellyfish"" " Text%lineA% := "" loop, parse, % %lineA%[index] Text%lineA% .= letterA[a_loopfield] "-" Text%lineA% := RTrim(Text%lineA%, "-") Text%lineB% := "" loop, parse, % %lineB%[index] Text%lineB% .= letterB[a_loopfield] "-" Text%lineB% := RTrim(Text%lineB%, "-") sort, Text%lineB%, d- TextPossibleCells := "" loop, % index-1 loop, parse, % UnitCells[SubStr(%lineA%[index], a_index, 1)+plusA], - if (number[a_loopfield] = 0 and InStr(%lineB%[index], SubStr(a_loopfield, posB, 1)) and InStr(AllPossibleNumbers[a_loopfield], n)) TextPossibleCells .= a_loopfield " , " TextPossibleCells := RTrim(TextPossibleCells, ", ") explanation[rc n] := n " can't be in " rc " because of the" fish "in " lineA " " Text%lineA% "/" lineB " " Text%lineB% ":`n" . "In " lineA " " Text%lineA% ", " n " can be in " TextPossibleCells ". That's all in " lineB " " Text%lineB% ", and that's one " n " for each " lineB ", " . "so there can't be any more " n "s in these " lineB "s.$" concat := "" loop, % index-1 loop, parse, % UnitCells[SubStr(%lineA%[index], a_index, 1)+plusA], - if not InStr(%lineB%[index], SubStr(a_loopfield, posB, 1)) ; no other lineB ExplConcat(explanation[a_loopfield n]) if (concat != "") explanation[rc n] := concat explanation[rc n] if (eliminate(rc, n) = "one left") return 1 } } else if (index < 4 or main_index > 1 and index < 5) if fishnet(index+1) return 1 } } } } ;---------------------------------------------------------------------------------------- font(TextControl, color, size, font1, font2 := "", weight := 400) { global color := hex(color) if (cTextControl[TextControl] != color or sTextControl[TextControl] != size or wTextControl[TextControl] != weight or fTextControl[TextControl] != font1 or HighlightOnTop) { gui, 1:font, w%weight% c%color% s%size%, %font1% if (font2 != "") gui, 1:font,, %font2% guicontrol, 1:font, %TextControl% guicontrol, 1:movedraw, %TextControl% cTextControl[TextControl] := color sTextControl[TextControl] := size wTextControl[TextControl] := weight fTextControl[TextControl] := font1 } } ;---------------------------------------------------------------------------------------- GuiToString(caller := "") { global string := display "/" cBackground "/" loop, parse, AllCells, - if (number[a_loopfield] != 0) { string .= a_loopfield number[a_loopfield] if (display = "numbers" and cNumber[a_loopfield] != "default") string .= cNumber[a_loopfield] string .= "," } string := StrReplace(string, "xyz") string := StrReplace(string, "0x") string := RTrim(string, ",") string .= "/" loop, parse, AllCells, - if (number[a_loopfield] = 0) loop, 9 if (PencilMark[a_loopfield a_index] = 1) { string .= a_loopfield a_index if (display = "numbers" and cPencilMark[a_loopfield a_index] != "default") string .= cPencilMark[a_loopfield a_index] string .= "," } string := RTrim(string, ",") if (caller = "history") string .= "/" FixedCells return string } ;---------------------------------------------------------------------------------------- hex(color) { global if InKeys(ColorValue, color) color := ColorValue[color] else if (SubStr(color, 1, 3) = "xyz") color := SubStr(color, 4) if (SubStr(color, 1, 2) != "0x") color := "0x" . color return color } ;---------------------------------------------------------------------------------------- highlight(rc, color, restore := 0) { global if (highlight[rc] = color and not restore) return highlight[rc] := color if (color = "") text("WhiteSquare" rc, "") else { font("WhiteSquare" rc, HighlightValue[color], sHighlight, "Webdings") text("WhiteSquare" rc, "g") } HighlightOnTop := 1 if (number[rc] != 0) set(rc, number[rc]) else loop, 9 if (PencilMark[rc a_index] = 1) PencilMark(rc, a_index, 1) HighlightOnTop := 0 } ;---------------------------------------------------------------------------------------- history() { global string := GuiToString("history") if (string != history[HistoryIndex]) { if (history.MaxIndex() > HistoryIndex) loop, % history.MaxIndex()-HistoryIndex history.pop() history.push(string) HistoryIndex += 1 if (history.MaxIndex() > 99) { history.RemoveAt(1) HistoryIndex -= 1 } return 1 } } ;---------------------------------------------------------------------------------------- InKeys(array, var) { For key, value in array if (var = key) return 1 } ;---------------------------------------------------------------------------------------- InValues(array, var) { VarString := var . "x" For index, value in array { ValueString := value . "x" ; "x" makes sure that var and value are compared as strings. if (VarString = ValueString) return 1 } } ;---------------------------------------------------------------------------------------- intersection(u, r1, r2, r3, c1, c2, c3) { global UnitCells[u] := "" loop, parse, AllCells, - { rc := a_loopfield r := SubStr(rc, 1, 1) c := SubStr(rc, 2, 1) if ((r = r1 or r = r2 or r = r3) and (c = c1 or c = c2 or c = c3)) UnitCells[u] .= rc "-" } UnitCells[u] := RTrim(UnitCells[u], "-") UnitRows[u] := [r1, r2, r3] UnitColumns[u] := [c1+9, c2+9, c3+9] } ;---------------------------------------------------------------------------------------- item(n) { global if (display = "colors") return ColorName[n] else return n } ;---------------------------------------------------------------------------------------- LeaveImage: loop, parse, AllCells, - if not InStr(image, a_loopfield) set(a_loopfield, 0) return ;---------------------------------------------------------------------------------------- merge(array, MergeWhat) { local rc, n, i1, i2, element1, element2 n := [] rc := [] for i1, element1 in array { loop, parse, element1, %a_space% { if (a_index = 1) n[i1] := a_loopfield else if (a_index = 5) { rc[i1] := a_loopfield break } } } array2 := [] skip := [] for i1, element1 in array { if skip[i1] continue n_merge := n[i1] rc_merge := rc[i1] " " for i2, element2 in array if (i2 > i1) { if (MergeWhat = "n") { string := StrReplace(array[i2], n[i2] " can't be in " rc[i2], n[i1] " can't be in " rc[i2]) if InStr(string, "X-wing") string := StrReplace(string, n[i2] " is ", n[i1] " is ") else if InStr(string, "Empty rectangle") or InStr(string, "Y-wing") string := StrReplace(string, rc[i2] " can't be " n[i2], rc[i2] " can't be " n[i1]) if (string = array[i1]) { skip[i2] := 1 n_merge .= " and " n[i2] } } else if (MergeWhat = "rc") { string := StrReplace(array[i2], n[i2] " can't be in " rc[i2], n[i2] " can't be in " rc[i1]) if InStr(string, "X-wing") string := StrReplace(string, rc[i2] " is ", rc[i1] " is ") else if InStr(string, "Empty rectangle") or InStr(string, "Y-wing") string := StrReplace(string, rc[i2] " can't be " n[i2], rc[i1] " can't be " n[i2]) if (string = array[i1]) { skip[i2] := 1 rc_merge .= " and " rc[i2] " " } } } if (MergeWhat = "n" and InStr(n_merge, " and ")) { array[i1] := StrReplace(array[i1], n[i1] " can't be in " rc[i1], n_merge " can't be in " rc[i1]) if InStr(array[i1], "X-wing") { array[i1]:= StrReplace(array[i1], n[i1] " is ", n_merge " are ") array[i1]:= StrReplace(array[i1], " it ", " they ") } else if InStr(array[i1], "Empty rectangle") or InStr(array[i1], "Y-wing") array[i1] := StrReplace(array[i1], rc[i1] " can't be " n[i1], rc[i1] " can't be " n_merge) } else if (MergeWhat = "rc" and InStr(rc_merge, " and ")) { array[i1] := StrReplace(array[i1], n[i1] " can't be in " rc[i1], n[i1] " can't be in " rc_merge) array[i1] := StrReplace(array[i1], " ", "") if InStr(array[i1], "X-wing") array[i1]:= StrReplace(array[i1], rc[i1] " is ", rc_merge " are ") else if InStr(array[i1], "Empty rectangle") or InStr(array[i1], "Y-wing") array[i1] := StrReplace(array[i1], rc[i1] " can't be " n[i1], rc_merge " can't be " n[i1]) } array2.push(array[i1]) } return array2 } ;---------------------------------------------------------------------------------------- MergeIfThen(MergeIndex) { local rc, n, IfThen1, IfThen2, i1, i2, element1, element2 IfThen1 := [] loop, parse, % explanation[EliminateIn EliminateWhat], `n if (a_loopfield != "" and not InValues(IfThen1, a_loopfield)) IfThen1.push(a_loopfield) NoMerge := [] text1 := [] rc := [] n := [] text2 := [] for i1, element1 in IfThen1 if (i1 > 1) { text1[i1] := "" rc[i1] := "" n[i1] := "" text2[i1] := "" loop, parse, element1, `, i2 := a_index if (i2 < MergeIndex) NoMerge[i1] := 1 else loop, parse, element1, `, { if (a_index < MergeIndex) text1[i1] .= a_loopfield "," else if (a_index = MergeIndex) { loop, parse, a_loopfield, %a_space% { if (MergeIndex = 2) ; a_index 2 to 7 = then rc can't be n { if (a_index = 2) text1[i1] .= " " a_loopfield else if (a_index = 3 or a_index = 4) rc[i1] .= " " a_loopfield else if (a_index = 7) n[i1] := " " RTrim(a_loopfield, ".") } else ; a_index 2 to 6 = rc can't be n { if (a_index = 2 or a_index = 3) rc[i1] .= " " a_loopfield else if (a_index = 6) n[i1] := " " RTrim(a_loopfield, ".") } } if (i2 = MergeIndex) text2[i1] := "." } else text2[i1] .= "," a_loopfield } } IfThen2 := [] skip := [] for i1, element1 in IfThen1 { if (i1 = 1 or NoMerge[i1]) { IfThen2.push(element1) continue } if skip[i1] continue merge_rc := rc[i1] merge_n := n[i1] for i2, element2 in IfThen1 if (i2 > i1 and text1[i1] = text1[i2] and (InStr(text2[i1], RTrim(text2[i2], ".")) or InStr(text2[i2], RTrim(text2[i1], ".")))) { skip[i2] := 1 if not InStr(merge_n, "-") and not InStr(merge_rc, rc[i2]) ; Don't merge rc and n at the same time. merge_rc .= "-" rc[i2] else if not InStr(merge_rc, "-") and not InStr(merge_n, n[i2]) merge_n .= "-" n[i2] if not InStr(text2[i1], RTrim(text2[i2], ".")) text2[i1] := text2[i2] } if InStr(merge_rc, "-") { sort, merge_rc, d- merge_rc := StrReplace(merge_rc, "-", " and") } else if InStr(merge_n, "-") { sort, merge_n, d- merge_n := StrReplace(merge_n, "-", " and") } IfThen2.push(text1[i1] merge_rc " can't be" merge_n text2[i1]) } explanation[EliminateIn EliminateWhat] := "" for i2, element2 in IfThen2 explanation[EliminateIn EliminateWhat] .= element2 "`n" } ;---------------------------------------------------------------------------------------- MouseClickCell(rc) { global rm := SubStr(rc, 1, 1) cm := SubStr(rc, 2, 1) x := wBorder + pos[cm] + wWhiteSquare/2 y := hTitleMenu + pos[rm] + wWhiteSquare/2 click %x%, %y%, 0 } ;---------------------------------------------------------------------------------------- MouseGetCell() { global mousegetpos, xm, ym cm := "" rm := "" loop, 9 if (xm >= wBorder+pos[a_index] and xm < wBorder+pos[a_index+1]) { cm := a_index break } loop, 9 if (ym >= hTitleMenu+pos[a_index] and ym < hTitleMenu+pos[a_index+1]) { rm := a_index break } if (cm != "" and rm != "") return 1 } ;---------------------------------------------------------------------------------------- NextElements(index) { global IfThenElement[index] := IfThenElement[index-1] IfThenBeforeElement[index] := IfThenBeforeElement[index-1] ExplBeforeElement[index] := ExplBeforeElement[index-1] QueuedEliminations[index] := QueuedEliminations[index-1] } ;---------------------------------------------------------------------------------------- NextPermutation(pattern, maximum) { global ; The patterns array started with the lower numbers, and each pattern is sorted numerically. ; => Permutating is done by increasing numbers, and the last numbers of each pattern are increased first. RevPattern := "" ; in reverse order ... loop, parse, pattern RevPattern := a_loopfield RevPattern FirstPossibleIncrease := "" loop, parse, RevPattern ; ... to loop higher numbers first if (a_loopfield < maximum-(a_index-1)) ; For a possible increase, the first digit of RevPattern (= the last digit of pattern) must be < maximum, the second digit must be < maximum-1 etc. { FirstPossibleIncrease := StrLen(pattern)-(a_index-1) ; refers to pattern again, not to RevPattern break } if (FirstPossibleIncrease = "") return "" ; => exit the loop for permutations permutation := "" loop, parse, pattern { if (a_index < FirstPossibleIncrease) permutation .= a_loopfield else if (a_index = FirstPossibleIncrease) { increase := a_loopfield+1 permutation .= increase } else { increase += 1 permutation .= increase } } return permutation } ;---------------------------------------------------------------------------------------- PencilMark(rc, p, value, color := "") { global if not KeepLastAction LastAction := "" PencilMark[rc p] := value if (color != "") cPencilMark[rc p] := color if (value = 0) text("PencilMark" rc p, "") else if (value = 1) { if (display = "colors") { font("PencilMark" rc p, ColorValue[ColorName[p]], sPencilColor, "Webdings") text("PencilMark" rc p, "=") } else { if (highlight[rc] != "" and cPencilMark[rc p] = "default") color := "black" else if (cPencilMark[rc p] = "default") color := cDefault else color := cPencilMark[rc p] weight := cPencilMark[rc p]="default" ? 400 : 700 font("PencilMark" rc p, color, sPencilNumber, "Ubuntu", "Arial", weight) text("PencilMark" rc p, p) } } } ;---------------------------------------------------------------------------------------- PleaseWait: wingetpos, winx, winy, winwidth, winheight, %WinTitle% ahk_class AutoHotkeyGUI x := winx+winwidth//2-2*wWhiteSquare y := winy+winheight//2-wWhiteSquare//2 s := sNumber*3//5 gui, 1:+disabled gui, PleaseWait:+owner1 -caption gui, PleaseWait:font, s%s% gui, PleaseWait:add, text,, Please wait ... gui, PleaseWait:show, x%x% y%y% NoActivate return ;---------------------------------------------------------------------------------------- PossibleNumbersLen(cell1, cell2, cell3 := "") { global PossibleNumbersString := "" loop, 3 if (cell%a_index% != "") loop, parse, % PossibleNumbers[cell%a_index%] if not Instr(PossibleNumbersString, a_loopfield) PossibleNumbersString .= a_loopfield "-" PossibleNumbersLen := StrLen(PossibleNumbersString)/2 if (PossibleNumbersLen = 3) { sort, PossibleNumbersString, d- n1 := SubStr(PossibleNumbersString, 1, 1) n2 := SubStr(PossibleNumbersString, 3, 1) n3 := SubStr(PossibleNumbersString, 5, 1) } return PossibleNumbersLen } ;---------------------------------------------------------------------------------------- set(rc, n, color := "") { global if not KeepLastAction LastAction := "" if (KeepExplanationAndHighlightings != "" and not InStr(KeepExplanationAndHighlightings, rc n)) { tooltip gui, explain:destroy loop, parse, AllCells, - highlight(a_loopfield, "") KeepExplanationAndHighlightings := "" } number[rc] := n if (color != "") cNumber[rc] := color if (context = "CreateImage" and not InStr(image, rc)) return if (context = "EasyAndSymmetrical" or context = "DifficultButNotSymmetrical") return if (n = 0) text("number" rc, "") else { loop, 9 PencilMark(rc, a_index, 0) if (display = "colors") { font("number" rc, ColorValue[ColorName[n]], sColor, "Webdings") text("number" rc, "=") } else { if (highlight[rc] != "") color := "black" else if (cNumber[rc] = "default") { if InStr(FixedCells, rc) color := cFixed else color := cDefault } else color := cNumber[rc] font("number" rc, color, sNumber, "Ubuntu", "Arial") text("number" rc, n) } } } ;---------------------------------------------------------------------------------------- SizeAndPosition(plus := 0) { global size := {"wWhiteSquare": 80, "sHighlight": 60, "sGreySquare": 64, "sNumber": 50, "sColor": 54 , "wPencilMark": 22, "sPencilNumber": 14, "sPencilColor": 12, "wCaption": 32, "sCaption": 20} ZoomMax := round(a_screenheight/960, 2) ; 12 squares should not be higher than the screen. if (plus = 0 and wGui != "" or plus < 0 and zoom < 0.52 or plus > 0 and zoom > ZoomMax-0.02) return 0 if (zoom+plus < 0.48) zoom := 0.48 else if (zoom+plus > ZoomMax+0.02) zoom := ZoomMax+0.02 else zoom += plus for var, value in size %var% := floor(zoom*value) if (a_screendpi != 96) { sHighlight := sHighlight*96//a_screendpi sGreySquare := sGreySquare*96//a_screendpi sNumber := sNumber*96//a_screendpi sColor := sColor*96//a_screendpi sPencilNumber := sPencilNumber*96//a_screendpi sPencilColor := sPencilColor*96//a_screendpi sCaption := sCaption*96//a_screendpi } if (zoom < 1) wLine := 1 else if (zoom < 1.5) wLine := 2 else if (zoom < 2) wLine := 3 else wLine := 4 pos := [] loop, 10 { pos[a_index] := (a_index-1)*(wWhiteSquare+wLine) ; positions of rows/columns/captions relative to the gui if (a_index = 10) pos[a_index] += 3*wLine else if (a_index > 6) pos[a_index] += 2*wLine else if (a_index > 3) pos[a_index] += wLine } wGui := pos[10]+wCaption loop, 3 add%a_index% := (wWhiteSquare-3*wPencilMark)//2 + (a_index-1)*wPencilMark ; positions of pencil mark rows/columns relative to the cell xAdd := [add1, add2, add3, add1, add2, add3, add1, add2, add3] yAdd := [add1, add1, add1, add2, add2, add2, add3, add3, add3] loop, parse, AllCells, - { ; The backgroundtrans option in the number and PencilMark text controls can cause double numbers/pencil marks. ; Movedraw after Set() and PencilMark() to avoid this. if (number[a_loopfield] != 0) set(a_loopfield, number[a_loopfield]) else loop, 9 if (PencilMark[a_loopfield a_index] = 1) PencilMark(a_loopfield, a_index, 1) if (highlight[a_loopfield] != "") highlight(a_loopfield, highlight[a_loopfield], 1) r := SubStr(a_loopfield, 1, 1) c := SubStr(a_loopfield, 2, 1) font("GreySquare" a_loopfield, "0x818181", sGreySquare, "Webdings") guicontrol, 1:movedraw, GreySquare%a_loopfield%, % "x" pos[c] "y" pos[r] "w" pos[c+1]-pos[c] "h" pos[r+1]-pos[r] ; GreySquare is not always an exact square. guicontrol, 1:movedraw, WhiteSquare%a_loopfield%, % "x" pos[c] "y" pos[r] "w" wWhiteSquare "h" wWhiteSquare guicontrol, 1:movedraw, number%a_loopfield%, % "x" pos[c] "y" pos[r] "w" wWhiteSquare "h" wWhiteSquare loop, 9 guicontrol, 1:movedraw, PencilMark%a_loopfield%%a_index%, % "x" pos[c]+xAdd[a_index] "y" pos[r]+yAdd[a_index] "w" wPencilMark "h" wPencilMark } loop, parse, caption123, - guicontrol, 1:movedraw, caption123%a_loopfield%, % "x" pos[10] "y" pos[a_index]+wWhiteSquare*0.3 "w" wCaption "h" wWhiteSquare loop, parse, captionABC, - guicontrol, 1:movedraw, captionABC%a_loopfield%, % "x" pos[a_index] "y" pos[10] "w" wWhiteSquare "h" wCaption captions(cDefault) return 1 } ;---------------------------------------------------------------------------------------- StringToGui(string) { local rc string1 := "" loop, parse, AllCells, - string1 .= a_loopfield number[a_loopfield] "-" loop, parse, AllCells, - { number[a_loopfield] := 0 cNumber[a_loopfield] := "default" loop, 9 { PencilMark[a_loopfield a_index] := 0 cPencilMark[a_loopfield a_index] := "default" } } loop, 5 SubStr%a_index% := "" loop, parse, string, /, `n`r SubStr%a_index% := a_loopfield if (display != SubStr1) { menu, ViewMenu, rename, 5&, S&witch from %SubStr1% to %display% display := SubStr1 } if (cBackground != SubStr2) background(SubStr2) loop, parse, SubStr3, `, { rc := SubStr(a_loopfield, 1, 2) number[rc] := SubStr(a_loopfield, 3, 1) if (StrLen(a_loopfield) > 3) cNumber[rc] := SubStr(a_loopfield, 4) } loop, parse, SubStr4, `, { rc := SubStr(a_loopfield, 1, 2) p := SubStr(a_loopfield, 3, 1) PencilMark[rc p] := 1 if (StrLen(a_loopfield) > 3) cPencilMark[rc p] := SubStr(a_loopfield, 4) } if (FixedCells = "" and SubStr5 != "") { menu, SudokuMenu, rename, 5&, &Unfix menu, SudokuMenu, add, &Unfix, unfix } else if (FixedCells != "" and SubStr5 = "") { menu, SudokuMenu, rename, 5&, &Fix menu, SudokuMenu, add, &Fix, fix } FixedCells := SubStr5 loop, parse, AllCells, - { set(a_loopfield, number[a_loopfield], cNumber[a_loopfield]) if (number[a_loopfield] = 0) loop, 9 PencilMark(a_loopfield, a_index, PencilMark[a_loopfield a_index], cPencilMark[a_loopfield a_index]) } changes := 0 loop, parse, AllCells, - if not InStr(string1, a_loopfield number[a_loopfield]) { changes += 1 if (changes > 1) { tooltip gui, explain:destroy loop, parse, AllCells, - highlight(a_loopfield, "") break } } } ;---------------------------------------------------------------------------------------- text(TextControl, text) { global if (tTextControl[TextControl] != text or HighlightOnTop) { guicontrol, 1:text, %TextControl%, %text% tTextControl[TextControl] := text } }