Рубрика:
Карьера/Образование /
Кафедра
|
Facebook
Мой мир
Вконтакте
Одноклассники
Google+
|
КИРИЛЛ ТКАЧЕНКО, специалист по учебно-методической работе 1-й кат., ассистент, аспирант, Федеральное государственное бюджетное образовательное учреждение высшего образования «Севастопольский государственный университет», tkachenkokirillstanislavovich@mail.ru
Играем с компьютером в головоломку «Отшельник»
Продолжается цикл статей, посвященный программированию игр и головоломок типа «выигрывающие стратегии». На этот раз рассматриваем реализацию графического интерфейса для игры «Отшельник»
В публикации [1] описан процесс анализа и разработки программы для автоматизированного решения головоломки «Отшельник». Этот решатель, реализованный на нескольких языках программирования высокого уровня, может породить одно-единственное, но верное решение. Результат можно проверить и поварьировать с учетом симметричных ходов нареальной игровой доске с фишками (или, возможно, с колышками), которые можно изготовить из бумаги, картона, пластика, дерева, металла и прочего. Но, возвращаясь кбессмертному труду [2], хочется процитировать: «Кто сам колет свои дрова, согревается дважды». Аудитория журнала, вероятно, будет рада самостоятельно запрограммировать имитацию игровой доски на языке программирования высокого уровня. В настоящей публикации рассматриваются язык Python 3 и библиотека графического пользовательского интерфейса Tkinter.
Выбор универсального Python 3 как целевого языка реализации обусловлен его широкой сферой применения, в первую очередь в системном администрировании, а также вразработке прикладного программного обеспечения на множестве платформ, таких как Windows, Linux, в веб-разработке. Библиотек GUI для него реализовано достаточно много. Некоторые авторы считают для него «родной» и наиболее важной библиотеку Tkinter, которая хороша своим удобством, простотой, всеобъемлющей переносимостью и восходит кбиблиотеке Tk языковой связки Tcl/Tk. Саму Tk иногда принято использовать и в некоторых других языках, например, Ruby.
Необходимо минимально рассмотреть основные элементы Tkinter, которые будут использоваться в разработке. Корневое окно (или, говоря иначе, root-окно) создается конструктором tkinter.Tk(). С помощью метода title() устанавливается заголовок окна. С помощью метода geometry() задается геометрия окна, а именно размеры и местоположение. Метод mainloop() создает окно и начинает обработку событий.
Виджет tkinter.Label предназначен для отображения нередактируемой пользователем графического интерфейса надписи. В конструкторе класса этого виджета первым неименованным аргументом является родительский виджет, в который будет производиться упаковка. Именованный аргумент text задает отображаемый текст. Именованный аргумент font, в свою очередь, определяет используемый для текста шрифт, его начертание и размер.
Метод grid() виджета использует, очевидно, упаковщик grid. Этот упаковщик представляет собой прямоугольную матрицу – таблицу с ячейками. В эти ячейки помещаются виджеты. Именованный аргумент row задает номер строки. Именованный аргумент column задает номер столбца. Именованный аргумент columnspan определяет количество занимаемых методом столбцов.
Метод bind виджета выполняет привязку обработчика событий. У этого метода имеется три аргумента: название события, функция-обработчик события и необязательный аргумент (указывает о добавлении обработчика к уже имеющимся). Обработчик события должен принимать один аргумент типа tkinter.Event. С помощью создания lambda-функций возможно предоставить обработчику дополнительные аргументы. Существует несколько способов наименования событий, при использовании одного из них название события обработки однократного нажатия левой кнопки мыши может быть записано как <Button-1>.
Итак, в среде разработки, поддерживающей Python 3 (например, Geany, Notepad++ и тому подобное), создается файл исходного текста программы с расширением .py.
Листинг программы с номерами строк
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 '''
4 Игра "Отшельник"
5 '''
6
7 import tkinter
8
9
10 class Samag:
11 '''
12 Головной класс
13 '''
14 def __init__(self):
15 '''
16 Метод инициализации объекта класса
17 '''
18 self.font_size = 'Times 48 bold'
19 self.board = []
20 self.x_coords = []
21 self.y_coords = []
22 txt = 'Игра \u00ABОтшельник\u00BB'
23 self.root = tkinter.Tk()
24 self.root.title(txt)
25 self.root.geometry('320x640')
26 self.init_board()
27 self.redraw()
28 lbl_new = tkinter.Label(self.root, text='\u25E6', \
29 font=self.font_size)
30 lbl_new.grid(row=1, column=0)
31 lbl_new.bind('<Button-1>', self.lbl_new_click)
32 lbl_caption = tkinter.Label(self.root, text=txt + \
33 ' (специально для \u00ABSAMAG\u00BB)')
34 lbl_caption.grid(row=0, column=0, columnspan=7)
35 self.root.mainloop()
36
37 def in_cross(self, x, y):
38 return 2 <= x <= 4 or 2 <= y <= 4
39
40 def init_board(self):
41 self.board = [[self.in_cross(x, y) and \
42 not (x == 3 and y == 3) \
43 for y in range(7)] for x in range(7)]
44 self.x_coords = []
45 self.y_coords = []
46
47 def redraw(self):
48 for x in range(7):
49 for y in range(7):
50 if self.in_cross(x, y):
51 lbl = tkinter.Label(self.root, text=('\u2022' \
52 if self.board[x][y] else '\u2043'), \
53 font=self.font_size)
54 lbl.grid(row=x + 1, column=y)
55 lbl.bind('<Button-1>', lambda e, x=x, y=y: \
56 self.lbl_board_click(e, x, y))
57
58 def lbl_board_click(self, e, x, y):
59 self.x_coords += [x]
60 self.y_coords += [y]
61 if len(self.x_coords) > 3:
62 self.x_coords = self.x_coords[-3:]
63 self.y_coords = self.y_coords[-3:]
64 if len(self.x_coords) == 3:
65 self.try_make_move()
66
67 def sign(self, x):
68 return 1 if x > 0 else (0 if x == 0 else -1)
69
70 def is_on_one_line(self, a1, a2, a3, b1, b2, b3):
71 return (b1 == b2) and (b1 == b3) and \
72 abs(a1 - a2) == 1 and \
73 abs(a1 - a3) == 2 and \
74 abs(a2 - a3) == 1 and \
75 self.sign(a1 - a2) == self.sign(a2 - a3) and \
76 self.sign(a1 - a2) == self.sign(a1 - a3)
77
78 def try_make_move(self):
79 (x1, x2, x3) = self.x_coords
80 (y1, y2, y3) = self.y_coords
81 if ((self.is_on_one_line(x1, x2, x3, y1, y2, y3) or \
82 self.is_on_one_line(y1, y2, y3, x1, x2, x3)) and \
83 self.board[x1][y1] and self.board[x2][y2] and \
84 not self.board[x3][y3]):
85 self.board[x1][y1] = False
86 self.board[x2][y2] = False
87 self.board[x3][y3] = True
88 self.x_coords = []
89 self.y_coords = []
90 self.redraw()
91 self.maybe_win()
92
93 def maybe_win(self):
94 count = 0
95 for a in self.board:
96 for b in a:
97 if b:
98 count += 1
99 if count == 1:
100 self.init_board()
101 self.redraw()
102
103 def lbl_new_click(self, e):
104 self.init_board()
105 self.redraw()
106
107
108 def main():
109 Samag()
110
111
112 if __name__ == '__main__':
113 main()
Вносятся следующие строки из единственного прилагаемого листинга, которые описывают для среды исполнения возможность запуска программы интерпретатором, а также указывают используемую кодировку для него же (строки 1-2). Затем пишется строка документирования (строки 3-5). Подключается Tkinter – кроссплатформенная библиотека GUI. Эта оконная библиотека может быть использована в Windows, Linux и прочих операционных системах (строка 7).
Для головного и единственного класса описывается заголовок к нему (строка 10), а после этого добавляется строка документирования (строки 11-13).
В классе имеется десять методов:
Метод 1. Первым методом станет метод инициализации объекта класса (заголовок в строке 14). По правилам для каждого метода записывается необходимая строка документирования (строки 15-17). В этом методе в переменную строкового типа заносится строка описания используемого шрифта у меток игрового поля – Times New Roman, полужирный, размер 48 (строка 18). Переменная для хранения содержимого доски (строка 19) инициализируется пустым списком, так же как и список для хранения горизонталей нажатых меток поля (строка 20) и список для хранения вертикалей нажатых меток поля (строка 21). Переменная строка-баннер (строка 22) инициализируется строкой метки заголовка.
Создается базовый класс Tkinter приложения (строка 23). Устанавливаются заголовок окна (строка 24) и его размеры (строка 25), производятся инициализация игры (строка 26) иотрисовка игрового поля (строка 27).
Создается метка для начала новой игры. Для нее устанавливаются отображаемый текст «пустой кружок» и шрифт, описанный выше (строки 28-29). Для этой метки используется упаковщик grid, представляющий собой таблицу с ячейками. В данном случае – 8 на 7. Метка помещается в строку №1, столбец №0 (строка 30). К метке привязывается событие щелчка левой кнопки мыши (строка 31).
Создается метка заголовка приложения (строки 32-33). Для нее используется упаковщик grid, как и выше. Но метка помещается в строку №0, столбец №0 и занимает семь столбцов (строка 34).
Затем начинает выполнение главный цикл обработки событий (строка 35).
Метод 2. Следующим описывается метод, который возвращает значение True, если клетка с указанными горизонталью x и вертикалью y принадлежит крестовому игровому полю, иначе возвращает False (строка 37-38). Для этого и последующих методов читатель вправе самостоятельно написать документирующие строки. Числовые значения оценки положения поля в формуле (строка 38) получаются из рис. 1.
Рисунок 1. Иллюстрация координатной сетки игрового поля
Метод 3. Метод для инициализации игры, в том числе игрового поля и списков для хранения координат нажатых меток, начинается со строки 40. В этом методе переменная дляхранения содержимого доски (строки 41-43) инициализируется следующим образом, а именно для всех клеток с горизонталями от 0 до 6 и вертикалями от 0 до 6, если клетка принадлежит крестовому игровому полю и не является центральной, то значение True, иначе False.
Список для хранения горизонталей нажатых меток поля (строка 44), как и список для хранения вертикалей нажатых меток поля (строка 45), инициализируется пустыми списками.
Метод 4. Метод, который выполняет отрисовку игровой доски (строка 47). В нем для всех горизонталей с номерами x от 0 до 6 (строка 48), как, впрочем, и для всех вертикалей сномерами y от 0 до 6 (строка 49), выполняется: если клетка принадлежит крестовому игровому полю (строка 50), то создается метка для игрового поля (строки 51-53) и для нее устанавливается отображаемый текст «закрашенный кружок», если на поле имеется фишка (колышек), или «прочерк», если поле пусто. Шрифт метки описывается выше.
Используется упаковщик grid (строка 54), представляющий собой таблицу с ячейками. В данном случае – 8 на 7. Метка помещается в строку №x+1, столбец №y. К метке привязывается событие щелчка левой кнопки мыши (строки 55-56). В качестве аргументов передается не только событие, но также и координаты метки.
Метод 5. В методе обработки одинарного щелчка левой кнопки мыши по метке клетки игрового поля (строка 58) в список горизонталей добавляется нажатая горизонталь (строка 59), в список вертикалей добавляется нажатая вертикаль (строка 60). Если в списках больше трех координат (строка 61), то остаются только последние три нажатия (строки 62-63). Если в списках ровно три координаты (строка 64), то происходит попытка выполнения перемещения фишки (колышка) (строка 65).
Метод 6. Метод, который возвращает знак числа (строки 67–68), является стандартной реализацией signum.
Метод 7. Метод, который проверяет, лежат ли три поля на одной горизонтали/вертикали непосредственно рядом (строка 70), выполняет проверку. Если три поля находятся на одной горизонтали/вертикали, поля находятся рядом, последовательность полей – в одну сторону (строки 71-76).
Метод 8. В методе для попытки и последующего выполнения хода (строка 78) распаковываются горизонтали (строка 79) и вертикали (строка 80). Если поля лежат на одной горизонтали или вертикали непосредственно рядом и содержат, соответственно, фишку (колышек), фишку и ее отсутствие (строки 81-84), то делается ход (строки 85-87), очищаются координаты (строки 88-89), отрисовывается доска и проверяется победная ситуация (строки 90-91).
Метод 9. В методе для проверки наличия победной ситуации (строка 93) для каждого поля (строки 94-97) подсчитывается количество True (строка 98). Если одно (строка 99), тоинициализируется игры (строка 100), отрисовывается игровое поле (строка 101).
Метод 10. Метод для создания новой игры, обработки события по щелчку на соответствующую метку (строка 103) выполняет инициализацию игры (строка 104) и отрисовку игрового поля (строка 105).
Головная функция описывается в том же исходном модуле (строка 108). В ней создается объект головного класса (строка 109).
Точка входа (строки 112-113) выполняет запуск.
Полученную с помощью свободного программного обеспечения диаграмму классов системы для единственного класса, с указанием полей и методов, можно увидеть на рис. 2, а программную систему в работе – на рис. 3.
|
|
|
Рисунок 2. Класс Samag |
|
Рисунок 3. Главное окно программы |
Для пользователя работа с программой чрезвычайно проста. Щелчком левой кнопкой мыши по белому кружочку создается новая игра. Для выполнения хода необходимо подряд нажать на поле с фишкой (колышком), поле с фишкой и поле без фишки. Такая последовательность имитирует реальную игру с физически существующей доской.
- Ткаченко К. Головоломка «Отшельник». Реализации решения «выигрывающих стратегий». // «Системный администратор», №10, 2014 г. – С.82-85 (http://samag.ru/archive/article/2803).
- Арсак Ж. Программирование игр и головоломок. – М.: «Наука», 1990. – 324 с.
Facebook
Мой мир
Вконтакте
Одноклассники
Google+
|