Grafy w Pythonie – SUDOKU

su = [
    [ 1, None, 2, 7 , None, None, 4, None, 5],
    [ None, None, None, None, None, 3, 6, None, None],
    [None, None, 4, 8, None, None, None, None, None],
    [8, None, None, 4, None, None, None, 1, 2],
    [None, None, None, None, None, 2, None, None, None],
    [3, None, None, None, None, 8, None, None, 4],
    [5, None, None, None, None, None, 1, 6, None],
    [None, 6, None, None, None, 9, None, 3, 7],
    [None, None, 3, None, None, None, None, None, None]
]



su = [
    [None, None, 4, None, 3, None, 6, None, None],
    [None, None, 7, None, None, 4, 3, None, None],
    [None, None, None, 8, None, 1, 9, None, None],
    [None, 9, None, 7, None, 5, None, 1, None],
    [7, None, 3, 2, None, None, None, 6, None],
    [5, None, None, None, None, None, None, None, None],
    [4, None, None, None, 6, None, None, None, None],
    [3, None, None, None, 2, None, 5, 9, None],
    [2, None, 5, None, None, None, None, 8, None]
]

# czym są kolory w rozwiązywaniu sudoku? To po prostu liczby, ktore wpisujemy w kratki!
# ile takich kolorów ma być? Tyle ile pól w sudoku. Zakladajac ze mamy sudoku 9x9, to liczba
# kolorów wynosi 81

colors = []
for p in su:
    colors.extend(p)

# Do rozwiazania sudoku potrzebujemy grafu, ktory określa jakie pola ze sobą "sąsiadują"
# Słowo "sąsiadują" oznacza w tym przypadku
#      pola w tym samym wierszu
#      pola w tej samej kolumnie
#      pola w tym samym kwadracie
# Do ustalenia, które pola są ze sobą w tej relacji przyda się kilka stałych wartości:

SIZE3 = 3
SIZE9 = SIZE3 * SIZE3
SIZE27 = SIZE3 * SIZE9
SIZE81 = SIZE3 * SIZE27
number_of_colors = SIZE9

# generowanie grafu - początkowo graf jest pusty, ale zaraz będą zaznaczane polaczenia pomiędzy polami\

graph = [ [0] * SIZE81 for x in range(SIZE81) ]

# zaczyna się zabawa. Trzeba ustalić, które pola są w tym samym wierszu/kolumnie/kwadracie
# przgladamy każde pole i ustalamy relacje

for row in range(SIZE81):
    for col in range(SIZE81):

        # relacja do wszystkich w tym samym wierszu
        if col % SIZE9 == row % SIZE9:
            graph[row][col] = 1

        # relacja do wszystkich w tej samej kolumnie
        if col // SIZE9 == row // SIZE9:
            graph[row][col] = 1
        
        # relacja dla wszystkich w tym samym kwadracie
        mini_box_horizontal_row = (row // SIZE27) * SIZE27
        mini_box_horizontal_col = (col // SIZE27) * SIZE27
        mini_box_vertical_row = (row % SIZE9 ) // SIZE3
        mini_box_vertical_col = (col % SIZE9 ) // SIZE3

        if mini_box_horizontal_row == mini_box_horizontal_col and mini_box_vertical_row == mini_box_vertical_col:
            graph[row][col] = 1

        # komórka nie jest w relacji sama do siebie:
        if row == col:
            graph[row][col] = 0



# funkcje służące do kolorowania grafu, tak jak bylo to pokazane na kursie
# tutaj funkcja sprawdzająca, czy pomalowanie węzła na określony kolor spowodowałoby konflikt

def is_no_conflict(graph, vertex_idx, colors, color):
    for i in range(len(graph)):
        if graph[vertex_idx][i] == 1 and colors[i] == color:
            return False
    return True

# funkcja służąca do kolorowania grafu. W porownaniu do funkcji z kursu mamy tu mala zmiane.
# Część pól w sudoku ma już swoją określoną wartość, dlatego w tych polach nie próbujemy 
# odgadnąć wartości - bierzemy ją tak jak jest

def try_paint(graph, number_of_colors, colors, vertex_idx):

    # jesli jestesmy na ostatnim polu to koniec
    if vertex_idx == len(graph):
        return True

    # jesli aktualna komorka jeszcze nie ma przypisanej liczby-koloru
    if colors[vertex_idx] == None: 
        # probujemy kolorowac kazdym kolorem po kolei - w koncu trafimy!
        for color in range(1,number_of_colors+1):
            colors[vertex_idx] = color
            # sprawdzamy czy nie ma konfliktu i jeśli ok, to zwrócimy wartość dalszych prób kolorowania   
            if is_no_conflict(graph, vertex_idx, colors, color):
                # probujemy dalej kolorowac
                if try_paint(graph, number_of_colors, colors, vertex_idx + 1):
                    return True
            # jesli jestemy tutaj, to nowy kolor spowodowal konflikt i... trzeba sie wycofac z tego koloru
            # i w kolejnej iteracji petli zastosować następny kolor
            colors[vertex_idx] = None
    else:
        # tu juz mam kolor i nie musze zgadywac
        return try_paint(graph, number_of_colors, colors, vertex_idx + 1)

    # jesli jestem tutaj, to kolorowanie nie udalo sie i trzeba wyprobowac inny kolor gdzies wczesniej
    return False



# probujemy kolorowania, czyli wypeniania grafu
result = try_paint(graph, number_of_colors, colors, 0)

# jesli udalo sie znalezc rozwiazanie to je wyswietl
if result:
    print(f"Solution found: {colors}")
    start = 0
    while start < SIZE81:
        print(colors[start:start+SIZE9])
        start += SIZE9
else:
    print("Solution not found.")