Наш Сапёр имеет оригинальный элемент в правилах игры. Мы написали игру сапёр, в которой действие разворачивается не на квадратном поле, а на поле свёрнутом в трубочку, а трубочка завёрнута в бублик. Поле представляет собой развёртку тора.

На таком поле, в отличие от всем знакомой игры, на границах поля играть сложнее чем на середине поля, так ка на на границах приходится учитывать, игровую ситуацию с противоположной стороны поля.

В остальном, правила не отличаются от классической игры Сапёр. Игрок открывает клетку на игровом поле, компьютер подсчитывает сколько мин окружает эту клетку. Количество соседних мин выводится на нажатую клетку. Если игрок нажал на клетку под которой установлена мина, игрок проиграл. Пометить клетку под которой, возможно, скрывается мина можно правой кнопкой мыши. 

Игра заканчивается победой игрока, если игрок пометил все мины. 

from tkinter import *

class btn(Button):
    def __init__(self, container):
        super().__init__(container)
        self.pack(side=LEFT)

SIDE = 15
butn = []
frm = [Frame() for i in range(SIDE)]
for i in frm: i.pack()
for i in range(SIDE):
    butn += [btn(frm[i]) for j in range(SIDE)]

mainloop()

Программа 1. Поле для игры Сапёр.

В программе 1, мы создаём на экране компьютера поле для игры сапёр. Писать графический интерфейс игы будем с помощью библиотеки tkinter, поэтому, в начале программы мы импортируем из библиотеки tkinter все функции.

В программе 1 мы создаём новый класс btn, являющийся подклассом класса Button. В конструкторе класса btn вызывается конструктор класса Button, который создаёт кнопку. Методом pack() выводим эту кнопку на экран прижатой к левой границе контейнера. Имя контейнера для кнопки мы передаём в конструктор btn, как единственный параметр.

В нашей программе игровое поле будет квадратным со стороной 15 клеток. Переменная SIDE определяет размер поля в клетках.

Все кнопки, созданные нами на игровом поле, как объекты 

Рис. 1. Поле для игры Сапёр.

from tkinter import *

class btn(Button):
    def __init__(self, container, number):
        super().__init__(container)
        self.pack(side=LEFT)
        self.num = number

SIDE = 15
butn = []
frm = [Frame() for i in range(SIDE)]
for i in frm: i.pack()
for i in range(SIDE):
    butn += [btn(frm[i], j+i*SIDE) for j in range(SIDE)]

mainloop()

Программа 2. Поле для игры Сапёр.

В программу 2 добавили:

  • параметр number в строку def __init__(self, container, number):
  • строку self.num = number
  • вычисляемый параметр j+i*SIDE в строку butn += [btn(frm[i], j+i*SIDE) for j in range(SIDE)]

варпвр

from tkinter import *

class btn(Button):
    def __init__(self, container, number):
        super().__init__(container, command=self.play)
        self.pack(side=LEFT)
        self.num = number
    def play(self):
        print(self.num)

SIDE = 15
butn = []
frm = [Frame() for i in range(SIDE)]
for i in frm: i.pack()
for i in range(SIDE):
    butn += [btn(frm[i], j+i*SIDE) for j in range(SIDE)]

mainloop()

Программа 3. Поле для игры Сапёр.

В программу 3 добавили свойство command=self.play в строку super().__init__(container, command=self.play) и определение функции play():

def play(self):
    print(self.num)

где функция print(self.num) выводит номер нажатой кнопки.

from tkinter import *
from random import shuffle

class btn(Button):
    def __init__(self, container, number):
        super().__init__(container, command=self.play)
        self.pack(side=LEFT)
        self.num = number
    def play(self):
        self.config(text=str(playGround[self.num]))

def newGame():
    global playGround
    shuffle(playGround)

SIDE = 15
butn = []
frm = [Frame() for i in range(SIDE)]
for i in frm: i.pack()
for i in range(SIDE):
    butn += [btn(frm[i], j+i*SIDE) for j in range(SIDE)]
mines = SIDE**2 * 10 // 64
playGround = [1] * mines + [0] * (SIDE**2 - mines)
newGame()

mainloop()

Программа 4. Поле для игры Сапёр.

В программу 4 добавили:

  • строку from random import shuffle
  • строку self.config(text=str(playGround[self.num])) в функцию btn.play()
  • строку mines = SIDE**2 * 10 // 64
  • строку playGround = [1] * mines + [0] * (SIDE**2 - mines)
  • строку newGame()
  • определение функции newGame():
def newGame():
    global playGround
    shuffle(playGround)

ghgfhj

Рис. 2. Поле для игры Сапёр.

На поле для игры Сапёр, см. рис. 2, мы видим недостатки.

from tkinter import *
from random import shuffle

class btn(Button):
    def __init__(self, container, number):
        super().__init__(container, command=self.play)
        self.config(text=' ', font=('mono', 10), width=1, height=1, padx=4, pady=0)
        self.pack(side=LEFT)
        self.num = number
    def play(self):
        self.config(text=str(playGround[self.num]))

def newGame():
    global playGround
    shuffle(playGround)

SIDE = 15
butn = []
frm = [Frame() for i in range(SIDE)]
for i in frm: i.pack()
for i in range(SIDE):
    butn += [btn(frm[i], j+i*SIDE) for j in range(SIDE)]
mines = SIDE**2 * 10 // 64
playGround = [1] * mines + [0] * (SIDE**2 - mines)
newGame()

mainloop()

Программа 5. Поле для игры Сапёр.

В программу 5 добавили строку self.config(text=' ', font=('mono', 10), width=1, height=1, padx=4, pady=0) с параметрами для кнопки.

Где:

  • font=('mono', 10)
  • width=1
  • height=1
  • padx=4
  • pady=0

Рис. 3. Поле для игры Сапёр.

from tkinter import *
from random import shuffle

class btn(Button):
    def __init__(self, container, number):
        super().__init__(container, command=self.play)
        self.config(text=' ', font=('mono', 10), width=1, height=1, padx=4, pady=0)
        self.pack(side=LEFT)
        self.num = number
    def play(self):
        global playGround
        i = self.num
        roomie  = playGround[(i+1)%SIDE + SIDE*(i//SIDE)]        # Соседи
        roomie += playGround[(i-1+SIDE)%SIDE + SIDE*(i//SIDE)]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + i%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + i%SIDE]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + (i+1)%SIDE]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + (i-1+SIDE)%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + (i+1)%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + (i-1+SIDE)%SIDE]
        self.config(text=str(roomie))

def newGame():
    global playGround
    shuffle(playGround)

SIDE = 15
butn = []
frm = [Frame() for i in range(SIDE)]
for i in frm: i.pack()
for i in range(SIDE):
    butn += [btn(frm[i], j+i*SIDE) for j in range(SIDE)]
mines = SIDE**2 * 10 // 64
playGround = [1] * mines + [0] * (SIDE**2 - mines)
newGame()

mainloop()

Программа 6. Поле для игры Сапёр.

В программе 6 переписали метод btn.play().

Рис. 4. Поле для игры Сапёр.

from tkinter import *
from random import shuffle

class btn(Button):
    def __init__(self, container, number):
        super().__init__(container, command=self.play)
        self.config(text=' ', font=('mono', 10), width=1, height=1, padx=4, pady=0)
        self.pack(side=LEFT)
        self.num = number
    def play(self):
        global playGround
        i = self.num
        roomie  = playGround[(i+1)%SIDE + SIDE*(i//SIDE)]        # Соседи
        roomie += playGround[(i-1+SIDE)%SIDE + SIDE*(i//SIDE)]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + i%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + i%SIDE]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + (i+1)%SIDE]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + (i-1+SIDE)%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + (i+1)%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + (i-1+SIDE)%SIDE]
        self.config(text=str(roomie))
        if roomie == 0:
            self.config(bg='#dfd', fg='#dfd')

def newGame():
    global playGround
    shuffle(playGround)

SIDE = 15
butn = []
frm = [Frame() for i in range(SIDE)]
for i in frm: i.pack()
for i in range(SIDE):
    butn += [btn(frm[i], j+i*SIDE) for j in range(SIDE)]
mines = SIDE**2 * 10 // 64
playGround = [1] * mines + [0] * (SIDE**2 - mines)
newGame()

mainloop()

Программа 7. Поле для игры Сапёр.

В программе 7 мы добавили в метод btn.play() две строки:

if roomie == 0:
self.config(bg='#dfd', fg='#dfd')

где свойство bg, а свойство fg tytrfuftujfyuj

Рис. 5. Поле для игры Сапёр.

from tkinter import *
from random import shuffle

class btn(Button):
    def __init__(self, container, number):
        super().__init__(container, command=self.play)
        self.config(text=' ', font=('mono', 10), width=1, height=1, padx=4, pady=0)
        self.pack(side=LEFT)
        self.num = number
    def play(self):
        global playGround
        i = self.num
        if playGround[i]:
            self.config(text='X', bg='#f77')
            return
        roomie  = playGround[(i+1)%SIDE + SIDE*(i//SIDE)]        # Соседи
        roomie += playGround[(i-1+SIDE)%SIDE + SIDE*(i//SIDE)]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + i%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + i%SIDE]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + (i+1)%SIDE]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + (i-1+SIDE)%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + (i+1)%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + (i-1+SIDE)%SIDE]
        self.config(text=str(roomie))
        if roomie == 0:
            self.config(bg='#dfd', fg='#dfd')

def newGame():
    global playGround
    shuffle(playGround)

SIDE = 15
butn = []
frm = [Frame() for i in range(SIDE)]
for i in frm: i.pack()
for i in range(SIDE):
    butn += [btn(frm[i], j+i*SIDE) for j in range(SIDE)]
mines = SIDE**2 * 10 // 64
playGround = [1] * mines + [0] * (SIDE**2 - mines)
newGame()

mainloop()

Программа 8. Игра Сапёр.

В программе 8 мы добавили в метод btn.play() три строки:

if playGround[i]:
self.config(text='X', bg='#f77')
return

раплоплааенг

Рис. 6. Игра Сапёр.

from tkinter import *
from random import shuffle

class btn(Button):
    def __init__(self, container, number):
        super().__init__(container, command=self.play)
        self.config(text=' ', font=('mono', 10), width=1, height=1, padx=4, pady=0)
        self.bind('<Button-3>', self.marking)
        self.pack(side=LEFT)
        self.num = number
    def play(self):
        global playGround
        i = self.num
        if playGround[i]:
            self.config(text='X', bg='#f77')
            return
        roomie  = playGround[(i+1)%SIDE + SIDE*(i//SIDE)]        # Соседи
        roomie += playGround[(i-1+SIDE)%SIDE + SIDE*(i//SIDE)]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + i%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + i%SIDE]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + (i+1)%SIDE]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + (i-1+SIDE)%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + (i+1)%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + (i-1+SIDE)%SIDE]
        self.config(text=str(roomie))
        if roomie == 0:
            self.config(bg='#dfd', fg='#dfd')
    def marking(self, event):
        if self.cget('text')==' ' or self.cget('text')=='M':
            self.config(text= 'M' if self.cget('text')==' ' else ' ')
            self.config(bg='#fdd' if self.cget('text')=='M' else '#ddd')

def newGame():
    global playGround
    shuffle(playGround)

SIDE = 15
butn = []
frm = [Frame() for i in range(SIDE)]
for i in frm: i.pack()
for i in range(SIDE):
    butn += [btn(frm[i], j+i*SIDE) for j in range(SIDE)]
mines = SIDE**2 * 10 // 64
playGround = [1] * mines + [0] * (SIDE**2 - mines)
newGame()

mainloop()

Программа 9. Игра Сапёр.

В программу 9 в конструктор класса btn мы добавили:

  • строку self.bind('<Button-3>', self.marking)
  • определение метода self.marking():
def marking(self, event):
if self.cget('text')==' ' or self.cget('text')=='M': self.config(text= 'M' if self.cget('text')==' ' else ' ') self.config(bg='#fdd' if self.cget('text')=='M' else '#ddd')

ghgfhj

Рис. 7. Игра Сапёр.

from tkinter import *
from random import shuffle

class btn(Button):
    def __init__(self, container, number):
        super().__init__(container, command=self.play)
        self.config(text=' ', font=('mono', 10), width=1, height=1, padx=4, pady=0)
        self.bind('<Button-3>', self.marking)
        self.pack(side=LEFT)
        self.num = number
    def play(self):
        global playGround
        i = self.num
        if playGround[i]:
            self.config(text='X', bg='#f77')
            return
        roomie  = playGround[(i+1)%SIDE + SIDE*(i//SIDE)]        # Соседи
        roomie += playGround[(i-1+SIDE)%SIDE + SIDE*(i//SIDE)]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + i%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + i%SIDE]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + (i+1)%SIDE]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + (i-1+SIDE)%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + (i+1)%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + (i-1+SIDE)%SIDE]
        self.config(text=str(roomie))
        if roomie == 0:
            self.config(bg='#dfd', fg='#dfd')
    def marking(self, event):
        if self.cget('text')==' ' or self.cget('text')=='M':
            self.config(text= 'M' if self.cget('text')==' ' else ' ')
            self.config(bg='#fdd' if self.cget('text')=='M' else '#ddd')

def newGame():
    global playGround
    shuffle(playGround)
    tk.title('Achtung, '+str(mines)+' Minen!')

SIDE = 15
butn = []
tk = Tk()
frm = [Frame() for i in range(SIDE)]
for i in frm: i.pack()
for i in range(SIDE):
    butn += [btn(frm[i], j+i*SIDE) for j in range(SIDE)]
mines = SIDE**2 * 10 // 64
playGround = [1] * mines + [0] * (SIDE**2 - mines)
newGame()

mainloop()

Программа 10. Игра Сапёр.

В программу 10 мы добавили:

  • строку tk = Tk()
  • строку tk.title('Achtung, '+str(mines)+' Minen!') в функцию newGame()

ghgfhj

Рис. 8. Игра Сапёр.

from tkinter import *
from random import shuffle

class btn(Button):
    def __init__(self, container, number):
        super().__init__(container, command=self.play)
        self.config(text=' ', font=('mono', 10), width=1, height=1, padx=4, pady=0)
        self.bind('<Button-3>', self.marking)
        self.pack(side=LEFT)
        self.num = number
    def play(self):
        global playGround
        i = self.num
        if playGround[i]:
            self.config(text='X', bg='#f77')
            tk.title('You Dead')
            return
        roomie  = playGround[(i+1)%SIDE + SIDE*(i//SIDE)]        # Соседи
        roomie += playGround[(i-1+SIDE)%SIDE + SIDE*(i//SIDE)]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + i%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + i%SIDE]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + (i+1)%SIDE]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + (i-1+SIDE)%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + (i+1)%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + (i-1+SIDE)%SIDE]
        self.config(text=str(roomie))
        if roomie == 0:
            self.config(bg='#dfd', fg='#dfd')
    def marking(self, event):
        if self.cget('text')==' ' or self.cget('text')=='M':
            self.config(text= 'M' if self.cget('text')==' ' else ' ')
            self.config(bg='#fdd' if self.cget('text')=='M' else '#ddd')

def newGame():
    global playGround
    shuffle(playGround)
    tk.title('Achtung, '+str(mines)+' Minen!')
    for i in butn: i.config(text=' ', bg='#ddd', fg='#000')

SIDE = 15
butn = []
tk = Tk()
Button(text='New Game', command=newGame).pack()
frm = [Frame() for i in range(SIDE)]
for i in frm: i.pack()
for i in range(SIDE):
    butn += [btn(frm[i], j+i*SIDE) for j in range(SIDE)]
mines = SIDE**2 * 10 // 64
playGround = [1] * mines + [0] * (SIDE**2 - mines)
newGame()

Программа 11. Игра Сапёр.

В программу 11 мы добавили:

  • в методе btn.play() строку tk.title('You Dead')
  • создание кнопки Button(text='New Game', command=newGame).pack()
  • строку в функцию newGame: for i in butn: i.config(text=' ', bg='#ddd', fg='#000')

парапрыаеро

Рис. 9. Игра Сапёр.

#!/usr/bin/env python3
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- 
#
# LinuxORfreeBSD.py
# Copyright (C) 2021 Aleksandr Diorditsa, see <https://adior.ru>
# I want to thank all my students for the inspiration they give me.
#
# LinuxORfreeBSD.py is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# LinuxORfreeBSD.py is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.

from tkinter import *
from random import shuffle

class btn(Button):
    def __init__(self, container, number):
        super().__init__(container, command=self.play)
        self.config(text=' ', font=('mono', 10), width=1, height=1, padx=4, pady=0)
        self.bind('<Button-3>', self.marking)
        self.pack(side=LEFT)
        self.num = number
    def play(self):
        global playGround
        i = self.num
        if playGround[i]:
            self.config(text='X', bg='#f77')
            tk.title('You Dead')
            return
        roomie  = playGround[(i+1)%SIDE + SIDE*(i//SIDE)]        # Соседи
        roomie += playGround[(i-1+SIDE)%SIDE + SIDE*(i//SIDE)]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + i%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + i%SIDE]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + (i+1)%SIDE]
        roomie += playGround[((i//SIDE+1)%SIDE)*SIDE + (i-1+SIDE)%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + (i+1)%SIDE]
        roomie += playGround[((i//SIDE+SIDE-1)%SIDE)*SIDE + (i-1+SIDE)%SIDE]
        self.config(text=str(roomie))
        if roomie == 0:
            self.config(bg='#dfd', fg='#dfd')
    def marking(self, event):
        global playGround, mapMinefields
        if self.cget('text')==' ' or self.cget('text')=='M':
            self.config(text= 'M' if self.cget('text')==' ' else ' ')
            self.config(bg='#fdd' if self.cget('text')=='M' else '#ddd')
            mapMinefields[self.num] = 0 if self.cget('text')==' ' else 1
            if mapMinefields == playGround: tk.title('You Win!')

def newGame():
    global playGround, mapMinefields
    shuffle(playGround)
    tk.title('Achtung, '+str(mines)+' Minen!')
    for i in butn: i.config(text=' ', bg='#ddd', fg='#000')
    mapMinefields = [0] * SIDE**2

SIDE = 15
butn = []
tk = Tk()
Button(text='New Game', command=newGame).pack()
frm = [Frame() for i in range(SIDE)]
for i in frm: i.pack()
for i in range(SIDE):
    butn += [btn(frm[i], j+i*SIDE) for j in range(SIDE)]
mines = SIDE**2 * 10 // 64
playGround = [1] * mines + [0] * (SIDE**2 - mines)
newGame()

mainloop()

Программа 12. Игра Сапёр.

В программу 12 мы добавили:

в функцию newGame() список mapMinefields = [0] * SIDE**2

и объявили mapMinefields как global

в метод btn.marking() строку mapMinefields[self.num] = 0 if self.cget('text')==' ' else 1

и туда же, строку if mapMinefields == playGround: tk.title('You Win!')

Рис. 19. Игра Сапёр.