Написать игру Сапёр (Mines) на Python с использованием библиотеки Tkinter оказалось не сложно. В этой статье покажем все этапы создания этой игры, которая массово была представлена публике в Windows 95. Следует отметить, что в Windows 3.11 игра winmine уже была. Но кто теперь это вспомнит?
Правила игры:
Воспользуемся шаблоном из кнопок для игр на поле в клетку.
from tkinter import * # графическая библиотека
from random import shuffle # перемешать список suffle(A)
column = 3 # столбцы
row = 3 # строки
btn = []
playground = list('ЧТОЕСТЖУ ') # виртуальное игровое поле
def play(n): # функция обработчик нажатия на кнопку
btn[n].config(text=n)
for i in range(row):
f = Frame() # Фрейм
f.pack(expand=YES, fill=BOTH) # Вывод фрейма на экран
for j in range(column):
n = i * column + j
btn += [Button(f)]
btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
btn[n].config(width=3, height=2)
btn[n].config(text=playground[n])
btn[n].config(command=lambda n=n: play(n))
mainloop() # главный цикл программы
Лист. 1. Шаблон для игр на поле в клетку.
from tkinter import * # графическая библиотека
from random import shuffle # перемешать список suffle(A)
column = 5 # столбцы
row = 5 # строки
btn = []
playground = [1,0,0,0,0] * (row*column//5+1) # виртуальное игровое поле
shuffle(playground)
def play(n): # функция обработчик нажатия на кнопку
btn[n].config(text=n)
for i in range(row):
f = Frame() # Фрейм
f.pack(expand=YES, fill=BOTH) # Вывод фрейма на экран
for j in range(column):
n = i * column + j
btn += [Button(f)]
btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
btn[n].config(width=3, height=2)
btn[n].config(text=playground[n])
btn[n].config(command=lambda n=n: play(n))
mainloop() # главный цикл программы
Лист. 2. Создан и перемешан список playground.
from tkinter import * # графическая библиотека
from random import shuffle # перемешать список suffle(A)
column = 5 # столбцы
row = 5 # строки
btn = []
playground = [1,0,0,0,0] * (row*column//5+1) # виртуальное игровое поле
shuffle(playground)
def play(n): # функция обработчик нажатия на кнопку
if playground[n] == 1:
btn[n].config(text='M')
for i in range(row):
f = Frame() # Фрейм
f.pack(expand=YES, fill=BOTH) # Вывод фрейма на экран
for j in range(column):
n = i * column + j
btn += [Button(f)]
btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
btn[n].config(width=3, height=2)
btn[n].config(text=playground[n])
btn[n].config(command=lambda n=n: play(n))
mainloop() # главный цикл программы
Лист. 3. В функции play вычисляется попадание на мину.
from tkinter import * # графическая библиотека
from random import shuffle # перемешать список suffle(A)
column = 5 # столбцы
row = 5 # строки
btn = []
playground = [1,0,0,0,0] * (row*column//5+1) # виртуальное игровое поле
shuffle(playground)
def play(n): # функция обработчик нажатия на кнопку
m = 0
if playground[n] == 1:
btn[n].config(text='M')
btn[n].config(bg='#f88', activebackground='#f66')
elif m == 0:
btn[n].config(bg='#afa', activebackground='#7f7')
else :
btn[n].config(text=m)
for i in range(row):
f = Frame() # Фрейм
f.pack(expand=YES, fill=BOTH) # Вывод фрейма на экран
for j in range(column):
n = i * column + j
btn += [Button(f)]
btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
btn[n].config(width=3, height=2)
btn[n].config(text=playground[n])
btn[n].config(command=lambda n=n: play(n))
mainloop() # главный цикл программы
Лист. 4. Определена логика работы функции play и добавлен цвет.
from tkinter import * # графическая библиотека
from random import shuffle # перемешать список suffle(A)
column = 5 # столбцы
row = 5 # строки
btn = []
playground = [1,0,0,0,0] * (row*column//5+1) # виртуальное игровое поле
shuffle(playground)
def play(n): # функция обработчик нажатия на кнопку
m = (playground[n+1] + playground[n-1] +
playground[n+column] + playground[n-column] +
playground[n+column+1] + playground[n+column-1] +
playground[n-column+1] + playground[n-column-1])
if playground[n] == 1:
btn[n].config(text='M')
btn[n].config(bg='#f88', activebackground='#f66')
elif m == 0:
btn[n].config(bg='#afa', activebackground='#7f7')
else :
btn[n].config(text=m)
for i in range(row):
f = Frame() # Фрейм
f.pack(expand=YES, fill=BOTH) # Вывод фрейма на экран
for j in range(column):
n = i * column + j
btn += [Button(f)]
btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
btn[n].config(width=3, height=2)
btn[n].config(text=playground[n])
btn[n].config(command=lambda n=n: play(n))
mainloop() # главный цикл программы
Лист. 5. В функции play B добавлен расчёт количества мин вокруг нажатой кнопки.
IndexError: list index out of range
Лист. 6. Ошибка.
На листинге 6 представлена ошибка, возникающая в функции play в том случае, когда игрок нажимает последние кнопки из нижнего ряда. Но проблема не только в этом.
from tkinter import * # графическая библиотека
from random import shuffle # перемешать список suffle(A)
column = 7 # столбцы
row = 7 # строки
btn = []
playground = [1,0,0,0,0] * (row*column//5+1) # виртуальное игровое поле
shuffle(playground)
def play(n): # функция обработчик нажатия на кнопку
m = (playground[n+1] + playground[n-1] +
playground[n+column] + playground[n-column] +
playground[n+column+1] + playground[n+column-1] +
playground[n-column+1] + playground[n-column-1])
if playground[n] == 1:
btn[n].config(text='M')
btn[n].config(bg='#f88', activebackground='#f66')
elif m == 0:
btn[n].config(bg='#afa', activebackground='#7f7')
else :
btn[n].config(text=m)
for i in range(row):
f = Frame() # Фрейм
f.pack(expand=YES, fill=BOTH) # Вывод фрейма на экран
for j in range(column):
n = i * column + j
btn += [Button(f)]
btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
btn[n].config(width=3, height=2)
btn[n].config(bg='#ccc', activebackground='#aaa')
btn[n].config(command=lambda n=n: play(n))
mainloop() # главный цикл программы
Лист. 7. Увеличен размер игрового поля. Программа теперь не выводит значения из списка playground на игровое поле.
Рис. 1.
from tkinter import * # графическая библиотека
from random import shuffle # перемешать список suffle(A)
column = 7 # столбцы
row = 7 # строки
btn = []
playground = [1,0,0,0,0] * (row*column//5+1) # виртуальное игровое поле
shuffle(playground)
def play(n): # функция обработчик нажатия на кнопку
m = 0
for i in [n-column, n, n+column]:
for j in [-1, 0, 1]:
m += playground[i+j]
if playground[n] == 1:
btn[n].config(text='M')
btn[n].config(bg='#f88', activebackground='#f66')
elif m == 0:
btn[n].config(bg='#afa', activebackground='#7f7')
else :
btn[n].config(text=m)
for i in range(row):
f = Frame() # Фрейм
f.pack(expand=YES, fill=BOTH) # Вывод фрейма на экран
for j in range(column):
n = i * column + j
btn += [Button(f)]
btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
btn[n].config(width=3, height=2)
btn[n].config(bg='#ccc', activebackground='#aaa')
btn[n].config(command=lambda n=n: play(n))
mainloop() # главный цикл программы
Лист. 8. Подсчёт количества мин осуществляется теперь в цикле.
from tkinter import * # графическая библиотека
from random import shuffle # перемешать список suffle(A)
column = 7 # столбцы
row = 7 # строки
btn = []
playground = [1,0,0,0,0] * (row*column//5+1) # виртуальное игровое поле
shuffle(playground)
def play(n): # функция обработчик нажатия на кнопку
m = 0
for i in [n-column, n, n+column]:
for j in [-1, 0, 1]:
if (i+j >= 0 and i+j < row*column
and i//column == (i+j)//column):
m += playground[i+j]
if playground[n] == 1:
btn[n].config(text='M')
btn[n].config(bg='#f88', activebackground='#f66')
elif m == 0:
btn[n].config(bg='#afa', activebackground='#7f7')
else :
btn[n].config(text=m)
for i in range(row):
f = Frame() # Фрейм
f.pack(expand=YES, fill=BOTH) # Вывод фрейма на экран
for j in range(column):
n = i * column + j
btn += [Button(f)]
btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
btn[n].config(width=3, height=2)
btn[n].config(bg='#ccc', activebackground='#aaa')
btn[n].config(command=lambda n=n: play(n))
mainloop() # главный цикл программы
Лист. 9. Исправлены краевые ошибки подсчёта мин.
Рис. 2.
from tkinter import * # графическая библиотека
from random import shuffle # перемешать список suffle(A)
column = 7 # столбцы
row = 7 # строки
btn = []
playground = [1,0,0,0,0] * (row*column//5+1) # виртуальное игровое поле
shuffle(playground)
def play(n): # функция обработчик нажатия на кнопку
m = 0
for i in [n-column, n, n+column]:
for j in [-1, 0, 1]:
if (i+j >= 0 and i+j < row*column
and i//column == (i+j)//column):
m += playground[i+j]
if playground[n] == 1:
btn[n].config(text='M')
btn[n].config(bg='#f88', activebackground='#f66')
elif m == 0:
btn[n].config(bg='#afa', activebackground='#7f7')
else :
btn[n].config(text=m)
def marker(n):
btn[n].config(text='F', bg='#ffa', activebackground='#ff7')
for i in range(row):
f = Frame() # Фрейм
f.pack(expand=YES, fill=BOTH) # Вывод фрейма на экран
for j in range(column):
n = i * column + j
btn += [Button(f)]
btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
btn[n].config(width=3, height=2)
btn[n].config(bg='#ccc', activebackground='#aaa')
btn[n].config(command=lambda n=n: play(n))
btn[n].bind('<Button-3>', lambda event, n=n: marker(n))
mainloop() # главный цикл программы
Лист. 10. Создаём функцию marker.
from tkinter import * # графическая библиотека
from random import shuffle # перемешать список suffle(A)
column = 7 # столбцы
row = 7 # строки
btn = []
playground = [1,0,0,0,0] * (row*column//5+1) # виртуальное игровое поле
shuffle(playground)
def play(n): # функция обработчик нажатия на кнопку
m = 0
for i in [n-column, n, n+column]:
for j in [-1, 0, 1]:
if (i+j >= 0 and i+j < row*column
and i//column == (i+j)//column):
m += playground[i+j]
if playground[n] == 1:
btn[n].config(text='M', bg='#f88', activebackground='#f66')
elif m == 0:
btn[n].config(text=0, fg='#afa', bg='#afa', activebackground='#7f7')
else :
btn[n].config(text=m, bg='#ccc', activebackground='#aaa')
def marker(n):
if btn[n].cget('text') == '':
btn[n].config(text='F', bg='#ffa', activebackground='#ff7')
for i in range(row):
f = Frame() # Фрейм
f.pack(expand=YES, fill=BOTH) # Вывод фрейма на экран
for j in range(column):
n = i * column + j
btn += [Button(f)]
btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
btn[n].config(width=3, height=2)
btn[n].config(bg='#ccc', activebackground='#aaa')
btn[n].config(command=lambda n=n: play(n))
btn[n].bind('<Button-3>', lambda event, n=n: marker(n))
mainloop() # главный цикл программы
Лист. 11. Прорабатываем логику установки маркера (флажка).
#!/usr/bin/env python3
# Mines
# This is my version of the game, known as mines or Minesweeper.
#
# Created on December 21, 2023.
# Author: Diorditsa A.
#
# mines.py is distributed in the hope that it will be useful, but
# WITHOUT WARRANTY OF ANY KIND; not even an implied warranty
# MARKETABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See. See the GNU General Public License for more information.
# You can get a copy of the GNU General Public License
# by link http://www.gnu.org/licenses/
from tkinter import * # графическая библиотека
from random import shuffle # перемешать список suffle(A)
column = 7 # столбцы
row = 7 # строки
btn = []
playground = [1,0,0,0,0] * (row*column//5+1) # виртуальное игровое поле
shuffle(playground)
def play(n): # функция обработчик нажатия на кнопку
m = 0
for i in [n-column, n, n+column]:
for j in [-1, 0, 1]:
if (i+j >= 0 and i+j < row*column
and i//column == (i+j)//column):
m += playground[i+j]
if playground[n] == 1:
btn[n].config(text='M', bg='#f88', activebackground='#f66')
elif m == 0:
btn[n].config(text=0, fg='#afa', bg='#afa', activebackground='#7f7')
else :
btn[n].config(text=m, bg='#ccc', activebackground='#aaa')
def marker(n):
if btn[n].cget('text') == '':
btn[n].config(text='F', bg='#ffa', activebackground='#ff7')
elif btn[n].cget('text') == 'F':
btn[n].config(text='', bg='#ccc', activebackground='#aaa')
for i in range(row):
f = Frame() # Фрейм
f.pack(expand=YES, fill=BOTH) # Вывод фрейма на экран
for j in range(column):
n = i * column + j
btn += [Button(f)]
btn[n].pack(expand=YES, fill=BOTH, side=LEFT)
btn[n].config(width=3, height=2)
btn[n].config(bg='#ccc', activebackground='#aaa')
btn[n].config(command=lambda n=n: play(n))
btn[n].bind('<Button-3>', lambda event, n=n: marker(n))
mainloop() # главный цикл программы
Лист. 12. Делаем возможным снятие маркера (флажка).