Hallo. Ich bin absolut neu bei Python. Es gibt da einen Code den ich unbedingt ausführen möchte:
https://www.101computing.net/sudoku-gen ... algorithm/
Ich habe die neue Version von Python installiert. In der Konsole gebe ich py ein. Dann mache ich mit dem Code aus obiger Quelle Copy und Paste. Leider wird nur das Sudoku Gitternetz gezeichnet und die Zahlen werden nicht ausgegeben.
Dabei funktioniert der Code, wenn man ihn auf der Homepage ausführt. Wo also ist der Fehler?
Vielen Dank für Antworten.
Externer Code funktioniert nicht
- __blackjack__
- User
- Beiträge: 14078
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@Player2022: Also bei mir funktioniert es mit Python 3.7 wenn ich den Aufruf ``tracer(0)`` nicht auf dem `myPen`-Objekt mache, sondern die Funktion im `turtle`-Modul verwende, denn das ist eine API-Änderung die mit dem Umstieg auf Python 3 kam.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
-
- User
- Beiträge: 4
- Registriert: Mittwoch 13. Juli 2022, 22:16
@ __blackjack__: Also wie soll ich "myPen.tracer(0)" konkret ersetzen? Es hat also etwas mit der Bildschirmaktualisierung zu tun wie ich im Web gelesen habe? Bitte langsam und Schritt für Schritt erklären.
Hallo,
@Player2022 die Antwort von __blackjack__ ist so zu verstehen:
In dem Code steht in Zeile 19 Er schrieb, dass er den Aufruf von 'trace(0)' nicht auf das 'myPen' - Objekt macht. Das heißt also, dass die Zeile 19 rausfliegt und dafür muss 'trace(0)' anders aufgerufen werden. Das hat er auch geschrieben, nämlich dass er 'trace' aus dem 'turtle'-Modul verwendet. Also würde man an der Stelle in die Doku von 'turtle' schauen und die Funktion suchen:
https://docs.python.org/3/library/turtl ... tle.tracer
Dann Zeile 19 durch
ersetzen und so läufts bei mir mit Python 3.9 auch.
Grüße
Dennis
@Player2022 die Antwort von __blackjack__ ist so zu verstehen:
In dem Code steht in Zeile 19
Code: Alles auswählen
myPen.tracer(0)
https://docs.python.org/3/library/turtl ... tle.tracer
Dann Zeile 19 durch
Code: Alles auswählen
turtle.tracer(0)
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
-
- User
- Beiträge: 4
- Registriert: Mittwoch 13. Juli 2022, 22:16
Hallo.
Ich bin so vorgegangen wie beschrieben, doch das Problem bleibt.
Das Gitter erscheint, ohne die Zahlen- Bei Copy Paste über die Konsole. Bei Doppelklick öffnet sich das Programm nicht einmal.
Ich benutze übrigens Version 3.10. An was kann das liegen?
Viele Grüße
Ich bin so vorgegangen wie beschrieben, doch das Problem bleibt.
Das Gitter erscheint, ohne die Zahlen- Bei Copy Paste über die Konsole. Bei Doppelklick öffnet sich das Programm nicht einmal.
Ich benutze übrigens Version 3.10. An was kann das liegen?
Viele Grüße
-
- User
- Beiträge: 4
- Registriert: Mittwoch 13. Juli 2022, 22:16
Hallo.
Hab es nun mit PYCharm ausprobiert und es funktioniert. Das Problem ist, dass das Sudoku gleich schließt nach Erzeugung.
Was kann ich da machen?
Hab es nun mit PYCharm ausprobiert und es funktioniert. Das Problem ist, dass das Sudoku gleich schließt nach Erzeugung.
Was kann ich da machen?
- __blackjack__
- User
- Beiträge: 14078
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
Die Funktion aufrufen die das verhindert. Schau doch mal was das `turtle`-Modul so alles bietet. Das ist ja nicht undokumentiert.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
- __blackjack__
- User
- Beiträge: 14078
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
Der Quelltext von der Webseite ist übrigens nicht gut. Der hält sich nicht an die üblichen Konventionen wie Einrückung mit vier Leerzeichen pro Ebene und das Namen in Python klein_mit_unterstrichen geschrieben werden. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).
Grunddatentypen haben nichts in Namen verloren. Den Typen ändert man gar nicht so selten mal während der Programmentwicklung und dann muss man überall im Programm die betroffenen Namen ändern, oder man hat falsche, irreführende Namen im Quelltext.
Der Namenszusatz `my` macht keinen Sinn wenn es den gleichen Namen nicht auch mit `our` oder `their` gibt, gegen den sich das `my` irgendwie abgrenzen würde.
Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. ``global`` und globale Variablen verwendet man nicht. Alles was eine Funktion/Methode ausser Konstanten benötigt, bekommt sie als Argument(e) übergeben. In `fillGrid()` ist ``global`` sogar überflüssig, weil dort überhaupt nicht auf `counter` zugegriffen wird.
Kommentare vor Funktionen die beschreiben was die Funktion tut, sollten keine Kommentare sondern Docstrings sein.
Das erstellen des leeren Sodukofelds durch neun mal hinschreiben des gleichen `append()` mit literalen Listen mit jeweis neun Nullen ist nicht idiomatisch für Python. Mal abgesehen davon, dass man die gesamte verschachtelte Liste als literal hätte schreiben können, würde man Code schreiben der diese Datenstruktur erstellt, statt da mit viel Tipparbeit und/oder kopieren und einfügen zu arbeiten.
Statt ``not a in b`` würde man ``a not in b`` schreiben.
Statt beim Test ob der Wert schon in der Spalte vorkommt, jeden Index von 0 bis 8 manuell hin zu schreiben, würde man das in einer Schleife machen.
Für `square` ist zu viel Code verwendet worden. Statt der vielen ``if``-Bedingungen könnte man die entsprechenden Indizes auch einfach ausrechnen.
`square` wird an eine leere Liste gebunden die niemals irgendwo verwendet wird.
Es ist extrem verwirrend, dass in der Schleife mit der Laufvariablen `i` „list comprehensions“ stehen die ebenfalls `i` als Laufvariable verwenden. Auch wenn das ein eigener Namensraum ist, mag das dem menschlichen Leser nicht immer klar sein.
Die `fillGrid()`-Funktion gibt entweder `True` oder `None` zurück — das sollte `True` oder `False` sein.
`checkGrid()` lässt sich mit Comprehensions ohne Indexzugriffe und `any()` oder `all()` mit einem Ausdruck beschreiben.
`drawGrid()` braucht `myPen` als Argument statt das einfach so magisch aus der Umgebung zu verwenden.
Namen sollten keine kryptischen Abkürzungen enthalten, oder gar nur daraus bestehen. `intDim`?
Funktionen und Methoden werden üblicherweise nach der Tätigkeit benannt die sie durchführen um sie von eher passiven Werten unterscheiden zu können. `text()` ist keine Tätigkeit, das ist also kein guter Funktionsname.
`counter` wird an eine 1 gebunden die nirgends verwendet wird.
Die Auswahl einer zufälligen, nicht-leeren Zelle wiederholt Code vor und in der Schleife. Da würde man besser eine ”Endlosschleife” schreiben, die bei entsprechender Bedingung dann per ``break`` verlassen wird.
`copyGrid` klingt wie eine Funktion, das sollte besser `grid_copy` heissen, beziehungsweise muss das auch gar nicht wirklich an einen Namen gebunden werden, denn das kann man mit einem Ausdruck erzeugen, statt da Listen und zusätzliche Namen zu verwenden.
`solveGrid()` sollte die Anzahl der Lösungen als Ergebnis liefern und keine globale Variable verändern.
In `solveGrid()` und `fillGrid()` wiederholt sich viel Code, das sollte nicht sein. Der gehört in eine oder mehrere eigene Funktionen.
Überarbeitete Version:
Grunddatentypen haben nichts in Namen verloren. Den Typen ändert man gar nicht so selten mal während der Programmentwicklung und dann muss man überall im Programm die betroffenen Namen ändern, oder man hat falsche, irreführende Namen im Quelltext.
Der Namenszusatz `my` macht keinen Sinn wenn es den gleichen Namen nicht auch mit `our` oder `their` gibt, gegen den sich das `my` irgendwie abgrenzen würde.
Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. ``global`` und globale Variablen verwendet man nicht. Alles was eine Funktion/Methode ausser Konstanten benötigt, bekommt sie als Argument(e) übergeben. In `fillGrid()` ist ``global`` sogar überflüssig, weil dort überhaupt nicht auf `counter` zugegriffen wird.
Kommentare vor Funktionen die beschreiben was die Funktion tut, sollten keine Kommentare sondern Docstrings sein.
Das erstellen des leeren Sodukofelds durch neun mal hinschreiben des gleichen `append()` mit literalen Listen mit jeweis neun Nullen ist nicht idiomatisch für Python. Mal abgesehen davon, dass man die gesamte verschachtelte Liste als literal hätte schreiben können, würde man Code schreiben der diese Datenstruktur erstellt, statt da mit viel Tipparbeit und/oder kopieren und einfügen zu arbeiten.
Statt ``not a in b`` würde man ``a not in b`` schreiben.
Statt beim Test ob der Wert schon in der Spalte vorkommt, jeden Index von 0 bis 8 manuell hin zu schreiben, würde man das in einer Schleife machen.
Für `square` ist zu viel Code verwendet worden. Statt der vielen ``if``-Bedingungen könnte man die entsprechenden Indizes auch einfach ausrechnen.
`square` wird an eine leere Liste gebunden die niemals irgendwo verwendet wird.
Es ist extrem verwirrend, dass in der Schleife mit der Laufvariablen `i` „list comprehensions“ stehen die ebenfalls `i` als Laufvariable verwenden. Auch wenn das ein eigener Namensraum ist, mag das dem menschlichen Leser nicht immer klar sein.
Die `fillGrid()`-Funktion gibt entweder `True` oder `None` zurück — das sollte `True` oder `False` sein.
`checkGrid()` lässt sich mit Comprehensions ohne Indexzugriffe und `any()` oder `all()` mit einem Ausdruck beschreiben.
`drawGrid()` braucht `myPen` als Argument statt das einfach so magisch aus der Umgebung zu verwenden.
Namen sollten keine kryptischen Abkürzungen enthalten, oder gar nur daraus bestehen. `intDim`?
Funktionen und Methoden werden üblicherweise nach der Tätigkeit benannt die sie durchführen um sie von eher passiven Werten unterscheiden zu können. `text()` ist keine Tätigkeit, das ist also kein guter Funktionsname.
`counter` wird an eine 1 gebunden die nirgends verwendet wird.
Die Auswahl einer zufälligen, nicht-leeren Zelle wiederholt Code vor und in der Schleife. Da würde man besser eine ”Endlosschleife” schreiben, die bei entsprechender Bedingung dann per ``break`` verlassen wird.
`copyGrid` klingt wie eine Funktion, das sollte besser `grid_copy` heissen, beziehungsweise muss das auch gar nicht wirklich an einen Namen gebunden werden, denn das kann man mit einem Ausdruck erzeugen, statt da Listen und zusätzliche Namen zu verwenden.
`solveGrid()` sollte die Anzahl der Lösungen als Ergebnis liefern und keine globale Variable verändern.
In `solveGrid()` und `fillGrid()` wiederholt sich viel Code, das sollte nicht sein. Der gehört in eine oder mehrere eigene Funktionen.
Überarbeitete Version:
Code: Alles auswählen
#!/usr/bin/env python3
import turtle
from random import randint, shuffle
from time import sleep
CELL_SIZE = 35
TOP_LEFT_X = -150
TOP_LEFT_Y = 150
def check_grid(grid):
"""
A function to check if the grid is full.
"""
return all(all(value != 0 for value in row) for row in grid)
def is_value_in_row(grid, value, row_index):
return value in grid[row_index]
def is_value_in_column(grid, value, column_index):
return any(value == row[column_index] for row in grid)
def is_value_in_square(grid, value, row_index, column_index):
y = row_index // 3 * 3
x = column_index // 3 * 3
return any(value in row[x : x + 3] for row in grid[y : y + 3])
def is_valid_value(grid, value, row_index, column_index):
return not (
is_value_in_row(grid, value, row_index)
or is_value_in_column(grid, value, column_index)
or is_value_in_square(grid, value, row_index, column_index)
)
def fill_grid(grid, numbers):
"""
A backtracking/recursive function to check all possible combinations of
numbers until a solution is found.
"""
for row_index, row in enumerate(grid):
for column_index, old_value in enumerate(row):
if old_value == 0:
shuffle(numbers)
for value in numbers:
if is_valid_value(grid, value, row_index, column_index):
row[column_index] = value
if check_grid(grid):
return True
if fill_grid(grid, numbers):
return True
row[column_index] = 0
return False
assert False
def solve_grid(grid, counter=0):
"""
A backtracking/recursive function to check all possible combinations of
numbers until a solution is found.
"""
for row_index, row in enumerate(grid):
for column_index, old_value in enumerate(row):
if old_value == 0:
for value in range(1, 10):
if is_valid_value(grid, value, row_index, column_index):
row[column_index] = value
if check_grid(grid):
counter += 1
break
counter = solve_grid(grid, counter)
row[column_index] = 0
return counter
assert False
def draw_text(pen, message, x, y, size):
pen.penup()
pen.goto(x, y)
pen.write(message, align="left", font=("Arial", size, "normal"))
def draw_grid(pen, grid):
"""
A procedure to draw the grid on screen using Python's `turtle` module.
"""
for i in range(0, 10):
pen.pensize(3 if i % 3 == 0 else 1)
pen.penup()
pen.goto(TOP_LEFT_X, TOP_LEFT_Y - i * CELL_SIZE)
pen.pendown()
pen.goto(TOP_LEFT_X + 9 * CELL_SIZE, TOP_LEFT_Y - i * CELL_SIZE)
pen.penup()
pen.goto(TOP_LEFT_X + i * CELL_SIZE, TOP_LEFT_Y)
pen.pendown()
pen.goto(TOP_LEFT_X + i * CELL_SIZE, TOP_LEFT_Y - 9 * CELL_SIZE)
for row_index, row in enumerate(grid):
for column_index, value in enumerate(row):
if value != 0:
draw_text(
pen,
value,
TOP_LEFT_X + column_index * CELL_SIZE + 9,
TOP_LEFT_Y - row_index * CELL_SIZE - CELL_SIZE + 8,
18,
)
def main():
grid = [[0] * 9 for _ in range(9)]
pen = turtle.Turtle()
turtle.tracer(0)
pen.speed(0)
pen.color("black")
pen.hideturtle()
fill_grid(grid, list(range(1, 10)))
draw_grid(pen, grid)
turtle.update()
sleep(1)
#
# Start Removing Numbers one by one.
#
# A higher number of attempts will end up removing more numbers from the
# grid potentially resulting in more difficiult grids to solve!
#
attempts = 5
while attempts:
#
# Select a random cell that is not already empty.
#
assert any(any(row) for row in grid)
while True:
row_index, column_index = randint(0, 8), randint(0, 8)
if grid[row_index][column_index] != 0:
break
#
# Remember the cell value in case we need to put it back.
#
removed_value = grid[row_index][column_index]
grid[row_index][column_index] = 0
solution_count = solve_grid(list(map(list, grid)))
if solution_count != 1:
grid[row_index][column_index] = removed_value
attempts -= 1
pen.clear()
draw_grid(pen, grid)
turtle.update()
print("Sudoku Grid Ready")
turtle.done()
if __name__ == "__main__":
main()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari