Автор оригинала: Pankaj Kumar.
Создайте MineSweeper, используя Python из основных для продвинутых
В этой статье мы будем проходить шаги по созданию нашего собственного терминального минселлера, используя язык Python.
О игре
MineSweeper – это однопользовательская игра, в которой игрок должен очистить квадратную сетку, содержащую шахты и цифры. Игрок должен помешать себе посадить на шахту с помощью номеров в соседних плитках.
GamePlay Demo.
После нескольких часов создания игры MineSweeper.
Проектирование MineSweeper с помощью Python
Перед созданием игровой логики нам нужно разработать основной расположение игры. Квадратная сетка довольно легко создать с помощью Python by:
# Printing the Minesweeper Layout def print_mines_layout(): global mine_values global n print() print("\t\t\tMINESWEEPER\n") st = " " for i in range(n): st = st + " " + str(i + 1) print(st) for r in range(n): st = " " if r == 0: for col in range(n): st = st + "______" print(st) st = " " for col in range(n): st = st + "| " print(st + "|") st = " " + str(r + 1) + " " for col in range(n): st = st + "| " + str(mine_values[r][col]) + " " print(st + "|") st = " " for col in range(n): st = st + "|_____" print(st + '|') print()
Сетка, отображаемая в каждой итерации, напоминает следующий рисунок:
«М»
Символ обозначает наличие «моего» в этой клетке. Как мы видим четко, любое число на сетке обозначает количество шахт, присутствующих в соседних «восьми» клетках.
Использование переменных, таких как, mine_values
будет объяснено дальше в руководстве.
Входная система
Одна из самых важных частей любой игры поддерживает метод ввода. В нашей версии MineSweeper мы будем использовать номера строки и столбцов для нашей методики ввода.
Перед началом игры скрипт должен предоставить набор инструкций для игрока. Наша игра печатает следующее.
Номера строки и столбцов, отображаемые вместе с сеткой, полезны для нашей системы ввода. Как мы знаем, отслеживание шахт без какого-либо индикатора может быть сложно. Поэтому MineSweeper имеет предоставление использования «флага», чтобы пометить клетки, которые мы знаем, содержит шахту.
Хранилище данных
Для одной игры MineSweeper нам нужно отслеживать следующую информацию:
- Размер сетки.
- Количество шахт Отказ
- «Фактические» значения сетки – В начале игры нам нужен контейнер для хранения реальных ценностей для игры, неизвестен игроку. Например, местоположение мин.
- «Очевидные» значения сетки – После каждого перемещения нам нужно обновить все значения, которые должны быть показаны игроку.
- Помеченные позиции – клетки, которые были помечены.
Эти значения хранятся с использованием следующих структур данных
if __name__ == "__main__": # Size of grid n = 8 # Number of mines mines_no = 8 # The actual values of the grid numbers = [[0 for y in range(n)] for x in range(n)] # The apparent values of the grid mine_values = [[' ' for y in range(n)] for x in range(n)] # The positions that have been flagged flags = []
Там не так много в игре-логике MineSweeper. Все усилия должны быть сделаны при настройке макета MineSeeper.
Настройка шахт
Нам нужно установить позиции шахт случайным образом, чтобы игрок мог не предсказать свои позиции. Это может быть сделано:
# Function for setting up Mines def set_mines(): global numbers global mines_no global n # Track of number of mines already set up count = 0 while count < mines_no: # Random number from all possible grid positions val = random.randint(0, n*n-1) # Generating row and column from the number r = val // n col = val % n # Place the mine, if it doesn't already have one if numbers[r][col] != -1: count = count + 1 numbers[r][col] = -1
В коде мы выбираем случайное число из всех возможных ячеек в сетке. Мы продолжаем делать это, пока мы не получим указанное количество шахт.
Примечание: Фактическое значение для шахты хранится как -1, тогда как значения, хранящиеся для отображения, обозначают шахту как «М»
Отказ
Примечание: «Рэннт» Функция может использоваться только после импорта случайной библиотеки. Это сделано путем написания «Импорт случайных»
В начале программы.
Настройка номеров сетки
Для каждой клетки в сетке мы должны проверить все соседние соседи, есть ли шахта или нет. Это делается:
# Function for setting up the other grid values def set_values(): global numbers global n # Loop for counting each cell value for r in range(n): for col in range(n): # Skip, if it contains a mine if numbers[r][col] == -1: continue # Check up if r > 0 and numbers[r-1][col] == -1: numbers[r][col] = numbers[r][col] + 1 # Check down if r < n-1 and numbers[r+1][col] == -1: numbers[r][col] = numbers[r][col] + 1 # Check left if col > 0 and numbers[r][col-1] == -1: numbers[r][c] = numbers[r][c] + 1 # Check right if col < n-1 and numbers[r][col+1] == -1: numbers[r][col] = numbers[r][col] + 1 # Check top-left if r > 0 and col > 0 and numbers[r-1][col-1] == -1: numbers[r][col] = numbers[r][col] + 1 # Check top-right if r > 0 and col < n-1 and numbers[r-1][col+1]== -1: numbers[r][col] = numbers[r][col] + 1 # Check below-left if r < n-1 and col > 0 and numbers[r+1][col-1]== -1: numbers[r][col] = numbers[r][col] + 1 # Check below-right if r < n-1 and col< n-1 and numbers[r+1][col+1]==-1: numbers[r][col] = numbers[r][col] + 1
Эти значения должны быть скрыты от игрока, поэтому они хранятся в Числа
Переменная.
Игровая петля
Игровой цикл – очень важная часть игры. Нужно обновлять каждый ход игрока, а также завершение игры.
# Set the mines set_mines() # Set the values set_values() # Display the instructions instructions() # Variable for maintaining Game Loop over = False # The GAME LOOP while not over: print_mines_layout()
В каждой итерации цикла MineSweeper Grid должен быть отображен, а также шаг игрока должен быть обработан.
Обрабатывать ввод игрока
Как мы упоминали ранее, есть два вида игрока ввода:
# Input from the user inp = input("Enter row number followed by space and column number = ").split()
Стандартный ввод
В нормальном моменте движения указаны номер строки и столбца. Мотив игрока за этим ходом – разблокировать ячейку, которая не содержит шахты.
# Standard Move if len(inp) == 2: # Try block to handle errant input try: val = list(map(int, inp)) except ValueError: clear() print("Wrong input!") instructions() continue
Ввод флага
В промежуточном движении три значения отправляются геймером. Первые два значения обозначают местоположение ячейки, а последняя обозначает помечу.
# Flag Input elif len(inp) == 3: if inp[2] != 'F' and inp[2] != 'f': clear() print("Wrong Input!") instructions() continue # Try block to handle errant input try: val = list(map(int, inp[:2])) except ValueError: clear() print("Wrong input!") instructions() continue
Дезинфицировать ввод
После хранения ввода мы должны делать некоторые чеки, для плавного функционирования игры.
# Sanity checks if val[0] > n or val[0] < 1 or val[1] > n or val[1] < 1: clear() print("Wrong Input!") instructions() continue # Get row and column numbers r = val[0]-1 col = val[1]-1
По завершении процесса ввода, номера строки и столбцы должны быть извлечены и сохранены в 'R'
и 'C'
Отказ
Обрабатывать ввод флага
Управление входом флага не является большой проблемой. Это требует проверки некоторых предварительных реквизисов, прежде чем пометить ячейку для шахты.
Следующие проверки должны быть сделаны:
- Ячейка уже была помечена или нет.
- Будет ли ячейка, которая будет помечена уже отображается игроку.
- Количество флагов не превышает количество шахт.
После ухода за этими проблемами ячейка попадает в шахту.
# If cell already been flagged if [r, col] in flags: clear() print("Flag already set") continue # If cell already been displayed if mine_values[r][col] != ' ': clear() print("Value already known") continue # Check the number for flags if len(flags) < mines_no: clear() print("Flag set") # Adding flag to the list flags.append([r, col]) # Set the flag for display mine_values[r][col] = 'F' continue else: clear() print("Flags finished") continue
Обрабатывать стандартный вход
Стандартный вход включает в себя общее функционирование игры. Есть три разных сценария:
Закрепление на шахте
Игра завершается, как только игрок выбирает ячейку, имеющую шахтую. Это может произойти из неудач или плохого суждения.
# If landing on a mine --- GAME OVER if numbers[r][col] == -1: mine_values[r][col] = 'M' show_mines() print_mines_layout() print("Landed on a mine. GAME OVER!!!!!") over = True continue
После того, как мы приземлимся на камере с моим, нам нужно отобразить все мины в игре и изменять переменную за игровой петлей.
Функция 'show_mines ()'
несет ответственность за это.
def show_mines(): global mine_values global numbers global n for r in range(n): for col in range(n): if numbers[r][col] == -1: mine_values[r][col] = 'M'
Посещение a ‘0’-kece Cell.
Самая сложная часть создания игры управляет этим сценарием. Всякий раз, когда Gamer, посещает a ‘0’-keblued клетку, все соседние элементы должны быть отображены до тех пор, пока не нулевая ячейка не будет достигнута.
# If landing on a cell with 0 mines in neighboring cells elif numbers[r][n] == 0: vis = [] mine_values[r][n] = '0' neighbours(r, col)
Эта цель достигается с использованием Рекурсия Отказ Рекурсия – это инструмент программирования, в котором функция вызывает сами до тех пор, пока не будет удовлетворен базовым корпусом. Соседи
Функция представляет собой рекурсивный, решение нашей проблемы.
def neighbours(r, col): global mine_values global numbers global vis # If the cell already not visited if [r,col] not in vis: # Mark the cell visited vis.append([r,col]) # If the cell is zero-valued if numbers[r][col] == 0: # Display it to the user mine_values[r][col] = numbers[r][col] # Recursive calls for the neighbouring cells if r > 0: neighbours(r-1, col) if r < n-1: neighbours(r+1, col) if col > 0: neighbours(r, col-1) if col < n-1: neighbours(r, col+1) if r > 0 and col > 0: neighbours(r-1, col-1) if r > 0 and col < n-1: neighbours(r-1, col+1) if r < n-1 and col > 0: neighbours(r+1, col-1) if r < n-1 and col < n-1: neighbours(r+1, col+1) # If the cell is not zero-valued if numbers[r][col] != 0: mine_values[r][col] = numbers[r][col]
Для этой конкретной концепции игры используется новая структура данных, а именно Вис
Отказ Роль Вис
отслеживать уже посещенные клетки во время рекурсии. Без этой информации рекурсия будет продолжаться постоянно.
После того, как все клетки с нулевым значением отображаются и их соседи, мы можем перейти к последнему сценарию.
Выбор не нулевой ячейки
Для решения этого случая не требуется никаких усилий, поскольку все, что нам нужно сделать, это изменить значение отображения.
# If selecting a cell with atleast 1 mine in neighboring cells else: mine_values[r][col] = numbers[r][col]
Конечная игра
Существует требование проверить наличие завершению игры, каждый раз, когда сделан шаг. Это делается:
# Check for game completion if(check_over()): show_mines() print_mines_layout() print("Congratulations!!! YOU WIN") over = True continue
Функция check_over ()
отвечает за проверку завершения игры.
# Function to check for completion of the game def check_over(): global mine_values global n global mines_no # Count of all numbered values count = 0 # Loop for checking each cell in the grid for r in range(n): for col in range(n): # If cell not empty or flagged if mine_values[r][col] != ' ' and mine_values[r][col] != 'F': count = count + 1 # Count comparison if count == n * n - mines_no: return True else: return False
Мы считаем количество ячеек, которые не пусты или помечены. Когда этот счет равен общей ячейкам, кроме тех, которые содержащие мины, то игра считается всеми.
Очистка вывода после каждого хода
Терминал становится переполненным, когда мы продолжаем на печатных материалах на нем. Поэтому должно быть обеспечено постоянно очистить его. Это может быть сделано:
# Function for clearing the terminal def clear(): os.system("clear")
Примечание: Есть необходимость импортировать ОС
Библиотека, прежде чем использовать эту функцию. Это можно сделать по «Импорт ОС»
В начале программы.
Полный код
Ниже приведен полный код игры MineSweeper:
# Importing packages import random import os # Printing the Minesweeper Layout def print_mines_layout(): global mine_values global n print() print("\t\t\tMINESWEEPER\n") st = " " for i in range(n): st = st + " " + str(i + 1) print(st) for r in range(n): st = " " if r == 0: for col in range(n): st = st + "______" print(st) st = " " for col in range(n): st = st + "| " print(st + "|") st = " " + str(r + 1) + " " for col in range(n): st = st + "| " + str(mine_values[r][col]) + " " print(st + "|") st = " " for col in range(n): st = st + "|_____" print(st + '|') print() # Function for setting up Mines def set_mines(): global numbers global mines_no global n # Track of number of mines already set up count = 0 while count < mines_no: # Random number from all possible grid positions val = random.randint(0, n*n-1) # Generating row and column from the number r = val // n col = val % n # Place the mine, if it doesn't already have one if numbers[r][col] != -1: count = count + 1 numbers[r][col] = -1 # Function for setting up the other grid values def set_values(): global numbers global n # Loop for counting each cell value for r in range(n): for col in range(n): # Skip, if it contains a mine if numbers[r][col] == -1: continue # Check up if r > 0 and numbers[r-1][col] == -1: numbers[r][col] = numbers[r][col] + 1 # Check down if r < n-1 and numbers[r+1][col] == -1: numbers[r][col] = numbers[r][col] + 1 # Check left if col > 0 and numbers[r][col-1] == -1: numbers[r][col] = numbers[r][col] + 1 # Check right if col < n-1 and numbers[r][col+1] == -1: numbers[r][col] = numbers[r][col] + 1 # Check top-left if r > 0 and col > 0 and numbers[r-1][col-1] == -1: numbers[r][col] = numbers[r][col] + 1 # Check top-right if r > 0 and col < n-1 and numbers[r-1][col+1] == -1: numbers[r][col] = numbers[r][col] + 1 # Check below-left if r < n-1 and col > 0 and numbers[r+1][col-1] == -1: numbers[r][col] = numbers[r][col] + 1 # Check below-right if r < n-1 and col < n-1 and numbers[r+1][col+1] == -1: numbers[r][col] = numbers[r][col] + 1 # Recursive function to display all zero-valued neighbours def neighbours(r, col): global mine_values global numbers global vis # If the cell already not visited if [r,col] not in vis: # Mark the cell visited vis.append([r,col]) # If the cell is zero-valued if numbers[r][col] == 0: # Display it to the user mine_values[r][col] = numbers[r][col] # Recursive calls for the neighbouring cells if r > 0: neighbours(r-1, col) if r < n-1: neighbours(r+1, col) if col > 0: neighbours(r, col-1) if col < n-1: neighbours(r, col+1) if r > 0 and col > 0: neighbours(r-1, col-1) if r > 0 and col < n-1: neighbours(r-1, col+1) if r < n-1 and col > 0: neighbours(r+1, col-1) if r < n-1 and col < n-1: neighbours(r+1, col+1) # If the cell is not zero-valued if numbers[r][col] != 0: mine_values[r][col] = numbers[r][col] # Function for clearing the terminal def clear(): os.system("clear") # Function to display the instructions def instructions(): print("Instructions:") print("1. Enter row and column number to select a cell, Example \"2 3\"") print("2. In order to flag a mine, enter F after row and column numbers, Example \"2 3 F\"") # Function to check for completion of the game def check_over(): global mine_values global n global mines_no # Count of all numbered values count = 0 # Loop for checking each cell in the grid for r in range(n): for col in range(n): # If cell not empty or flagged if mine_values[r][col] != ' ' and mine_values[r][col] != 'F': count = count + 1 # Count comparison if count == n * n - mines_no: return True else: return False # Display all the mine locations def show_mines(): global mine_values global numbers global n for r in range(n): for col in range(n): if numbers[r][col] == -1: mine_values[r][col] = 'M' if __name__ == "__main__": # Size of grid n = 8 # Number of mines mines_no = 8 # The actual values of the grid numbers = [[0 for y in range(n)] for x in range(n)] # The apparent values of the grid mine_values = [[' ' for y in range(n)] for x in range(n)] # The positions that have been flagged flags = [] # Set the mines set_mines() # Set the values set_values() # Display the instructions instructions() # Variable for maintaining Game Loop over = False # The GAME LOOP while not over: print_mines_layout() # Input from the user inp = input("Enter row number followed by space and column number = ").split() # Standard input if len(inp) == 2: # Try block to handle errant input try: val = list(map(int, inp)) except ValueError: clear() print("Wrong input!") instructions() continue # Flag input elif len(inp) == 3: if inp[2] != 'F' and inp[2] != 'f': clear() print("Wrong Input!") instructions() continue # Try block to handle errant input try: val = list(map(int, inp[:2])) except ValueError: clear() print("Wrong input!") instructions() continue # Sanity checks if val[0] > n or val[0] < 1 or val[1] > n or val[1] < 1: clear() print("Wrong input!") instructions() continue # Get row and column numbers r = val[0]-1 col = val[1]-1 # If cell already been flagged if [r, col] in flags: clear() print("Flag already set") continue # If cell already been displayed if mine_values[r][col] != ' ': clear() print("Value already known") continue # Check the number for flags if len(flags) < mines_no: clear() print("Flag set") # Adding flag to the list flags.append([r, col]) # Set the flag for display mine_values[r][col] = 'F' continue else: clear() print("Flags finished") continue else: clear() print("Wrong input!") instructions() continue # Sanity checks if val[0] > n or val[0] < 1 or val[1] > n or val[1] < 1: clear() print("Wrong Input!") instructions() continue # Get row and column number r = val[0]-1 col = val[1]-1 # Unflag the cell if already flagged if [r, col] in flags: flags.remove([r, col]) # If landing on a mine --- GAME OVER if numbers[r][col] == -1: mine_values[r][col] = 'M' show_mines() print_mines_layout() print("Landed on a mine. GAME OVER!!!!!") over = True continue # If landing on a cell with 0 mines in neighboring cells elif numbers[r][col] == 0: vis = [] mine_values[r][col] = '0' neighbours(r, col) # If selecting a cell with atleast 1 mine in neighboring cells else: mine_values[r][col] = numbers[r][col] # Check for game completion if(check_over()): show_mines() print_mines_layout() print("Congratulations!!! YOU WIN") over = True continue clear()
Заключение
Мы надеемся, что этот учебник по созданию нашей собственной игры MineSeeper был понятен, а также весело. Для любых запросов не стесняйтесь комментировать ниже. Полный код также доступен на моем Аккаунт GitHub Отказ