Рубрики
Без рубрики

15 головоломка с Python Turtle Graphics

Реализация Python из классической 15 головоломки с использованием графики черепахи. Развивайте свои навыки программирования Python с этим забавным проектом.

Автор оригинала: Robin Andrews.

Около 40 лет назад произошла Craze, который взлетел по всему миру. Это была трехмерная головоломка называется Куб Рубика которые держали людей в состоянии полуторака, в то время как они пытались получить кубик в выигрышную конфигурацию.

Странным уважением истории, почти ровно 100 годов до кубика Рубика Cube, на этот раз был еще одна головоломка, на этот раз для 15 головоломки Отказ В некоторых отношениях это было как двумерная версия куба Rubik. Тем не менее, был поворот – в зависимости от начальной конфигурации 15 головоломка может быть неразрешимый Отказ Были предложены призы, которые никогда не смогут честно победить, и люди втянули свои мозги, чтобы найти решение, где никто не существует.

В этой статье я хочу поделиться Python Реализация 15 головоломки. Это разрешимая версия – но я думаю, что вы чувствуете, что вы можете изменить код и делиться игрой с кем вы хотите раздражать!

Программирование 15 головоломки с Python

Мы будем использовать Python Графический модуль черепахи Для этой программы вместе с крошечным битом Tkinter , который является библиотекой графического интерфейса GUI (графический пользовательский интерфейс), на которой построен модуль черепахи. Вам понадобится фактическая установка Python, чтобы сделать эту работу, а не версию на основе браузера.

Вот список:

import turtle
import tkinter as tk
import random

NUM_ROWS = 4  # Max 4
NUM_COLS = 4  # Max 4
TILE_WIDTH = 90  # Actual image size
TILE_HEIGHT = 90  # Actual image size
FONT_SIZE = 24
FONT = ('Helvetica', FONT_SIZE, 'normal')
SCRAMBLE_DEPTH = 100

images = []
for i in range(NUM_ROWS * NUM_COLS - 1):
    file = f"number-images/{i+1}.gif" # Use `.format()` instead if needed.
    images.append(file)

images.append("number-images/empty.gif")
images.append("number-images/scramble.gif")


def register_images():
    global screen
    for i in range(len(images)):
        screen.addshape(images[i])


def index_2d(my_list, v):
    """Returns the position of an element in a 2D list."""
    for i, x in enumerate(my_list):
        if v in x:
            return (i, x.index(v))


def swap_tile(tile):
    """Swaps the position of the clicked tile with the empty tile."""
    global screen

    current_i, current_j = index_2d(board, tile)
    empty_i, empty_j = find_empty_square_pos()
    empty_square = board[empty_i][empty_j]

    if is_adjacent([current_i, current_j], [empty_i, empty_j]):
        temp = board[empty_i][empty_j]
        board[empty_i][empty_j] = tile
        board[current_i][current_j] = temp

        draw_board()


def is_adjacent(el1, el2):
    """Determines whether two elements in a 2D array are adjacent."""
    if abs(el2[1] - el1[1]) == 1 and abs(el2[0] - el1[0]) == 0:
        return True
    if abs(el2[0] - el1[0]) == 1 and abs(el2[1] - el1[1]) == 0:
        return True
    return False


def find_empty_square_pos():
    """Returns the position of the empty square."""
    global board
    for row in board:
        for candidate in row:
            if candidate.shape() == "number-images/empty.gif":
                empty_square = candidate

    return index_2d(board, empty_square)


def scramble_board():
    """Scrambles the board in a way that leaves it solvable."""
    global board, screen

    for i in range(SCRAMBLE_DEPTH):
        for row in board:
            for candidate in row:
                if candidate.shape() == "number-images/empty.gif":
                    empty_square = candidate

        empty_i, empty_j = find_empty_square_pos()
        directions = ["up", "down", "left", "right"]

        if empty_i == 0: directions.remove("up")
        if empty_i >= NUM_ROWS - 1: directions.remove("down")
        if empty_j == 0: directions.remove("left")
        if empty_j >= NUM_COLS - 1: directions.remove("right")

        direction = random.choice(directions)

        if direction == "up": swap_tile(board[empty_i - 1][empty_j])
        if direction == "down": swap_tile(board[empty_i + 1][empty_j])
        if direction == "left": swap_tile(board[empty_i][empty_j - 1])
        if direction == "right": swap_tile(board[empty_i][empty_j + 1])


def draw_board():
    global screen, board

    # Disable animation
    screen.tracer(0)

    for i in range(NUM_ROWS):
        for j in range(NUM_COLS):
            tile = board[i][j]
            tile.showturtle()
            tile.goto(-138 + j * (TILE_WIDTH + 2), 138 - i * (TILE_HEIGHT + 2))

    # Restore animation
    screen.tracer(1)


def create_tiles():
    """
    Creates and returns a 2D list of tiles based on turtle objects
    in the winning configuration.
    """
    board = [["#" for _ in range(NUM_COLS)] for _ in range(NUM_ROWS)]

    for i in range(NUM_ROWS):
        for j in range(NUM_COLS):
            tile_num = NUM_COLS * i + j
            tile = turtle.Turtle(images[tile_num])
            tile.penup()
            board[i][j] = tile

            def click_callback(x, y, tile=tile):
                """Passes `tile` to `swap_tile()` function."""
                return swap_tile(tile)

            tile.onclick(click_callback)

    return board


def create_scramble_button():
    """Uses a turtle with an image as a button."""
    global screen
    print(images)
    button = turtle.Turtle(images[NUM_ROWS * NUM_COLS])
    button.penup()
    button.goto(0, -240)
    button.onclick(lambda x, y: scramble_board())


def create_scramble_button_tkinter():
    """An alternative approach to creating a button using Tkinter."""
    global screen
    canvas = screen.getcanvas()
    button = tk.Button(canvas.master, text="Scramble", background="cadetblue", foreground="white", bd=0,
                       command=scramble_board)
    canvas.create_window(0, -240, window=button)


def main():
    global screen, board

    # Screen setup
    screen = turtle.Screen()
    screen.setup(600, 600)
    screen.title("15 Puzzle")
    screen.bgcolor("aliceblue")
    screen.tracer(0)  # Disable animation
    register_images()

    # Initialise game and display
    board = create_tiles()
    create_scramble_button_tkinter()
    # create_scramble_button()
    scramble_board()
    draw_board()
    screen.tracer(1)  # Restore animation


main()
turtle.done()

Вам понадобятся изображения, чтобы сделать эту работу – вы можете загрузить их (и код) из здесь Отказ

Пара точек о коде:

  • Игра Логика и дисплей плотно связаны в этой реализации, что не всегда является отличной идеей – я был стремиться к тому, чтобы играть в игру, так что было поспешно с планировкой.

  • Я предоставил два способа реализовать карабкаться кнопка. Tkinter Версия может обеспечить нежное введение в Черепаха родительская библиотека

  • Существует техника для передачи дополнительных параметров к обратным вызовам клики, которые я обсуждаю в Эта статья о игре 21

  • В коде есть небольшое количество повторений, которое я могу рефакторировать в какой-то момент, но на данный момент, насколько я могу сказать, у вас есть рабочая версия 15 головоломки играть с.

Решение 15 головоломки

Я не хочу лишать вас от удовольствия от решения этой головоломки для себя. Что я есть Готово сделано возможным работать с меньшими версией, чтобы развить стратегии и разрабатывать, что возможно. Для этого просто измените константы в верхней части кода. Одна концепция, которую я нахожу полезно подумать, когда пытаясь быть более эффективным, чем просто случайно движущиеся квадраты, это циклы – Посмотрите, можете ли вы увидеть какие-либо циклы, происходящие, когда вы пытаетесь решить эту головоломку.

Так что это была моя реализация 15 головоломки в Python. Я надеюсь, что вы нашли это приятным. Если вы хотите вызов, попробуйте написать код для игры в игру. И дайте мне знать, как вы находитесь с стратегией или кодом в комментариях ниже.

Счастливая головоломка!