Пятнашка

Ципихович Эндрю

Активный пользователь
Пользователь
Мар 27, 2021
490
26
28
здравствуйте, вот нашёл в сети:
Код:
from random import shuffle
from tkinter import Canvas, Tk
 
BOARD_SIZE = 4
SQUARE_SIZE = 80
EMPTY_SQUARE = BOARD_SIZE ** 2
 
root = Tk()
root.title("Pythonicway Fifteen")
 
c = Canvas(root, width=BOARD_SIZE * SQUARE_SIZE,
           height=BOARD_SIZE * SQUARE_SIZE, bg='#808080')
c.pack()
 
 
def get_inv_count():
    """ Функция считающая количество перемещений """
    inversions = 0
    inversion_board = board[:]
    inversion_board.remove(EMPTY_SQUARE)
    for i in range(len(inversion_board)):
        first_item = inversion_board[i]
        for j in range(i+1, len(inversion_board)):
            second_item = inversion_board[j]
            if first_item > second_item:
                inversions += 1
    return inversions
 
 
def is_solvable():
    """ Функция определяющая имеет ли головоломка рещение """
    num_inversions = get_inv_count()
    if BOARD_SIZE % 2 != 0:
        return num_inversions % 2 == 0
    else:
        empty_square_row = BOARD_SIZE - (board.index(EMPTY_SQUARE) // BOARD_SIZE)
        if empty_square_row % 2 == 0:
            return num_inversions % 2 != 0
        else:
            return num_inversions % 2 == 0
 
 
def get_empty_neighbor(index):
    # получаем индекс пустой клетки в списке
    empty_index = board.index(EMPTY_SQUARE)
    # узнаем расстояние от пустой клетки до клетки по которой кликнули
    abs_value = abs(empty_index - index)
    # Если пустая клетка над или под клектой на которую кликнули
    # возвращаем индекс пустой клетки
    if abs_value == BOARD_SIZE:
        return empty_index
    # Если пустая клетка слева или справа
    elif abs_value == 1:
        # Проверяем, чтобы блоки были в одном ряду
        max_index = max(index, empty_index)
        if max_index % BOARD_SIZE != 0:
            return empty_index
    # Рядом с блоком не было пустого поля
    return index
 
 
def draw_board():
    # убираем все, что нарисовано на холсте
    c.delete('all')
    # Наша задача сгруппировать пятнашки из списка в квадрат
    # со сторонами BOARD_SIZE x BOARD_SIZE
    # i и j будут координатами для каждой отдельной пятнашки
    for i in range(BOARD_SIZE):
        for j in range(BOARD_SIZE):
            # получаем значение, которое мы должны будем нарисовать
            # на квадрате
            index = str(board[BOARD_SIZE * i + j])
            # если это не клетка которую мы хотим оставить пустой
            if index != str(EMPTY_SQUARE):
                # рисуем квадрат по заданным координатам
                c.create_rectangle(j * SQUARE_SIZE, i * SQUARE_SIZE,
                                   j * SQUARE_SIZE + SQUARE_SIZE,
                                   i * SQUARE_SIZE + SQUARE_SIZE,
                                   fill='#43ABC9',
                                   outline='#FFFFFF')
                # пишем число в центре квадрата
                c.create_text(j * SQUARE_SIZE + SQUARE_SIZE / 2,
                              i * SQUARE_SIZE + SQUARE_SIZE / 2,
                              text=index,
                              font="Arial {} italic".format(int(SQUARE_SIZE / 4)),
                              fill='#FFFFFF')
 
 
def show_vactory_plate():
    # Рисуем черный квадрат по центру поля
    c.create_rectangle(SQUARE_SIZE / 5,
                       SQUARE_SIZE * BOARD_SIZE / 2 - 10 * BOARD_SIZE,
                       BOARD_SIZE * SQUARE_SIZE - SQUARE_SIZE / 5,
                       SQUARE_SIZE * BOARD_SIZE / 2 + 10 * BOARD_SIZE,
                       fill='#000000',
                       outline='#FFFFFF')
    # Пишем красным текст Победа
    c.create_text(SQUARE_SIZE * BOARD_SIZE / 2, SQUARE_SIZE * BOARD_SIZE / 1.9,
                  text="ПОБЕДА!", font="Helvetica {} bold".format(int(10 * BOARD_SIZE)), fill='#DC143C')
 
 
def click(event):
    # Получаем координаты клика
    x, y = event.x, event.y
    # Конвертируем координаты из пикселей в клеточки
    x = x // SQUARE_SIZE
    y = y // SQUARE_SIZE
    # Получаем индекс в списке объекта по которому мы нажали
    board_index = x + (y * BOARD_SIZE)
    # Получаем индекс пустой клетки в списке. Эту функцию мы напишем позже
    empty_index = get_empty_neighbor(board_index)
    # Меняем местами пустую клетку и клетку, по которой кликнули
    board[board_index], board[empty_index] = board[empty_index], board[board_index]
    # Перерисовываем игровое поле
    draw_board()
    # Если текущее состояние доски соответствует правильному - рисуем сообщение о победе
    if board == correct_board:
        # Эту функцию мы добавим позже
        show_victory_plate()
 
 
c.bind('<Button-1>', click)
c.pack()
 
 
board = list(range(1, EMPTY_SQUARE + 1))
correct_board = board[:]
shuffle(board)
 
while not is_solvable():
    shuffle(board)
 
draw_board()
root.mainloop()
вопрос как сделать, чтобы при клике на кость 15-ки если есть у такая возможность, чтобы двигались две\три кости сразу?
 

Vershitel_sudeb

Vershitel sudeb
Команда форума
Модератор
Мар 17, 2021
973
220
43
21
Москва
Доработал, поменяв функцию click, так же активировал вывод победы, там название функции было не правильным
Изменения:
Python:
def click(event):
    global board
    # Получаем координаты клика
    x, y = event.x, event.y
    # Конвертируем координаты из пикселей в клеточки
    x = x // SQUARE_SIZE
    y = y // SQUARE_SIZE
    # Получаем индекс в списке объекта по которому мы нажали
    field = np.array(board).reshape((BOARD_SIZE, -1))
    # Получаем координаты пустой клетки
    zero_y, zero_x = map(int, np.where(field == EMPTY_SQUARE))
    # Если нажали на пустую клетку, ничего не делаем
    if x == zero_x and y == zero_y:
        return 0
    # Если нажали на клетку в том столбце, где находится пустая клетка
    if x == zero_x:
        # Определяем направление сдвигания клеток: 1 - вниз, -1 - вверх
        vector = -(zero_y-y)//abs(zero_y-y)
        # Проходим по всем клеткам от пустой до нажатой сдигая фишки
        for y_pos in range(zero_y, y, vector):
            field[y_pos][x] = field[y_pos+vector][x]
    # Если нажали на клетку в той строке, где находится пустая клетка
    elif y == zero_y:
        # Определяем направление сдвигания клеток: 1 - вправо, -1 - влево
        vector = -(zero_x-x)//abs(zero_x-x)
        # Проходим по всем клеткам от пустой до нажатой сдигая фишки
        for x_pos in range(zero_x, x, vector):
            field[y][x_pos] = field[y][x_pos+vector]
    # Если нажали на клетку которая не в строке или стобце с пустой клеткой, ничего не делаем
    else:
        return 0
    # Ставим нажатую пустой
    field[y][x] = EMPTY_SQUARE
    board = list(field.ravel())
    draw_board()
    # Если текущее состояние доски соответствует правильному - рисуем сообщение о победе
    if board == correct_board:
        # Эту функцию мы добавим позже
        show_victory_plate()

Python:
from random import shuffle
from tkinter import Canvas, Tk
import numpy as np
BOARD_SIZE = 4
SQUARE_SIZE = 80
EMPTY_SQUARE = BOARD_SIZE ** 2
root = Tk()
root.title("Pythonicway Fifteen")
c = Canvas(root, width=BOARD_SIZE * SQUARE_SIZE,
           height=BOARD_SIZE * SQUARE_SIZE, bg='#808080')
c.pack()

def get_inv_count():
    """ Функция считающая количество перемещений """
    inversions = 0
    inversion_board = board[:]
    inversion_board.remove(EMPTY_SQUARE)
    for i in range(len(inversion_board)):
        first_item = inversion_board[i]
        for j in range(i+1, len(inversion_board)):
            second_item = inversion_board[j]
            if first_item > second_item:
                inversions += 1
    return inversions

def is_solvable():
    """ Функция определяющая имеет ли головоломка рещение """
    num_inversions = get_inv_count()
    if BOARD_SIZE % 2 != 0:
        return num_inversions % 2 == 0
    else:
        empty_square_row = BOARD_SIZE - \
            (board.index(EMPTY_SQUARE) // BOARD_SIZE)
        if empty_square_row % 2 == 0:
            return num_inversions % 2 != 0
        else:
            return num_inversions % 2 == 0

def get_empty_neighbor(index):
    # получаем индекс пустой клетки в списке
    empty_index = board.index(EMPTY_SQUARE)
    # узнаем расстояние от пустой клетки до клетки по которой кликнули
    abs_value = abs(empty_index - index)
    # Если пустая клетка над или под клектой на которую кликнули
    # возвращаем индекс пустой клетки
    if abs_value == BOARD_SIZE:
        return empty_index
    # Если пустая клетка слева или справа
    elif abs_value == 1:
        # Проверяем, чтобы блоки были в одном ряду
        max_index = max(index, empty_index)
        if max_index % BOARD_SIZE != 0:
            return empty_index
    # Рядом с блоком не было пустого поля
    return index

def draw_board():
    # убираем все, что нарисовано на холсте
    c.delete('all')
    # Наша задача сгруппировать пятнашки из списка в квадрат
    # со сторонами BOARD_SIZE x BOARD_SIZE
    # i и j будут координатами для каждой отдельной пятнашки
    for i in range(BOARD_SIZE):
        for j in range(BOARD_SIZE):
            # получаем значение, которое мы должны будем нарисовать
            # на квадрате
            index = str(board[BOARD_SIZE * i + j])
            # если это не клетка которую мы хотим оставить пустой
            if index != str(EMPTY_SQUARE):
                # рисуем квадрат по заданным координатам
                c.create_rectangle(j * SQUARE_SIZE, i * SQUARE_SIZE,
                                   j * SQUARE_SIZE + SQUARE_SIZE,
                                   i * SQUARE_SIZE + SQUARE_SIZE,
                                   fill='#43ABC9',
                                   outline='#FFFFFF')
                # пишем число в центре квадрата
                c.create_text(j * SQUARE_SIZE + SQUARE_SIZE / 2,
                              i * SQUARE_SIZE + SQUARE_SIZE / 2,
                              text=index,
                              font="Arial {} italic".format(
                                  int(SQUARE_SIZE / 4)),
                              fill='#FFFFFF')

def show_victory_plate():
    # Рисуем черный квадрат по центру поля
    c.create_rectangle(SQUARE_SIZE / 5,
                       SQUARE_SIZE * BOARD_SIZE / 2 - 10 * BOARD_SIZE,
                       BOARD_SIZE * SQUARE_SIZE - SQUARE_SIZE / 5,
                       SQUARE_SIZE * BOARD_SIZE / 2 + 10 * BOARD_SIZE,
                       fill='#000000',
                       outline='#FFFFFF')
    # Пишем красным текст Победа
    c.create_text(SQUARE_SIZE * BOARD_SIZE / 2, SQUARE_SIZE * BOARD_SIZE / 1.9,
                  text="ПОБЕДА!", font="Helvetica {} bold".format(int(10 * BOARD_SIZE)), fill='#DC143C')

def click(event):
    global board
    # Получаем координаты клика
    x, y = event.x, event.y
    # Конвертируем координаты из пикселей в клеточки
    x = x // SQUARE_SIZE
    y = y // SQUARE_SIZE
    # Получаем индекс в списке объекта по которому мы нажали
    field = np.array(board).reshape((BOARD_SIZE, -1))
    # Получаем координаты пустой клетки
    zero_y, zero_x = map(int, np.where(field == EMPTY_SQUARE))
    # Если нажали на пустую клетку, ничего не делаем
    if x == zero_x and y == zero_y:
        return 0
    # Если нажали на клетку в том столбце, где находится пустая клетка
    if x == zero_x:
        # Определяем направление сдвигания клеток: 1 - вниз, -1 - вверх
        vector = -(zero_y-y)//abs(zero_y-y)
        # Проходим по всем клеткам от пустой до нажатой сдигая фишки
        for y_pos in range(zero_y, y, vector):
            field[y_pos][x] = field[y_pos+vector][x]
    # Если нажали на клетку в той строке, где находится пустая клетка
    elif y == zero_y:
        # Определяем направление сдвигания клеток: 1 - вправо, -1 - влево
        vector = -(zero_x-x)//abs(zero_x-x)
        # Проходим по всем клеткам от пустой до нажатой сдигая фишки
        for x_pos in range(zero_x, x, vector):
            field[y][x_pos] = field[y][x_pos+vector]
    # Если нажали на клетку которая не в строке или стобце с пустой клеткой, ничего не делаем
    else:
        return 0
    # Ставим нажатую пустой
    field[y][x] = EMPTY_SQUARE
    board = list(field.ravel())
    draw_board()
    # Если текущее состояние доски соответствует правильному - рисуем сообщение о победе
    if board == correct_board:
        # Эту функцию мы добавим позже
        show_victory_plate()

c.bind('<Button-1>', click)
c.pack()

board = list(range(1, EMPTY_SQUARE + 1))
correct_board = board[:]
shuffle(board)
while not is_solvable():
    shuffle(board)
draw_board()
root.mainloop()
 

Ципихович Эндрю

Активный пользователь
Пользователь
Мар 27, 2021
490
26
28
спасибо, пока NameError: name 'np' is not defined
 

Vershitel_sudeb

Vershitel sudeb
Команда форума
Модератор
Мар 17, 2021
973
220
43
21
Москва
спасибо, пока NameError: name 'np' is not defined
import numpy as np
 

Ципихович Эндрю

Активный пользователь
Пользователь
Мар 27, 2021
490
26
28
хорошо, посему-то в конце, когда сложил имеется ошибка
NameError: name 'show_victory_plate' is not defined
как исправить?
 

Ципихович Эндрю

Активный пользователь
Пользователь
Мар 27, 2021
490
26
28
ну и потом буду пробовать реализовать из моей темы:
Если нужно показывать html внутри gui, то есть cefpython (он поддерживает Tkinter, wxPython и PyQT и другие gui). Если нужно отображать форму из gui в браузере (в виде html) можно попробовать PySimpleGUIWeb.
 

Vershitel_sudeb

Vershitel sudeb
Команда форума
Модератор
Мар 17, 2021
973
220
43
21
Москва
хорошо, посему-то в конце, когда сложил имеется ошибка
NameError: name 'show_victory_plate' is not defined
как исправить?
Там функция что-то типа show_vactory_plate, на одну букву отличается, найди её и назови правильно
 

Ципихович Эндрю

Активный пользователь
Пользователь
Мар 27, 2021
490
26
28
ок, работает как надо, выложу весь финальный код для потомков:
Python:
from random import shuffle
from tkinter import Canvas, Tk
import numpy as np

BOARD_SIZE = 4
SQUARE_SIZE = 80
EMPTY_SQUARE = BOARD_SIZE ** 2

root = Tk()
root.title("Pythonicway Fifteen")

c = Canvas(root, width=BOARD_SIZE * SQUARE_SIZE,
           height=BOARD_SIZE * SQUARE_SIZE, bg='#808080')
c.pack()


def get_inv_count():
    """ Функция считающая количество перемещений """
    inversions = 0
    inversion_board = board[:]
    inversion_board.remove(EMPTY_SQUARE)
    for i in range(len(inversion_board)):
        first_item = inversion_board[i]
        for j in range(i + 1, len(inversion_board)):
            second_item = inversion_board[j]
            if first_item > second_item:
                inversions += 1
    return inversions


def is_solvable():
    """ Функция определяющая имеет ли головоломка рещение """
    num_inversions = get_inv_count()
    if BOARD_SIZE % 2 != 0:
        return num_inversions % 2 == 0
    else:
        empty_square_row = BOARD_SIZE - (board.index(EMPTY_SQUARE) // BOARD_SIZE)
        if empty_square_row % 2 == 0:
            return num_inversions % 2 != 0
        else:
            return num_inversions % 2 == 0


def get_empty_neighbor(index):
    # получаем индекс пустой клетки в списке
    empty_index = board.index(EMPTY_SQUARE)
    # узнаем расстояние от пустой клетки до клетки по которой кликнули
    abs_value = abs(empty_index - index)
    # Если пустая клетка над или под клектой на которую кликнули
    # возвращаем индекс пустой клетки
    if abs_value == BOARD_SIZE:
        return empty_index
    # Если пустая клетка слева или справа
    elif abs_value == 1:
        # Проверяем, чтобы блоки были в одном ряду
        max_index = max(index, empty_index)
        if max_index % BOARD_SIZE != 0:
            return empty_index
    # Рядом с блоком не было пустого поля
    return index


def draw_board():
    # убираем все, что нарисовано на холсте
    c.delete('all')
    # Наша задача сгруппировать пятнашки из списка в квадрат
    # со сторонами BOARD_SIZE x BOARD_SIZE
    # i и j будут координатами для каждой отдельной пятнашки
    for i in range(BOARD_SIZE):
        for j in range(BOARD_SIZE):
            # получаем значение, которое мы должны будем нарисовать
            # на квадрате
            index = str(board[BOARD_SIZE * i + j])
            # если это не клетка которую мы хотим оставить пустой
            if index != str(EMPTY_SQUARE):
                # рисуем квадрат по заданным координатам
                c.create_rectangle(j * SQUARE_SIZE, i * SQUARE_SIZE,
                                   j * SQUARE_SIZE + SQUARE_SIZE,
                                   i * SQUARE_SIZE + SQUARE_SIZE,
                                   fill='#43ABC9',
                                   outline='#FFFFFF')
                # пишем число в центре квадрата
                c.create_text(j * SQUARE_SIZE + SQUARE_SIZE / 2,
                              i * SQUARE_SIZE + SQUARE_SIZE / 2,
                              text=index,
                              font="Arial {} italic".format(int(SQUARE_SIZE / 4)),
                              fill='#FFFFFF')


def show_victory_plate():
    # Рисуем черный квадрат по центру поля
    c.create_rectangle(SQUARE_SIZE / 5,
                       SQUARE_SIZE * BOARD_SIZE / 2 - 10 * BOARD_SIZE,
                       BOARD_SIZE * SQUARE_SIZE - SQUARE_SIZE / 5,
                       SQUARE_SIZE * BOARD_SIZE / 2 + 10 * BOARD_SIZE,
                       fill='#000000',
                       outline='#FFFFFF')
    # Пишем красным текст Победа
    c.create_text(SQUARE_SIZE * BOARD_SIZE / 2, SQUARE_SIZE * BOARD_SIZE / 1.9,
                  text="ПОБЕДА!", font="Helvetica {} bold".format(int(10 * BOARD_SIZE)), fill='#DC143C')


def click(event):
    global board
    # Получаем координаты клика
    x, y = event.x, event.y
    # Конвертируем координаты из пикселей в клеточки
    x = x // SQUARE_SIZE
    y = y // SQUARE_SIZE
    # Получаем индекс в списке объекта по которому мы нажали
    field = np.array(board).reshape((BOARD_SIZE, -1))
    # Получаем координаты пустой клетки
    zero_y, zero_x = map(int, np.where(field == EMPTY_SQUARE))
    # Если нажали на пустую клетку, ничего не делаем
    if x == zero_x and y == zero_y:
        return 0
    # Если нажали на клетку в том столбце, где находится пустая клетка
    if x == zero_x:
        # Определяем направление сдвигания клеток: 1 - вниз, -1 - вверх
        vector = -(zero_y - y) // abs(zero_y - y)
        # Проходим по всем клеткам от пустой до нажатой сдигая фишки
        for y_pos in range(zero_y, y, vector):
            field[y_pos][x] = field[y_pos + vector][x]
    # Если нажали на клетку в той строке, где находится пустая клетка
    elif y == zero_y:
        # Определяем направление сдвигания клеток: 1 - вправо, -1 - влево
        vector = -(zero_x - x) // abs(zero_x - x)
        # Проходим по всем клеткам от пустой до нажатой сдигая фишки
        for x_pos in range(zero_x, x, vector):
            field[y][x_pos] = field[y][x_pos + vector]
    # Если нажали на клетку которая не в строке или стобце с пустой клеткой, ничего не делаем
    else:
        return 0
    # Ставим нажатую пустой
    field[y][x] = EMPTY_SQUARE
    board = list(field.ravel())
    draw_board()
    # Если текущее состояние доски соответствует правильному - рисуем сообщение о победе
    if board == correct_board:
        # Эту функцию мы добавим позже
        show_victory_plate()


c.bind('<Button-1>', click)
c.pack()

board = list(range(1, EMPTY_SQUARE + 1))
correct_board = board[:]
shuffle(board)

while not is_solvable():
    shuffle(board)

draw_board()
root.mainloop()
 
Последнее редактирование модератором:

Vershitel_sudeb

Vershitel sudeb
Команда форума
Модератор
Мар 17, 2021
973
220
43
21
Москва
Импорт принято делать в начале кода, только если есть большая вероятность что он не понадобится, надо делать его где-то в другом месте.
И лучше не в функции, которая вызывается много раз
 

Ципихович Эндрю

Активный пользователь
Пользователь
Мар 27, 2021
490
26
28
конечно знал об этом почему так5 сделал? видимо поспешил
а можете под нижней стороной квадрата пятнашки добавить кнопку "Начать новую игру" с этим функционалом? спасибо
пысы
почему не после выигрыша? -иногда нет решения.................
 

Vershitel_sudeb

Vershitel sudeb
Команда форума
Модератор
Мар 17, 2021
973
220
43
21
Москва
конечно знал об этом почему так5 сделал? видимо поспешил
а можете под нижней стороной квадрата пятнашки добавить кнопку "Начать новую игру" с этим функционалом? спасибо
пысы
почему не после выигрыша? -иногда нет решения.................
Вообще-то решение должно быть всегда, там специально есть функция проворящая наличие решений. Если найдешь нерешаемый вариант - кинь скрин
 

Ципихович Эндрю

Активный пользователь
Пользователь
Мар 27, 2021
490
26
28
да конечно решение есть всегда, так как можно по кругу крутить - крутить и выкружить нужное..........
просто иногда надоедает
 

Форум IT Специалистов