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

Создайте MineSweeper, используя Python из основных для продвинутых

В этой статье мы будем проходить шаги по созданию нашего собственного терминального минселлера, используя язык Python.

Автор оригинала: 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 имеет предоставление использования «флага», чтобы пометить клетки, которые мы знаем, содержит шахту.

Хранилище данных

Для одной игры 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 Отказ