Externer Code funktioniert nicht

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Player2022
User
Beiträge: 4
Registriert: Mittwoch 13. Juli 2022, 22:16

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.
Benutzeravatar
__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
Player2022
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.
Benutzeravatar
Dennis89
User
Beiträge: 1562
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

@Player2022 die Antwort von __blackjack__ ist so zu verstehen:
In dem Code steht in Zeile 19

Code: Alles auswählen

myPen.tracer(0)
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

Code: Alles auswählen

turtle.tracer(0)
ersetzen und so läufts bei mir mit Python 3.9 auch.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Player2022
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
Player2022
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?
Benutzeravatar
__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
Benutzeravatar
__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:

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
Antworten