A technique for solving both Sudoku and Slitherlink with the use of Excel VBA was considered for a long time. Although Sudoku is more compatible with Excel, Slitherlink is difficult and complicated. Excel is designed for spreadsheet purposes, not for creating documents or puzzles. But it's worth considering. Japanese
Figure 1. is a diagram showing how to solve a slitherlink puzzle. As can be seen, same number of lines are drawn around the squares so that the whole becomes one circle. For basic procedure, 4 examples are also presented in Figure 1, but many techniques are needed for solving a puzzle.
For the third procedure (where, two 3's are diagonally adjacent), VBA expression is as follow. For cell c,
If c.Value = 3 And c.Range("b2").Value = 3 Then With c.Borders(xlEdgeLeft): .Weight = xlThick: .ColorIndex = 9 End With …… …… …… End If
As consideration must also be taken to the case where the positional relationship is multifaceted symmetrical, VBA code will exceed 1,000 lines. Aiming for about 200 lines, difficulty of reading program would be unavoidable.
Figure 2 shows a typical format of this puzzle. It is possible to have 6 to 25 squares in both vertical and horizontal directions.
VBA will start with a MsgBox (message box) "Start?". No (N) aborts, but Cancel displays another message "InputBox", and clears the borders to their initial state. The InputBox allows you to choose the border color. Starting color is chestnut color as is shown in Figure 3.
Figure 3. is an example of completed one. Ideally, pencil puzzles can be solved without using an eraser.
In the solution here, a puzzle would be cleared in normal mode, if a degree of difficulty is at around 2 in 5 stages. When it get stuck, it will pause and ask if you want to continue in "trial mode". In trial mode, VBA will find an answer using an eraser.
A progress message will be displayed approximately every 30 seconds asking if you would like to continue. It is possible to stop temporarily with No (N) reply in the "MsgBox", and to add hints, and to continue. if adding involve a mistake or the originally impossible puzzle, VBA will stop displaying the "Error" message.
Figure 4.Example
2 | 2 | 3 | 2 | 1 | 1 | 0 | 2 | 3 | 2 | 3 | 2 | 0 | 3 | 1 | 2 | 2 | 1 | 2 | 0 | 2 | 1 | 2 | 2 | 3 | 2 | 2 | 1 | 1 | 0 | 1 | 2 | 2 | 3 | 3 | 0 | 3 | 1 | 2 | 3 | 2 | 1 |
Paste into the c3 cell of the Excel worksheet. It is best to create an outer frame.
Below is a memo.
xlEdgeLeft=7 左 c7
xlEdgeTop=8 上 c8
xlEdgeRight=10 右 c10
xlEdgeBottom=9 下 c9
左上 c1 右上 c2
右下 c3 左下 c4
CurrentStatus ABCD ---- abcd
DL Draw ABCD Erase abcd
A variety of techniques are shown in Figure 5.
Dim c0, St(5), wRange, myRange, Qt, OK, CoL, Smd, Er As Boolean Sub slither() ' ==== SlitherLink ver.5.5.4 ==== Dim c01 As Range, Rc(4), Qc(4) As Range, Ar(11) For Each c In Range("a1:h8"): If c.Borders(7).LineStyle = 1 Then Set c01 = c: Exit For Next: If c01 Is Nothing Then MsgBox "Range Missing": End If c01.Row = 1 Or c01.Column = 1 Then c01.EntireRow.Insert: c01.EntireColumn.Insert For i = 1 To 27: If c01.Cells(i + 1, 1).Borders(7).LineStyle <> 1 Then y = i: Exit For Next '(注)xlEdgeLeft=7, xlEdgeTop=8, xlEdgeRight=10 xlEdgeBottom=9 xlSolid=1 For i = 1 To 27: If c01.Cells(1, i + 1).Borders(8).LineStyle <> 1 Then x = i: Exit For Next: If x * y < 36 Then MsgBox "number of squares are small": End Set c02 = c01.Cells(y, x): Set wRange = Range(c01, c02) If c01.Borders(10).LineStyle = 1 Then c01.EntireRow.Insert: c01.EntireColumn.Insert: Set c01 = c01.Offset(-1, -1): Set c02 = c02.Range("b2") Set wRange = Range(c01, c02): wRange.BorderAround Weight:=xlThin End If: Set P0 = c01: Set c0 = c01.Range("b2"): Set myRange = Range(c0, c02.Offset(-1, -1)): CoL = 9 Rg = Range(c01, c02.Range("B2")): St(0) = Rg: St(1) = Rg: St(2) = Rg: St(3) = Rg: St(4) = Rg: St(5) = Rg wRange.ColumnWidth = 3.4: wRange.RowHeight = 22.6: ActiveWindow.DisplayGridlines = False a = MsgBox("Start? clear, color change=cancel、Abort=n", vbYesNoCancel): If a = vbNo Then End If a = vbCancel Then CoL = InputBox("color number, End = cancel" & vbCr & "red3 bluw5 vioret7 "): _ wRange.Borders.LineStyle = 0: wRange.BorderAround Weight:=xlThin: myRange.Borders.Weight = xlThin y = wRange.Rows.Count: x = wRange.Columns.Count: Smd = 2: If CoL = "" Then End Set P1 = Range("BA2"): Set P2 = Range("BA42"): w = "a1:" & Chr(96 + x) & y: Set Rg1 = P1.Range(w) Set Rg2 = P2.Range(w): Set Bd = Range("a1").Borders(7): If CoL = 3 Or CoL = 5 Or CoL = 7 Then Else CoL = 9 myLoop: Call CurrentStatus(c) For Each c In Range(c0, c02): Set c1 = S(c, 1): Set c2 = S(c, 2): Set c3 = S(c, 3): Set c4 = S(c, 4): Set c7 = S(c, 7) Set c8 = S(c, 8): Set c9 = S(c, 9): Set c10 = S(c, 10): L = fS(c, 2): n = fS(c, 3): u = fS(c, 4): v = c.Value If v <> "" And v = 0 Then DL c, "", 0 '==== 0 の場合と <3^3^3> If v = 3 And c3.Value = 3 Then If c.Range("c1").Value = 3 Then DL c10, "b", 0 Else If c2.Value = 3 Then _ DL c10, "c", 0 Else If c4.Value = 3 Then DL c9, "d", 0 Else If c.Range("a3").Value = 3 Then DL c9, "a", 0 If u > 0 Then If v = L + u Then DL c, "", 1 Else If (L > 0 And v = L) Or L = 3 Then DL c, "", 0 '=== 罫線飽和や完了時 Set Rc(1) = c1: Set Rc(2) = c2: Set Rc(3) = c3: Set Rc(4) = c4: Set Qc(1) = c10: Set Qc(2) = c9: Set Qc(3) = c7 Set Qc(4) = c8: Ar(0) = Array(1, 2, 3, 4, 1, 2, 3): Ar(1) = Split(" ABC BCD CDA DAB"): Ar(2) = Split(" AB BC CD DA") Ar(3) = Split(" A B C D"): Ar(4) = Split(" a b c d"): Ar(5) = Split(" -b -c -d -a"): Ar(6) = Split(" ab bc cd da") Ar(7) = Split(" A|B B|C C|D D|A"): Ar(8) = Split(" Ab|aB Bc|bC Cd|cD Da|dA") Ar(9) = Split(" A-|-B B-|-C C-|-D D-|-A"): Ar(10) = Split(" a-|-b b-|-c c-|-d d-|-a") For i = 1 To 4: k = Ar(0)(i): j = Ar(0)(i + 1): m = Ar(0)(i + 2) If L = 4 Or L + u < v Or (v > 0 And L > v) Or (F(Rc(i), Ar(2)(j)) And F(c, Ar(3)(i) & "|" & Ar(3)(k))) Or _ (F(Rc(i), Ar(6)(j)) And F(c, Ar(8)(i))) Then Er = True: Exit For '=== Errorの場合 If F(Qc(j), Ar(1)(m)) Then If L = 2 And u > 0 Then DL c, "", 0 Else If L = 3 Then Er = True: Exit For If F(Qc(j), Ar(1)(m)) And (F(Qc(m), Ar(1)(i)) Or F(Qc(i), Ar(1)(k))) Then _ If L = 1 And u > 0 Then DL c, "", 0 Else If L = 2 Then Er = True: Exit For If (F(Rc(i), Ar(1)(m)) And F(Qc(m), Ar(2)(k))) Or (F(Rc(m), Ar(1)(m)) And F(Qc(k), Ar(2)(j))) Then _ If L = 2 And u > 0 Then DL c, "", 0 Else If L = 3 Then Er = True: Exit For If v = 1 And Qc(i).Value = 1 And F(Rc(k), Ar(5)(m)) Then DL c, Ar(3)(j), 0: _ If Qc(k).Value = 3 Or Rc(j).Value = 3 Then DL c, Ar(3)(k), 0 '===@ 1 | 1 @' 1 | 1 の場合 If v = 1 And Qc(i).Value = 3 And F(Rc(k), Ar(5)(m)) Then DL c, Ar(2)(m), 0: DL Qc(i), Ar(3)(k), 1 '===A 1 | 3 の場合 If v = 3 And Qc(i).Value = 1 And F(Rc(k), Ar(5)(m)) Then DL Qc(i), Ar(2)(j), 0: DL c, Ar(3)(k), 1 '===A 3 | 1 の場合 If v = 3 And Qc(i).Value = 3 Then '== 3 | 3の場合 DL c, Ar(3)(i), 1: DL c, Ar(3)(j), 1: DL Rc(k), Ar(3)(i), 0: If Rc(k).Value = 2 Then DL Rc(k), Ar(3)(k), 1 If Rc(j).Value = 2 Then DL Rc(j), Ar(3)(m), 1 End If If v = 3 And Rc(i).Value = 3 Then DL Rc(i), Ar(2)(i), 1: DL c, Ar(2)(j), 1 '=== <3 3> の場合 If F(Rc(i), Ar(6)(j)) And u > 0 Then '=== B 0 > 3 C 0 > 1, ┌ & ┛ E 0 > 2 └ & ┐ F 0 >2> 1 の場合 If v = 3 Then DL c, Ar(2)(i), 1 Else If v = 1 Then DL c, Ar(2)(i), 0 _ Else If v = 2 And Rc(j) = 1 Then DL Rc(j), Ar(2)(i), 0 Else If F(c, Ar(2)(j)) Then DL c, Ar(2)(i), 0 If v = 2 And (F(c, Ar(10)(j)) Or F(Rc(j), Ar(7)(i))) Then DL c, Ar(2)(i), 1 If F(Rc(j), Ar(6)(i)) Then If F(Rc(k), Ar(6)(m)) Or F(Rc(m), Ar(2)(k)) Then Call DL(c, "", 0) '===└ & ┐ End If '=== D 0 >1> 3 G罫線 3 に接近の場合 H罫線 1 に接近の場合 OP 罫線 2 に接近の場合 = 曲進不可 If v = 1 And F(c, Ar(6)(i)) And Rc(j).Value = 3 Then DL Rc(j), Ar(2)(j), 1 '=== D 0 >1> 3 If F(Rc(i), Ar(7)(j)) And v = 3 Then DL c, Ar(2)(j), 1 Else If F(Rc(i), Ar(8)(j)) And v = 1 Then DL c, Ar(2)(j), 0 If F(Rc(i), Ar(9)(j)) And (v = 3 Or (v = 2 And (F(Rc(j), Ar(9)(i)) Or F(c, Ar(10)(j))))) Then DL Rc(i), Ar(6)(j), 0 If F(Rc(i), Ar(6)(j)) And F(c, Ar(9)(i)) Then DL c, Ar(2)(i), 1 '====曲進行の場合 If F(c, Ar(3)(k) & Ar(4)(j)) And F(Rc(k), Ar(5)(m)) Then DL Rc(k), Ar(3)(m), 1 '==== 直進の場合 If F(Rc(m), Ar(3)(k) & Ar(4)(j)) And F(c, Ar(5)(m)) Then DL c, Ar(3)(m), 1 '==== 直進2 If F(c, Ar(2)(i)) And F(Rc(i), "-") Then DL Rc(i), Ar(6)(j), 0 '==== 曲進消去 If F(Rc(i), Ar(6)(j)) And F(c, Ar(10)(i)) Then DL c, Ar(6)(i), 0 '==== 行止の場合 If F(c, Ar(3)(i) & "-") And F(Rc(i), Ar(3)(j)) Then DL c, Ar(3)(k), 0: DL Rc(i), Ar(3)(m), 0 '== ┨ ┷ の場合 If F(Rc(i), Ar(6)(j)) And v = 2 Then If F(Rc(k), Ar(5)(m)) Then DL Rc(k), Ar(3)(m), 1 _ Else If F(Rc(m), Ar(4)(k)) Then DL Rc(m), Ar(3)(j), 1 '===I 0 > 2 Right If (F(Rc(i), Ar(6)(j)) Or F(Rc(k), Ar(6)(m))) And F(Rc(j), Ar(4)(k)) And F(Rc(m), Ar(4)(k)) And _ Qc(k).Value = 3 Then DL c, Ar(3)(m), 1 '=== N 0 > ? | 3 If Rc(i).Value <> 2 And v = 2 And Rc(j).Value = 2 Then Set R = Rc(j): Do While R.Value = 2: Set R = S(R, j): Loop: _ If R.Value = 3 Then If Rc(i).Value = 3 Or F(Rc(i), Ar(7)(j)) Or (Rc(i).Value = 1 And F(Rc(i), Ar(6)(i))) Then _ DL R, Ar(2)(j), 1 Else If F(Rc(i), Ar(6)(j)) Then DL R, Ar(2)(i), 1 '=== <3 2 2 2 2 2 2 3> If v = 2 And Rc(j).Value = 3 Then ' === 0 < 1 > 2 >3 0> 2 >3 <3 2 3> K L M If Rc(i).Value = 3 Or F(Rc(i), Ar(7)(j)) Or (Rc(i).Value = 1 And F(Rc(i), Ar(6)(i))) Then DL Rc(j), Ar(2)(j), 1 _ Else If F(Rc(i), Ar(6)(j)) Then DL Rc(j), Ar(2)(i), 1 If F(c, Ar(10)(i)) Or F(Rc(k), Ar(6)(m)) Or F(Rc(m), Ar(6)(k)) Then DL Rc(j), Ar(2)(j), 1: If F(c, Ar(4)(i)) Then _ DL c, Ar(3)(k), 1 Else If F(c, Ar(4)(k)) Then DL c, Ar(3)(i), 1 End If: Next: If Er = True Then Exit For Next: If Smd > 0 And Er = True Then MsgBox "Unexpected error": End If Er = False And Qt > 0 Then Qt1 = Qt1 + Qt: Qt = 0: GoTo myLoop For Each c In myRange: D = "": If F(c, "AB") Then D = "42" Else If F(c, "CD") Then D = "24" If D <> "" Then Call Cood(c, Val(Left(D, 1)), Val(Right(D, 1))): If OK = True Then OK = False: GoTo myLoop Next If Smd = 2 Or Timer - swT > 30 Then Application.ScreenUpdating = True: a = MsgBox("continue in trial mode?", vbYesNo): swT = Timer If a = vbNo Then If Smd > 0 Then Call Fin(c) Else If Smd < 1 Then Rg1.Copy P0: Call Fin(c) End If: Qt2 = Qt1: Qt1 = 0: Application.ScreenUpdating = False If Smd = 0 And Er = False And Qt2 > 30 Then Set Bd0 = Bd: Qt2 = 0: wRange.Copy P2: Smd = -1 If Smd = 0 Then Rg1.Copy P0 Else If Smd = -2 Then Rg2.Copy P0 If Er = True Then Select Case Smd Case 0: Bd.LineStyle = xlNone: Smd = 1: Case -1: Rg1.Copy P0: Bd0.LineStyle = xlNone: Smd = 1 Case -2: Bd.LineStyle = xlNone: Smd = -1: End Select: hst = "!" & hst: Er = False: GoTo myLoop End If Call Ttest(Bd, hst): If Smd >= 0 Then wRange.Copy P1: Smd = 0 Else If Smd < 0 Then wRange.Copy P2: Smd = -2 Bd.Weight = 4: Bd.ColorIndex = CoL: GoTo myLoop End Sub Sub Fin(c) Range("BA1:CM80").Clear: End End Sub Sub Cood(c, d1, d2) If Er = True Then Exit Sub Else Set c5 = c: St(5) = wRange: Call SL(c5) Do: mug = mug + 1: If mug > 10 And c.Address = c5.Address Then Er = True: Exit Do Select Case d1 Case 1: Set R = S(c5, 1): If F(c5, "B") Then d1 = 2 Else If F(R, "C") Then Set c5 = S(R, 10): d1 = 1: Call SL(c5) _ Else If F(R, "D") Then Set c5 = R: Call SL(c5): d1 = 4 Else Set hr1 = c5: Set vt1 = S(c5, 8): Exit Do Case 2: Set R = S(c5, 2): If F(c5, "C") Then d1 = 3 Else If F(R, "D") Then Set c5 = S(R, 9): d1 = 2: Call SL(c5) _ Else If F(R, "A") Then Set c5 = R: Call SL(c5): d1 = 1 Else Set hr1 = S(c5, 10): Set vt1 = S(c5, 10): Exit Do Case 3: Set R = S(c5, 3): If F(c5, "D") Then d1 = 4 Else If F(R, "A") Then Set c5 = S(c5, 9): d1 = 3: Call SL(c5) _ Else If F(R, "B") Then Set c5 = R: Call SL(c5): d1 = 2 Else Set hr1 = S(c5, 9): Set vt1 = S(c5, 3): Exit Do Case 4: Set R = S(c5, 4): If F(c5, "A") Then d1 = 1 Else If F(R, "B") Then Set c5 = S(R, 8): d1 = 4: Call SL(c5) _ Else If F(R, "C") Then Set c5 = R: Call SL(c5): d1 = 3 Else Set hr1 = S(c5, 4): Set vt1 = c5: Exit Do End Select: Loop: '======成功 終了場面 へ If Er = True Then Call IsSuc(Success): If Smd < 1 Then Exit Sub Else MsgBox "infinite loop": Call Fin(c) Set c6 = c: Do: mug = mug + 1: If mug > 800 Then MsgBox "Error ?": Call Fin(c) Select Case d2 Case 1: Set R = S(c6, 1): If F(c6, "A") Then d2 = 4 Else If F(R, "D") Then Set c6 = S(R, 9): d2 = 1 _ Else If F(R, "C") Then Set c6 = R: d2 = 2 Else Set hr2 = S(c6, 7): Set vt2 = c6: Exit Do Case 2: Set R = S(c6, 2): If F(c6, "B") Then d2 = 1 Else If F(R, "A") Then Set c6 = S(R, 7): d2 = 2 _ Else If F(R, "D") Then Set c6 = R: d2 = 3 Else Set hr2 = c6: Set vt2 = S(c6, 2): Exit Do Case 3: Set R = S(c6, 3): If F(c6, "C") Then d2 = 2 Else If F(R, "B") Then Set c6 = S(R, 8): d2 = 3 _ Else If F(R, "A") Then Set c6 = R: d2 = 4 Else Set hr2 = S(c6, 3): Set vt2 = S(c6, 10): Exit Do Case 4: Set R = S(c6, 4): If F(c6, "D") Then d2 = 3 Else If F(R, "C") Then Set c6 = S(R, 10): d2 = 4 _ Else If F(R, "B") Then Set c6 = R: d2 = 1 Else Set hr2 = S(c6, 9): Set vt2 = S(c6, 9): Exit Do End Select Loop: ad1 = hr1.Address: ad2 = hr2.Address: ad3 = vt1.Address: ad4 = vt2.Address If ad1 = ad2 Then Set b = hr1.Borders(8): If b.LineStyle <> xlNone Then b.LineStyle = xlNone: OK = True: Exit Sub If ad3 = ad4 Then Set b = vt1.Borders(7): If b.LineStyle <> xlNone Then b.LineStyle = xlNone: OK = True: Exit Sub End Sub Sub DL(c, wd, n) If wd = "" Then wd = "abcd" Else If fS(c, 4) = 0 Then Exit Sub i = 1: Erf = False: If Asc(wd) > 96 Then Erf = True '=== xlThick=4 wd = UCase(wd): w = Left(wd, 1) Do While w <> "" m = 0: Set b = c.Borders(InStr("ABDC", w) + 6): If b.Weight = 4 Then m = 1 Else If b.LineStyle = xlNone Then m = -1 If (m = 1 And n = 0) Or (m = -1 And n = 1) Then If Erf = False Then Er = True If Er = True Then If Smd < 1 Then Exit Sub Else If Smd > 0 Then c.Activate: MsgBox "Error in " & c.Address: End If m = 0 Then Qt = Qt + 1: If n = 1 Then b.Weight = xlThick: b.ColorIndex = CoL Else If n = 0 Then b.LineStyle = xlNone i = i + 1: w = Mid(wd, i, 1) Loop End Sub Sub CurrentStatus(c) For Each c In wRange: w = "": L = 0: m = 0: n = 0 For i = 7 To 10: j = i: If i = 9 Then j = 10 Else If i = 10 Then j = 9 Set b = c.Borders(i): If b.Weight = 4 Then L = L + 1: w = w & Chr(58 + j) _ Else If b.LineStyle = xlNone Then n = n + 1: w = w & Chr(90 + j) Else w = w & "-" Next: w1 = Left(w, 2) & Right(w, 1) & Mid(w, 3, 1): w = w1 & w1 Call Lst(c, 1, w): Call Lst(c, 2, L): Call Lst(c, 3, n): Call Lst(c, 4, 4 - L - n) Next End Sub Sub Ttest(Bd, hst) TrAg: For j = 1 To 2 For Each c In myRange If (j = 1 And fS(c, 4) = 1) Or (j > 1 And fS(c, 4) > 0) Then For i = 7 To 10 If c.Borders(i).LineStyle = 1 And c.Borders(i).Weight = xlThin Then If i = 9 Then w2 = c.Range("a2").Address & "8" Else If i = 10 Then w3 = c.Range("b1").Address & "7" w = c.Address & i: If InStr(hst, w) Then Else hst = hst & w & w2 & w3: Set Bd = c.Borders(i): Exit Sub End If: Next i: End If: Next: Next j If InStr(hst, "!") Then hst = "": GoTo TrAg MsgBox "Give UP": Call Fin(c) End Sub Sub SL(c) Dim Sc(4): Set Sc(1) = S(c, 7): Set Sc(2) = S(c, 8): Set Sc(3) = S(c, 9): Set Sc(4) = S(c, 10) Call Lst(c, 5, 0): For i = 1 To 4: If c.Borders(i + 6).Weight = 4 Then Call Lst(Sc(i), 5, 0) Next End Sub Sub Lst(c, id, wd) x = c.Column - c0.Column + 2: y = c.Row - c0.Row + 2: St(id)(y, x) = wd End Sub Sub IsSuc(Success) For Each c In myRange: If fS(c, 5) > 0 Then Exit Sub Next For Each c In myRange: For i = 7 To 10: Set b = c.Borders(i) If b.LineStyle = 1 And b.Weight = xlThin Then b.LineStyle = xlNone Next: Next: Application.ScreenUpdating = True: MsgBox "completed": Call Fin(c) End Sub Function F(c, w) F = False: x = c.Column - c0.Column + 2: y = c.Row - c0.Row + 2: w0 = St(1)(y, x): n = InStr(w, "|") If n = 0 Then If InStr(w0, w) Then F = True: Exit Function Else Exit Function If InStr(w0, Left(w, n - 1)) Or InStr(w0, Right(w, Len(w) - n)) Then F = True: Exit Function End Function Function S(c, n) Select Case n: Case 1: Set S = c.Offset(-1, -1): Case 2: Set S = c.Offset(-1, 1) Case 3: Set S = c.Range("b2"): Case 4: Set S = c.Offset(1, -1): Case 7: Set S = c.Offset(0, -1) Case 8: Set S = c.Offset(-1, 0): Case 9: Set S = c.Range("a2"): Case 10: Set S = c.Range("b1"): End Select End Function Function fS(c, n) x = c.Column - c0.Column + 2: y = c.Row - c0.Row + 2: fS = St(n)(y, x) End Function