Sonntag, 11. Oktober 2015

Sudoku Spiel mit VBA Excel programmieren


Sudoku Spiel mit VBA Excel programmieren

Author D.Selzer/McKenzie

Video: https://youtu.be/9jIXI4PDkYs

Hier zeige ich Ihnen, wie Sie mit einfachem Code ein umfangreiches Sudoku-Spiel mit Excel-VBA  programmieren können.

Den Source-Code VBA können Sie im Forum


herunterladen.

 

Hier der SourceCode VBA>

Public Sub CreateMySudoku()

   Dim colResult     As Collection

   Dim varSolutions  As Variant

   Dim dtmBegin      As Date

   Dim i             As Long

   Dim k             As Long

   Dim m             As Long

  

   ' Zeitpunkt Beginn speichern

   dtmBegin = Now()

  

   ' Collection mit Lösungen erzeugen

   Set colResult = CreateSudoku

  

   ' Zieltabelle

   With Worksheets("Create Sudoku")

      .Range("A1:I9").ClearContents

     

      ' Alle Lösungen durchlaufen, momentan

      ' nur eine pro Durchlauf möglich

      For Each varSolutions In colResult

        

         For i = 1 To 9

            m = m + 1

            For k = 1 To 9

               ' Wert in Tabelle schreiben

               .Cells(m, k) = varSolutions(i, k)

            Next k

         Next i

        

      Next varSolutions

     

      ' Zeitdauer ausgeben

      MsgBox "Dauer : " & Format(Now() - dtmBegin, "nn:ss"), , _

         "Sudoku erzeugen"

        

   End With

  

End Sub

Private Function CreateSudoku( _

   Optional avarAll As Variant, _

   Optional colResult As Collection, _

   Optional varResult As Variant, _

   Optional Level As Long = 1, _

   Optional varOrder As Variant, _

   Optional blnEnd As Boolean, _

   Optional curCount As Currency _

   ) As Collection

  

   Dim i                         As Long

   Dim k                         As Long

   Dim m                         As Long

   Dim o                         As Long

   Dim x                         As Long

   Dim y                         As Long

   Dim blnImpossible             As Boolean

   Dim varDummy                  As Variant

   Dim astrSource(1 To 9)        As String

   Dim astrColumn(1 To 9)        As String

   Dim astrSquare(1 To 3)        As String

   Dim lngCount(1 To 9)          As Long

  

   ' Zufallsgenerator initialisieren

   Randomize

  

   If Level = 1 Then

  

      Set colResult = New Collection

     

      ReDim varResult(1 To 9, 1 To 9)

  

      ' Reihenfolge der Anfangsziffern

      varOrder = Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

     

      ' Elemente 1-9 mischen

      For i = 1 To 9

         k = Int(Rnd * 9 + 1)

         varOrder(0) = varOrder(i)

         varOrder(i) = varOrder(k)

         varOrder(k) = varOrder(0)

      Next

     

      ' Aus Collection ein mehrdimensionales Array machen,

      ' um Verarbeitung zu beschleunigen.

      ' 1. Dimension Anfangsziffern 1-9

      ' 2. Dimension 10 Elemente der Kombination.

      ' 3. Dimension 40320 Kombinationen pro Anfangsziffer

      ' 3. Dimension Element Nr. 0 = Puffer

      ReDim avarAll(1 To 9, 0 To 9, 0 To 40320)

     

      Application.StatusBar = "Kombinationen erzeugen"

     

      ' Alle Kombinationen durchlaufen

      For Each varDummy In MakeSudokuKombinations()

     

         ' Aktuelle Anfangsziffer. (1. Dimension)

         k = varDummy(1)

        

         ' Aktuelle Position im Array (3. Dimension)

         lngCount(k) = lngCount(k) + 1

        

         ' Kombinationen im mehrdimensionalem Array speichern

         For i = 0 To 9 ' 2. Dimension

            avarAll(k, i, lngCount(k)) = varDummy(i)

         Next

     

      Next varDummy

     

      ' Mischen

      Application.StatusBar = "Kombinationen mischen"

      For x = 1 To 9

         For y = 1 To 40320

            ' Zufallszahl erzeugen

            k = Int(Rnd * 40320 + 1)

            For i = 0 To 9

               ' Gewähltes Element mit dem in der aktuellen

               ' Reihenfolge stehenden tauschen. Das in der

               ' aktuellen Reihenfolge stehende wird im

               ' nächsten Durchlauf nicht mehr berücksichtigt

               ' Dazu je drei mal Ziffern tauschen. Element Nr.: 0

               ' dient als Puffer zur Zwischenspeicherung

               avarAll(x, i, 0) = avarAll(x, i, k)

               avarAll(x, i, k) = avarAll(x, i, y)

               avarAll(x, i, y) = avarAll(x, i, 0)

            Next i

         Next y

      Next x

     

      Set varDummy = Nothing

     

   Else

  

      ' Aus dem bisherigen Zahlen ein eindimensionales

      ' Stringarray machen. Jede Spalte ein Element,

      ' zusammengesetzt aus allen Ziffern einer Spalte

      For i = 1 To Level - 1

         For k = 1 To 9

            astrColumn(k) = astrColumn(k) & varResult(i, k)

         Next k

      Next i

        

      ' Aus dem bisherigen Zahlen ein eindimensionales

      ' Stringarray machen. Jeder Neunerblock der aktuellen

      ' Zeile ein Element, zusammengesetzt aus allen Ziffern

      ' des jeweiligen Neunerblocks

      Erase astrSquare

      i = Level - 1

      k = Level - 2

      Select Case Level

         Case 2, 5, 8

            astrSquare(1) = _

               varResult(i, 1) & varResult(i, 2) & varResult(i, 3)

            astrSquare(2) = _

               varResult(i, 4) & varResult(i, 5) & varResult(i, 6)

            astrSquare(3) = _

               varResult(i, 7) & varResult(i, 8) & varResult(i, 9)

         Case 3, 6

            astrSquare(1) = _

               varResult(k, 1) & varResult(k, 2) & varResult(k, 3) & _

               varResult(i, 1) & varResult(i, 2) & varResult(i, 3)

            astrSquare(2) = _

               varResult(k, 4) & varResult(k, 5) & varResult(k, 6) & _

               varResult(i, 4) & varResult(i, 5) & varResult(i, 6)

            astrSquare(3) = _

               varResult(k, 7) & varResult(k, 8) & varResult(k, 9) & _

               varResult(i, 7) & varResult(i, 8) & varResult(i, 9)

      End Select

     

   End If

  

   ReDim varDummy(0 To 9)

  

   ' Aktuelle Anfangszahl dieser Ebene auslesen

   x = varOrder(Level)

  

   For y = 1 To 40320

      ' In einer Schleife alle Zahlen durchlaufen

  

      For i = 0 To 9

     

         ' Gewähltes Element in Array "varDummy" kopieren

         varDummy(i) = avarAll(x, i, y)

        

      Next i

     

     

      blnImpossible = False

     

      For i = 1 To 9

     

         m = (i - 1) \ 3 + 1 ' Aktuellen Block berechnen

        

         ' Überprüfen, ob die Ziffer in der Spalte,

         ' oder im aktuellen Neunerblock bereits

         ' vorkommt

         If InStr(astrSquare(m), varDummy(i)) > 0 Then _

            blnImpossible = True: Exit For

         If InStr(astrColumn(i), varDummy(i)) > 0 Then _

            blnImpossible = True: Exit For

           

      Next i

     

      ' Getestete Kombinationen zählen

      curCount = curCount + 1

     

      ' Notausstieg

      If curCount > 5000000 Then

         blnEnd = True

         MsgBox "5.000.000 Kombinationen getestet!" & vbCrLf & _

            "Probieren Sie es noch einmal.", , "Abbruch"

      End If

     

      If (curCount Mod 1000) = 0 Then

         ' Alle 1000 Kombinationen Statusbar ansprechen

         Application.StatusBar = "Getestete Kombinationen : " _

            & Format(curCount, "#,##0")

         ' Alle 10000 Kombinationen ein Abarbeiten

         ' der Ereignisse zulassen

         If (curCount Mod 10000) = 0 Then DoEvents

      End If

     

      If blnImpossible = False Then

     

         ' Ziffer kommt weder in Spalte, noch im

         ' aktuellen Quadrat vor

     

         For i = 1 To 9

            ' Aktuelle Kombination dieser Ebene

            ' ins Ergebnisarray

            varResult(Level, i) = varDummy(i)

         Next i

        

         If Level = 9 Then

        

            ' Ergebnis zur Collection hinzufügen

            colResult.Add varResult

           

            ' Zum Beenden Variable auf True setzen

            blnEnd = True

           

         Else

        

            ' Funktion rekursiv aufrufen

            CreateSudoku avarAll, colResult, _

               varResult, Level + 1, varOrder, blnEnd, curCount

              

         End If

        

      End If

     

      ' Ergebnis gefunden, Schleife verlassen

      If blnEnd Then Exit For

     

   Next y

  

  

   If Level = 1 Then

  

      ' Collection als Funktionsergebnis zurückgeben

      Set CreateSudoku = colResult

     

      ' Statusbar zurücksetzen

      Application.StatusBar = False

     

   End If

  

End Function

 

Private Function MakeSudokuKombinations( _

   Optional ByVal varBefore As Variant, _

   Optional Level As Long = 1, _

   Optional colAll As Collection _

   ) As Collection

  

   Dim i             As Long

   Dim strKey        As String

  

   If colAll Is Nothing Then Set colAll = New Collection

  

   Select Case Level

  

      Case 1

     

         ' Array mit 10 Elementen erzeugen, Element Nr.: 0

         ' nimmt später Kombination als Zeichenkette auf

         varBefore = Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

        

      Case 9

     

         ' Kombination als Zeichenkette

         strKey = Mid(Join(varBefore, ""), 2)

        

         ' Element 0 nimmt die Zeichenkette auf

         varBefore(0) = strKey

        

         ' Kombination zur Kollection hinzufügen

         colAll.Add varBefore

        

         Exit Function

        

   End Select

  

   For i = Level To 9

  

      ' Jede Ziffer ab der aktuellen Level an die

      ' Position der aktuellen Level bringen, dazu

      ' je drei mal Ziffern tauschen. Element Nr.: 0

      ' dient als Puffer zur Zwischenspeicherung

      varBefore(0) = varBefore(Level)

      varBefore(Level) = varBefore(i)

      varBefore(i) = varBefore(0)

     

      ' Rekursiv aufrufen

      MakeSudokuKombinations varBefore, Level + 1, colAll

     

   Next

  

   ' Collection als Funktionsergebnis zurückgeben

   If Level = 1 Then Set MakeSudokuKombinations = colAll

  

End Function

 

Public Sub SolveMySudoku()

   Dim colResult     As Collection

   Dim varSource     As Variant

   Dim varSolutions  As Variant

   Dim varNumbers    As Variant

   Dim dtmBegin      As Date

   Dim i             As Long

   Dim k             As Long

   Dim m             As Long

  

   ' Zeitpunkt Beginn speichern

   dtmBegin = Now()

  

   ' Bereich in ein Variantarray beamen

   varSource = Worksheets("Sudoku").Range("A1:I9")

  

   ' Collection mit Lösungen erzeugen

   Set colResult = SolveSudoku(varSource)

  

   ' Zieltabelle

   With Worksheets("Sudoku")

      .Range("A21:I29").ClearContents

      .Range("A31:I1000").ClearContents

      .Range("F12").Value = 0

 

      ' 1. Zeile (-1) der Lösungsausgabe

      m = 20

     

      If Not (colResult Is Nothing) Then

     

         ' Alle Lösungen durchlaufen

         For Each varSolutions In colResult

           

            For i = 1 To 9

               m = m + 1

               For k = 1 To 9

                  ' Wert in Tabelle schreiben

                  .Cells(m, k) = varSolutions(i, k)

               Next k

            Next i

           

            ' Zwischen den Lösungen 5 Zeilen freilassen

            m = m + 5

           

         Next varSolutions

        

         ' Anzahl Lösungen ausgeben

         .Range("F12").Value = colResult.Count

        

      End If

     

     

      ' Zeitdauer ausgeben

      .Range("J12").Value = Now() - dtmBegin

        

   End With

  

End Sub

 

Private Function SolveSudoku( _

   varSource As Variant, _

   Optional varAll As Variant, _

   Optional colResult As Collection, _

   Optional varResult As Variant, _

   Optional Level As Long = 1, _

   Optional curCount As Currency _

   ) As Collection

   Dim i                            As Long

   Dim k                            As Long

   Dim m                            As Long

   Dim o                            As Long

   Dim blnImpossible                As Boolean

   Dim varDummy                     As Variant

   Dim colDummy(1 To 9)             As New Collection

   Dim astrSource(1 To 9)           As String

   Dim astrColumn(1 To 9)           As String

   Dim astrSquare(1 To 3)           As String

   Dim astrFilter(1 To 9, 1 To 9)   As String

  

   On Error GoTo ErrorHandler

  

   If Level = 1 Then

   ' Erster Funktionsaufruf

  

      For i = 1 To 9 ' Jede Zeile

         For k = 1 To 9

        

            ' Aus dem Quellbereich ein eindimensionales Stringarray

            ' machen. Jedes Element enthält alle Ziffern einer

            ' Zeile als zusammengesetze Zeichenkette, wobei leere

            ' Felder durch Fragezeichen ersetzt werden.

            ' Fragezeichen "?" ersetzen ein beliebiges Zeichen

            ' beim Operator "Like"

             If Len(varSource(i, k)) = 0 Then varSource(i, k) = "?"

            astrSource(i) = astrSource(i) & varSource(i, k)

           

            ' Aus den Ziffern der jeweiligen Spalte ausschließlich

            ' der aktuellen Zeile ein Stringarray machen

            For m = 1 To 9 ' Jede Spalte

               If k <> i Then

                  ' Jede Zeile außer aktueller

                  astrFilter(i, m) = astrFilter(i, m) & varSource(k, m)

               End If

            Next m

           

         Next k

      Next i

           

      varSource = astrSource

     

      Application.StatusBar = "Kombinationen erzeugen"

 

      ' Alle Kombinationen durchlaufen

      For Each varDummy In MakeSudokuKombinations()

     

         ' Jede Zeile des Quellbereichs durchlaufen

         For i = 1 To 9

           

            ' Das Element mit dem Index 0

            ' enthält alle Ziffern der Kombination

            ' als zusammengesetze Zeichenkette.

            ' Mit dem Muster der aktuellen Zeile

            ' des Quellbereichs vergleichen

            If varDummy(0) Like astrSource(i) Then

              

               ' Überprüfen, ob Ziffer der jeweiligen Spalte

               ' und der aktuellen Zeile einmalig ist

               blnImpossible = False

               For k = 1 To 9

                  If InStr(astrFilter(i, k), varDummy(k)) <> 0 Then

                     blnImpossible = True

                     Exit For

                  End If

               Next k

           

               ' Muster stimmen überein! Ziffer in Spalte einmalig!

               ' Zur neuen Collection hinzufügen, die anschließend

               ' nur die notwendigen Kombinationen enthält

               If blnImpossible = False Then colDummy(i).Add varDummy

           

            End If

           

         Next i

        

      Next varDummy

     

      ' Funktionsinterne Variable auf erzeugte Collection

      varAll = colDummy

     

      ' Neue Collection anlegen

      Set colResult = New Collection

     

      ' Zielarray anlegen

      ReDim varResult(1 To 9, 1 To 9)

     

   End If

  

   ' Aus dem bisherigen Zahlen ein eindimensionales Stringarray

   ' machen. Jede Spalte ein Element, zusammengesetzt aus allen

   ' Ziffern einer Spalte

   For i = 1 To Level - 1

      For k = 1 To 9

         astrColumn(k) = astrColumn(k) & varResult(i, k)

      Next k

   Next i

  

   ' Aus dem bisherigen Zahlen ein eindimensionales Stringarray

   ' machen. Jeder Neunerblock der aktuellen  Zeile ein Element,

   ' zusammengesetzt aus allen Ziffern des jeweiligen Neunerblocks

   Erase astrSquare

   i = Level - 1

   k = Level - 2

   Select Case Level

      Case 2, 5, 8

         astrSquare(1) = _

            varResult(i, 1) & varResult(i, 2) & varResult(i, 3)

         astrSquare(2) = _

            varResult(i, 4) & varResult(i, 5) & varResult(i, 6)

         astrSquare(3) = _

            varResult(i, 7) & varResult(i, 8) & varResult(i, 9)

      Case 3, 6

         astrSquare(1) = _

            varResult(k, 1) & varResult(k, 2) & varResult(k, 3) & _

            varResult(i, 1) & varResult(i, 2) & varResult(i, 3)

         astrSquare(2) = _

            varResult(k, 4) & varResult(k, 5) & varResult(k, 6) & _

            varResult(i, 4) & varResult(i, 5) & varResult(i, 6)

         astrSquare(3) = _

            varResult(k, 7) & varResult(k, 8) & varResult(k, 9) & _

            varResult(i, 7) & varResult(i, 8) & varResult(i, 9)

   End Select

 

  

   For Each varDummy In varAll(Level)

  

      blnImpossible = False

      For i = 1 To 9

         ' Überprüfen, ob die Ziffer in der Spalte,

         ' oder im aktuellen Neunerblock bereits vorkommt

         m = (i - 1) \ 3 + 1

         If InStr(astrSquare(m), varDummy(i)) > 0 Then _

            blnImpossible = True: Exit For

         If InStr(astrColumn(i), varDummy(i)) > 0 Then _

            blnImpossible = True: Exit For

      Next i

     

      ' Getestete Kombinationen zählen

      curCount = curCount + 1

     

      If (curCount Mod 1000) = 0 Then

     

         ' Alle 1000 Kombinationen die Statusbar ansprechen

         Application.StatusBar = "Getestete Kombinationen : " _

            & Format(curCount, "#,##0")

           

         ' Alle 5000 Kombinationen ein Abarbeiten

         ' der Ereignisse zulassen

         If (curCount Mod 5000) = 0 Then DoEvents

        

      End If

     

      If blnImpossible = False Then

      ' Ziffer kommt weder in Spalte, noch im aktuellen Quadrat vor

        

         For i = 1 To 9

            ' Aktuelle Kombination dieser Ebene

            ' ins Ergebnisarray

            varResult(Level, i) = varDummy(i)

         Next

        

         If Level = 9 Then

            ' Ergebnis zur Collection hinzufügen

            colResult.Add varResult

         Else

            ' Funktion rekursiv aufrufen

            SolveSudoku varSource, varAll, colResult, varResult, _

               Level + 1, curCount

         End If

        

      End If

     

   Next varDummy

  

  

   If Level = 1 Then

  

      ' Collection als Funktionsergebnis zurückgeben

      Set SolveSudoku = colResult

     

      ' Statusbar zurücksetzen

      Application.StatusBar = False

     

   End If

  

   Exit Function

ErrorHandler:

   ' Statusbar zurücksetzen

   Application.StatusBar = False

End Function

 

Private Function MakeSudokuKombinations( _

   Optional ByVal varBefore As Variant, _

   Optional Level As Long = 1, _

   Optional colAll As Collection _

   ) As Collection

  

   Dim i             As Long

   Dim strKey        As String

  

   If colAll Is Nothing Then Set colAll = New Collection

  

   Select Case Level

  

      Case 1

     

         ' Array mit 10 Elementen erzeugen, Element Nr.: 0

         ' nimmt später Kombination als Zeichenkette auf

         varBefore = Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

        

      Case 9

     

         ' Kombination als Zeichenkette

         strKey = Mid(Join(varBefore, ""), 2)

        

         ' Element 0 nimmt die Zeichenkette auf

         varBefore(0) = strKey

        

         ' Kombination zur Kollection hinzufügen

         colAll.Add varBefore

        

         Exit Function

        

   End Select

  

   For i = Level To 9

  

      ' Jede Ziffer ab der aktuellen Level an die

      ' Position der aktuellen Level bringen, dazu

      ' je drei mal Ziffern tauschen. Element Nr.: 0

      ' dient als Puffer zur Zwischenspeicherung

      varBefore(0) = varBefore(Level)

      varBefore(Level) = varBefore(i)

      varBefore(i) = varBefore(0)

     

      ' Rekursiv aufrufen

      MakeSudokuKombinations varBefore, Level + 1, colAll

     

   Next

  

   ' Collection als Funktionsergebnis zurückgeben

   If Level = 1 Then Set MakeSudokuKombinations = colAll

  

End Function

 

 

 

Keine Kommentare:

Kommentar veröffentlichen

Hinweis: Nur ein Mitglied dieses Blogs kann Kommentare posten.