├── README.md ├── games ├── arcade_games │ ├── fifteen.py │ ├── minesweeper.py │ ├── pong.py │ └── snake.py └── terminal_games │ └── xos.py └── programs └── gui ├── PyPaint.py └── q_calc.py /README.md: -------------------------------------------------------------------------------- 1 | python 2 | ====== 3 | 4 | Examples of programs written in Python. 5 | Sources for http://pythonicway.com/ 6 | 7 | Contents: 8 | 9 | 1.Arcade games: 10 | - Pong game 11 | - Snake Game 12 | - Minesweeper 13 | 14 | 2.Terminal Games: 15 | - XO game 16 | 17 | 3.GUI programs: 18 | - Quadratic Calc using Tkinter 19 | - Simple Paint-like application in Python with Tkinter 20 | -------------------------------------------------------------------------------- /games/arcade_games/fifteen.py: -------------------------------------------------------------------------------- 1 | from random import shuffle 2 | from tkinter import Canvas, Tk 3 | 4 | BOARD_SIZE = 4 5 | SQUARE_SIZE = 80 6 | EMPTY_SQUARE = BOARD_SIZE ** 2 7 | 8 | root = Tk() 9 | root.title("Pythonicway Fifteen") 10 | 11 | c = Canvas(root, width=BOARD_SIZE * SQUARE_SIZE, 12 | height=BOARD_SIZE * SQUARE_SIZE, bg='#808080') 13 | c.pack() 14 | 15 | 16 | def get_inv_count(): 17 | """ Функция считающая количество перемещений """ 18 | inversions = 0 19 | inversion_board = board[:] 20 | inversion_board.remove(EMPTY_SQUARE) 21 | for i in range(len(inversion_board)): 22 | first_item = inversion_board[i] 23 | for j in range(i+1, len(inversion_board)): 24 | second_item = inversion_board[j] 25 | if first_item > second_item: 26 | inversions += 1 27 | return inversions 28 | 29 | 30 | def is_solvable(): 31 | """ Функция определяющая имеет ли головоломка рещение """ 32 | num_inversions = get_inv_count() 33 | if BOARD_SIZE % 2 != 0: 34 | return num_inversions % 2 == 0 35 | else: 36 | empty_square_row = BOARD_SIZE - (board.index(EMPTY_SQUARE) // BOARD_SIZE) 37 | if empty_square_row % 2 == 0: 38 | return num_inversions % 2 != 0 39 | else: 40 | return num_inversions % 2 == 0 41 | 42 | 43 | def get_empty_neighbor(index): 44 | # получаем индекс пустой клетки в списке 45 | empty_index = board.index(EMPTY_SQUARE) 46 | # узнаем расстояние от пустой клетки до клетки по которой кликнули 47 | abs_value = abs(empty_index - index) 48 | # Если пустая клетка над или под клектой на которую кликнули 49 | # возвращаем индекс пустой клетки 50 | if abs_value == BOARD_SIZE: 51 | return empty_index 52 | # Если пустая клетка слева или справа 53 | elif abs_value == 1: 54 | # Проверяем, чтобы блоки были в одном ряду 55 | max_index = max(index, empty_index) 56 | if max_index % BOARD_SIZE != 0: 57 | return empty_index 58 | # Рядом с блоком не было пустого поля 59 | return index 60 | 61 | 62 | def draw_board(): 63 | # убираем все, что нарисовано на холсте 64 | c.delete('all') 65 | # Наша задача сгруппировать пятнашки из списка в квадрат 66 | # со сторонами BOARD_SIZE x BOARD_SIZE 67 | # i и j будут координатами для каждой отдельной пятнашки 68 | for i in range(BOARD_SIZE): 69 | for j in range(BOARD_SIZE): 70 | # получаем значение, которое мы должны будем нарисовать 71 | # на квадрате 72 | index = str(board[BOARD_SIZE * i + j]) 73 | # если это не клетка которую мы хотим оставить пустой 74 | if index != str(EMPTY_SQUARE): 75 | # рисуем квадрат по заданным координатам 76 | c.create_rectangle(j * SQUARE_SIZE, i * SQUARE_SIZE, 77 | j * SQUARE_SIZE + SQUARE_SIZE, 78 | i * SQUARE_SIZE + SQUARE_SIZE, 79 | fill='#43ABC9', 80 | outline='#FFFFFF') 81 | # пишем число в центре квадрата 82 | c.create_text(j * SQUARE_SIZE + SQUARE_SIZE / 2, 83 | i * SQUARE_SIZE + SQUARE_SIZE / 2, 84 | text=index, 85 | font="Arial {} italic".format(int(SQUARE_SIZE / 4)), 86 | fill='#FFFFFF') 87 | 88 | 89 | def show_victory_plate(): 90 | # Рисуем черный квадрат по центру поля 91 | c.create_rectangle(SQUARE_SIZE / 5, 92 | SQUARE_SIZE * BOARD_SIZE / 2 - 10 * BOARD_SIZE, 93 | BOARD_SIZE * SQUARE_SIZE - SQUARE_SIZE / 5, 94 | SQUARE_SIZE * BOARD_SIZE / 2 + 10 * BOARD_SIZE, 95 | fill='#000000', 96 | outline='#FFFFFF') 97 | # Пишем красным текст Победа 98 | c.create_text(SQUARE_SIZE * BOARD_SIZE / 2, SQUARE_SIZE * BOARD_SIZE / 1.9, 99 | text="ПОБЕДА!", font="Helvetica {} bold".format(int(10 * BOARD_SIZE)), fill='#DC143C') 100 | 101 | 102 | def click(event): 103 | # Получаем координаты клика 104 | x, y = event.x, event.y 105 | # Конвертируем координаты из пикселей в клеточки 106 | x = x // SQUARE_SIZE 107 | y = y // SQUARE_SIZE 108 | # Получаем индекс в списке объекта по которому мы нажали 109 | board_index = x + (y * BOARD_SIZE) 110 | # Получаем индекс пустой клетки в списке. Эту функцию мы напишем позже 111 | empty_index = get_empty_neighbor(board_index) 112 | # Меняем местами пустую клетку и клетку, по которой кликнули 113 | board[board_index], board[empty_index] = board[empty_index], board[board_index] 114 | # Перерисовываем игровое поле 115 | draw_board() 116 | # Если текущее состояние доски соответствует правильному - рисуем сообщение о победе 117 | if board == correct_board: 118 | # Эту функцию мы добавим позже 119 | show_victory_plate() 120 | 121 | 122 | c.bind('', click) 123 | c.pack() 124 | 125 | 126 | board = list(range(1, EMPTY_SQUARE + 1)) 127 | correct_board = board[:] 128 | shuffle(board) 129 | 130 | while not is_solvable(): 131 | shuffle(board) 132 | 133 | draw_board() 134 | root.mainloop() 135 | -------------------------------------------------------------------------------- /games/arcade_games/minesweeper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | __author__ = "Dmitriy Krasota aka g0t0wasd" 4 | 5 | # Minesweeper in Python using Tkinter. 6 | # More at http://pythonicway.com/python-games/python-arcade/31-python-minesweep 7 | 8 | 9 | from tkinter import * 10 | import random 11 | 12 | GRID_SIZE = 20 # Размер поля 13 | SQUARE_SIZE = 20 # Размер клетки 14 | MINES_NUM = 40 # Количество мин на поле 15 | mines = set(random.sample(range(1, GRID_SIZE**2+1), MINES_NUM)) # Устанавливаем случайным образом мины на поле 16 | clicked = set() # Сет, хранящий все клетки, по которым мы кликнули 17 | 18 | 19 | def check_mines(neighbors): 20 | """ Функция, возвращающая количество мин вокруг neighbors """ 21 | return len(mines.intersection(neighbors)) 22 | 23 | 24 | def generate_neighbors(square): 25 | """ Возвращает клетки соседствующие с square """ 26 | # Левая верхняя клетка 27 | if square == 1: 28 | data = {GRID_SIZE + 1, 2, GRID_SIZE + 2} 29 | # Правая нижняя 30 | elif square == GRID_SIZE ** 2: 31 | data = {square - GRID_SIZE, square - 1, square - GRID_SIZE - 1} 32 | # Левая нижняя 33 | elif square == GRID_SIZE: 34 | data = {GRID_SIZE - 1, GRID_SIZE * 2, GRID_SIZE * 2 - 1} 35 | # Верхняя правая 36 | elif square == GRID_SIZE ** 2 - GRID_SIZE + 1: 37 | data = {square + 1, square - GRID_SIZE, square - GRID_SIZE + 1} 38 | # Клетка в левом ряду 39 | elif square < GRID_SIZE: 40 | data = {square + 1, square - 1, square + GRID_SIZE, 41 | square + GRID_SIZE - 1, square + GRID_SIZE + 1} 42 | # Клетка в правом ряду 43 | elif square > GRID_SIZE ** 2 - GRID_SIZE: 44 | data = {square + 1, square - 1, square - GRID_SIZE, 45 | square - GRID_SIZE - 1, square - GRID_SIZE + 1} 46 | # Клетка в нижнем ряду 47 | elif square % GRID_SIZE == 0: 48 | data = {square + GRID_SIZE, square - GRID_SIZE, square - 1, 49 | square + GRID_SIZE - 1, square - GRID_SIZE - 1} 50 | # Клетка в верхнем ряду 51 | elif square % GRID_SIZE == 1: 52 | data = {square + GRID_SIZE, square - GRID_SIZE, square + 1, 53 | square + GRID_SIZE + 1, square - GRID_SIZE + 1} 54 | # Любая другая клетка 55 | else: 56 | data = {square - 1, square + 1, square - GRID_SIZE, square + GRID_SIZE, 57 | square - GRID_SIZE - 1, square - GRID_SIZE + 1, 58 | square + GRID_SIZE + 1, square + GRID_SIZE - 1} 59 | return data 60 | 61 | 62 | def clearance(ids): 63 | """ Итеративная (эффективная) функция очистки поля """ 64 | clicked.add(ids) # добавляем нажатую клетку в сет нажатых 65 | ids_neigh = generate_neighbors(ids) # Получаем все соседние клетки 66 | around = check_mines(ids_neigh) # высчитываем количество мин вокруг нажатой клетки 67 | c.itemconfig(ids, fill="green") # окрашиваем клетку в зеленый 68 | 69 | # Если вокруг мин нету 70 | if around == 0: 71 | # Создаем список соседних клеток 72 | neigh_list = list(ids_neigh) 73 | # Пока в списке соседей есть клетки 74 | while len(neigh_list) > 0: 75 | # Получаем клетку 76 | item = neigh_list.pop() 77 | # Окрашиваем ее в зеленый цвет 78 | c.itemconfig(item, fill="green") 79 | # Получаем соседение клетки данной клетки 80 | item_neigh = generate_neighbors(item) 81 | # Получаем количество мин в соседних клетках 82 | item_around = check_mines(item_neigh) 83 | # Если в соседних клетках есть мины 84 | if item_around > 0: 85 | # Делаем эту проверку, чтобы писать по нескольку раз на той же клетке 86 | if item not in clicked: 87 | # Получаем координаты этой клетки 88 | x1, y1, x2, y2 = c.coords(item) 89 | # Пишем на клетке количество мин вокруг 90 | c.create_text(x1 + SQUARE_SIZE / 2, 91 | y1 + SQUARE_SIZE / 2, 92 | text=str(item_around), 93 | font="Arial {}".format(int(SQUARE_SIZE / 2)), 94 | fill='yellow') 95 | # Если в соседних клетках мин нету 96 | else: 97 | # Добавляем соседние клетки данной клетки в общий список 98 | neigh_list.extend(set(item_neigh).difference(clicked)) 99 | # Убираем повторяющиеся элементы из общего списка 100 | neigh_list = list(set(neigh_list)) 101 | # Добавляем клетку в нажатые 102 | clicked.add(item) 103 | # Если мины вокруг есть 104 | else: 105 | # Высчитываем координаты клетки 106 | x1, y1, x2, y2 = c.coords(ids) 107 | # Пишем количество мин вокруг 108 | c.create_text(x1 + SQUARE_SIZE / 2, 109 | y1 + SQUARE_SIZE / 2, 110 | text=str(around), 111 | font="Arial {}".format(int(SQUARE_SIZE / 2)), 112 | fill='yellow') 113 | 114 | 115 | def rec_clearance(ids): 116 | """ Рекурсивная (неэффективная) функция очистки поля """ 117 | clicked.add(ids) 118 | neighbors = generate_neighbors(ids) 119 | around = check_mines(neighbors) 120 | if around: 121 | x1, y1, x2, y2 = c.coords(ids) 122 | c.itemconfig(ids, fill="green") 123 | c.create_text(x1 + SQUARE_SIZE / 2, 124 | y1 + SQUARE_SIZE / 2, 125 | text=str(around), 126 | font="Arial {}".format(int(SQUARE_SIZE / 2)), 127 | fill='yellow') 128 | else: 129 | for item in set(neighbors).difference(clicked): 130 | c.itemconfig(item, fill="green") 131 | rec_clearance(item) 132 | 133 | 134 | def click(event): 135 | ids = c.find_withtag(CURRENT)[0] 136 | if ids in mines: 137 | c.itemconfig(CURRENT, fill="red") 138 | elif ids not in clicked: 139 | clearance(ids) 140 | c.itemconfig(CURRENT, fill="green") 141 | c.update() 142 | 143 | 144 | def mark_mine(event): 145 | ids = c.find_withtag(CURRENT)[0] 146 | if ids not in clicked: 147 | clicked.add(ids) 148 | x1, y1, x2, y2 = c.coords(ids) 149 | c.itemconfig(CURRENT, fill="yellow") 150 | else: 151 | clicked.remove(ids) 152 | c.itemconfig(CURRENT, fill="gray") 153 | 154 | 155 | root = Tk() 156 | root.title("Pythonicway Minesweep") 157 | c = Canvas(root, width=GRID_SIZE * SQUARE_SIZE, height=GRID_SIZE * SQUARE_SIZE) 158 | c.bind("", click) 159 | c.bind("", mark_mine) 160 | c.pack() 161 | for i in range(GRID_SIZE): 162 | for j in range(GRID_SIZE): 163 | c.create_rectangle(i * SQUARE_SIZE, j * SQUARE_SIZE, 164 | i * SQUARE_SIZE + SQUARE_SIZE, 165 | j * SQUARE_SIZE + SQUARE_SIZE, fill='gray') 166 | root.mainloop() 167 | -------------------------------------------------------------------------------- /games/arcade_games/pong.py: -------------------------------------------------------------------------------- 1 | from tkinter import * 2 | # импортируем библиотеку random 3 | import random 4 | 5 | # Добавляем глобальные переменные 6 | 7 | # глобальные переменные 8 | # настройки окна 9 | WIDTH = 900 10 | HEIGHT = 300 11 | 12 | # настройки ракеток 13 | 14 | # ширина ракетки 15 | PAD_W = 10 16 | # высота ракетки 17 | PAD_H = 100 18 | 19 | # настройки мяча 20 | # Насколько будет увеличиваться скорость мяча с каждым ударом 21 | BALL_SPEED_UP = 1.05 22 | # Максимальная скорость мяча 23 | BALL_MAX_SPEED = 40 24 | # радиус мяча 25 | BALL_RADIUS = 30 26 | 27 | INITIAL_SPEED = 20 28 | BALL_X_SPEED = INITIAL_SPEED 29 | BALL_Y_SPEED = INITIAL_SPEED 30 | 31 | # Счет игроков 32 | PLAYER_1_SCORE = 0 33 | PLAYER_2_SCORE = 0 34 | 35 | # Добавим глобальную переменную отвечающую за расстояние 36 | # до правого края игрового поля 37 | right_line_distance = WIDTH - PAD_W 38 | 39 | def update_score(player): 40 | global PLAYER_1_SCORE, PLAYER_2_SCORE 41 | if player == "right": 42 | PLAYER_1_SCORE += 1 43 | c.itemconfig(p_1_text, text=PLAYER_1_SCORE) 44 | else: 45 | PLAYER_2_SCORE += 1 46 | c.itemconfig(p_2_text, text=PLAYER_2_SCORE) 47 | 48 | def spawn_ball(): 49 | global BALL_X_SPEED 50 | # Выставляем мяч по центру 51 | c.coords(BALL, WIDTH/2-BALL_RADIUS/2, 52 | HEIGHT/2-BALL_RADIUS/2, 53 | WIDTH/2+BALL_RADIUS/2, 54 | HEIGHT/2+BALL_RADIUS/2) 55 | # Задаем мячу направление в сторону проигравшего игрока, 56 | # но снижаем скорость до изначальной 57 | BALL_X_SPEED = -(BALL_X_SPEED * -INITIAL_SPEED) / abs(BALL_X_SPEED) 58 | 59 | # функция отскока мяча 60 | def bounce(action): 61 | global BALL_X_SPEED, BALL_Y_SPEED 62 | # ударили ракеткой 63 | if action == "strike": 64 | BALL_Y_SPEED = random.randrange(-10, 10) 65 | if abs(BALL_X_SPEED) < BALL_MAX_SPEED: 66 | BALL_X_SPEED *= -BALL_SPEED_UP 67 | else: 68 | BALL_X_SPEED = -BALL_X_SPEED 69 | else: 70 | BALL_Y_SPEED = -BALL_Y_SPEED 71 | 72 | # устанавливаем окно 73 | root = Tk() 74 | root.title("PythonicWay Pong") 75 | 76 | # область анимации 77 | c = Canvas(root, width=WIDTH, height=HEIGHT, background="#003300") 78 | c.pack() 79 | 80 | # элементы игрового поля 81 | 82 | # левая линия 83 | c.create_line(PAD_W, 0, PAD_W, HEIGHT, fill="white") 84 | # правая линия 85 | c.create_line(WIDTH-PAD_W, 0, WIDTH-PAD_W, HEIGHT, fill="white") 86 | # центральная линия 87 | c.create_line(WIDTH/2, 0, WIDTH/2, HEIGHT, fill="white") 88 | 89 | # установка игровых объектов 90 | 91 | # создаем мяч 92 | BALL = c.create_oval(WIDTH/2-BALL_RADIUS/2, 93 | HEIGHT/2-BALL_RADIUS/2, 94 | WIDTH/2+BALL_RADIUS/2, 95 | HEIGHT/2+BALL_RADIUS/2, fill="white") 96 | 97 | # левая ракетка 98 | LEFT_PAD = c.create_line(PAD_W/2, 0, PAD_W/2, PAD_H, width=PAD_W, fill="yellow") 99 | 100 | # правая ракетка 101 | RIGHT_PAD = c.create_line(WIDTH-PAD_W/2, 0, WIDTH-PAD_W/2, 102 | PAD_H, width=PAD_W, fill="yellow") 103 | 104 | 105 | p_1_text = c.create_text(WIDTH-WIDTH/6, PAD_H/4, 106 | text=PLAYER_1_SCORE, 107 | font="Arial 20", 108 | fill="white") 109 | 110 | p_2_text = c.create_text(WIDTH/6, PAD_H/4, 111 | text=PLAYER_2_SCORE, 112 | font="Arial 20", 113 | fill="white") 114 | 115 | # добавим глобальные переменные для скорости движения мяча 116 | # по горизонтали 117 | BALL_X_CHANGE = 20 118 | # по вертикали 119 | BALL_Y_CHANGE = 0 120 | 121 | def move_ball(): 122 | # определяем координаты сторон мяча и его центра 123 | ball_left, ball_top, ball_right, ball_bot = c.coords(BALL) 124 | ball_center = (ball_top + ball_bot) / 2 125 | 126 | # вертикальный отскок 127 | # Если мы далеко от вертикальных линий - просто двигаем мяч 128 | if ball_right + BALL_X_SPEED < right_line_distance and \ 129 | ball_left + BALL_X_SPEED > PAD_W: 130 | c.move(BALL, BALL_X_SPEED, BALL_Y_SPEED) 131 | # Если мяч касается своей правой или левой стороной границы поля 132 | elif ball_right == right_line_distance or ball_left == PAD_W: 133 | # Проверяем правой или левой стороны мы касаемся 134 | if ball_right > WIDTH / 2: 135 | # Если правой, то сравниваем позицию центра мяча 136 | # с позицией правой ракетки. 137 | # И если мяч в пределах ракетки делаем отскок 138 | if c.coords(RIGHT_PAD)[1] < ball_center < c.coords(RIGHT_PAD)[3]: 139 | bounce("strike") 140 | else: 141 | # Иначе игрок пропустил - тут оставим пока pass, его мы заменим на подсчет очков и респаун мячика 142 | update_score("left") 143 | spawn_ball() 144 | else: 145 | # То же самое для левого игрока 146 | if c.coords(LEFT_PAD)[1] < ball_center < c.coords(LEFT_PAD)[3]: 147 | bounce("strike") 148 | else: 149 | update_score("right") 150 | spawn_ball() 151 | # Проверка ситуации, в которой мячик может вылететь за границы игрового поля. 152 | # В таком случае просто двигаем его к границе поля. 153 | else: 154 | if ball_right > WIDTH / 2: 155 | c.move(BALL, right_line_distance-ball_right, BALL_Y_SPEED) 156 | else: 157 | c.move(BALL, -ball_left+PAD_W, BALL_Y_SPEED) 158 | # горизонтальный отскок 159 | if ball_top + BALL_Y_SPEED < 0 or ball_bot + BALL_Y_SPEED > HEIGHT: 160 | bounce("ricochet") 161 | 162 | # зададим глобальные переменные скорости движения ракеток 163 | # скорось с которой будут ездить ракетки 164 | PAD_SPEED = 20 165 | # скорость левой платформы 166 | LEFT_PAD_SPEED = 0 167 | # скорость правой ракетки 168 | RIGHT_PAD_SPEED = 0 169 | 170 | # функция движения обеих ракеток 171 | def move_pads(): 172 | # для удобства создадим словарь, где ракетке соответствует ее скорость 173 | PADS = {LEFT_PAD: LEFT_PAD_SPEED, 174 | RIGHT_PAD: RIGHT_PAD_SPEED} 175 | # перебираем ракетки 176 | for pad in PADS: 177 | # двигаем ракетку с заданной скоростью 178 | c.move(pad, 0, PADS[pad]) 179 | # если ракетка вылезает за игровое поле возвращаем ее на место 180 | if c.coords(pad)[1] < 0: 181 | c.move(pad, 0, -c.coords(pad)[1]) 182 | elif c.coords(pad)[3] > HEIGHT: 183 | c.move(pad, 0, HEIGHT - c.coords(pad)[3]) 184 | 185 | 186 | def main(): 187 | move_ball() 188 | move_pads() 189 | # вызываем саму себя каждые 30 миллисекунд 190 | root.after(30, main) 191 | 192 | # Установим фокус на Canvas чтобы он реагировал на нажатия клавиш 193 | c.focus_set() 194 | 195 | # Напишем функцию обработки нажатия клавиш 196 | def movement_handler(event): 197 | global LEFT_PAD_SPEED, RIGHT_PAD_SPEED 198 | if event.keysym == "w": 199 | LEFT_PAD_SPEED = -PAD_SPEED 200 | elif event.keysym == "s": 201 | LEFT_PAD_SPEED = PAD_SPEED 202 | elif event.keysym == "Up": 203 | RIGHT_PAD_SPEED = -PAD_SPEED 204 | elif event.keysym == "Down": 205 | RIGHT_PAD_SPEED = PAD_SPEED 206 | 207 | # Привяжем к Canvas эту функцию 208 | c.bind("", movement_handler) 209 | 210 | # Создадим функцию реагирования на отпускание клавиши 211 | def stop_pad(event): 212 | global LEFT_PAD_SPEED, RIGHT_PAD_SPEED 213 | if event.keysym in "ws": 214 | LEFT_PAD_SPEED = 0 215 | elif event.keysym in ("Up", "Down"): 216 | RIGHT_PAD_SPEED = 0 217 | 218 | # Привяжем к Canvas эту функцию 219 | c.bind("", stop_pad) 220 | 221 | # запускаем движение 222 | main() 223 | 224 | # запускаем работу окна 225 | root.mainloop() -------------------------------------------------------------------------------- /games/arcade_games/snake.py: -------------------------------------------------------------------------------- 1 | from tkinter import Tk, Canvas 2 | import random 3 | 4 | # Globals 5 | WIDTH = 800 6 | HEIGHT = 600 7 | SEG_SIZE = 20 8 | IN_GAME = True 9 | 10 | 11 | # Helper functions 12 | def create_block(): 13 | """ Creates an apple to be eaten """ 14 | global BLOCK 15 | posx = SEG_SIZE * random.randint(1, (WIDTH-SEG_SIZE) / SEG_SIZE) 16 | posy = SEG_SIZE * random.randint(1, (HEIGHT-SEG_SIZE) / SEG_SIZE) 17 | BLOCK = c.create_oval(posx, posy, 18 | posx+SEG_SIZE, posy+SEG_SIZE, 19 | fill="red") 20 | 21 | 22 | def main(): 23 | """ Handles game process """ 24 | global IN_GAME 25 | if IN_GAME: 26 | s.move() 27 | head_coords = c.coords(s.segments[-1].instance) 28 | x1, y1, x2, y2 = head_coords 29 | # Check for collision with gamefield edges 30 | if x2 > WIDTH or x1 < 0 or y1 < 0 or y2 > HEIGHT: 31 | IN_GAME = False 32 | # Eating apples 33 | elif head_coords == c.coords(BLOCK): 34 | s.add_segment() 35 | c.delete(BLOCK) 36 | create_block() 37 | # Self-eating 38 | else: 39 | for index in range(len(s.segments)-1): 40 | if head_coords == c.coords(s.segments[index].instance): 41 | IN_GAME = False 42 | root.after(100, main) 43 | # Not IN_GAME -> stop game and print message 44 | else: 45 | set_state(restart_text, 'normal') 46 | set_state(game_over_text, 'normal') 47 | 48 | 49 | class Segment(object): 50 | """ Single snake segment """ 51 | def __init__(self, x, y): 52 | self.instance = c.create_rectangle(x, y, 53 | x+SEG_SIZE, y+SEG_SIZE, 54 | fill="white") 55 | 56 | 57 | class Snake(object): 58 | """ Simple Snake class """ 59 | def __init__(self, segments): 60 | self.segments = segments 61 | # possible moves 62 | self.mapping = {"Down": (0, 1), "Right": (1, 0), 63 | "Up": (0, -1), "Left": (-1, 0)} 64 | # initial movement direction 65 | self.vector = self.mapping["Right"] 66 | 67 | def move(self): 68 | """ Moves the snake with the specified vector""" 69 | for index in range(len(self.segments)-1): 70 | segment = self.segments[index].instance 71 | x1, y1, x2, y2 = c.coords(self.segments[index+1].instance) 72 | c.coords(segment, x1, y1, x2, y2) 73 | 74 | x1, y1, x2, y2 = c.coords(self.segments[-2].instance) 75 | c.coords(self.segments[-1].instance, 76 | x1+self.vector[0]*SEG_SIZE, y1+self.vector[1]*SEG_SIZE, 77 | x2+self.vector[0]*SEG_SIZE, y2+self.vector[1]*SEG_SIZE) 78 | 79 | def add_segment(self): 80 | """ Adds segment to the snake """ 81 | last_seg = c.coords(self.segments[0].instance) 82 | x = last_seg[2] - SEG_SIZE 83 | y = last_seg[3] - SEG_SIZE 84 | self.segments.insert(0, Segment(x, y)) 85 | 86 | def change_direction(self, event): 87 | """ Changes direction of snake """ 88 | if event.keysym in self.mapping: 89 | self.vector = self.mapping[event.keysym] 90 | 91 | def reset_snake(self): 92 | for segment in self.segments: 93 | c.delete(segment.instance) 94 | 95 | 96 | def set_state(item, state): 97 | c.itemconfigure(item, state=state) 98 | 99 | 100 | def clicked(event): 101 | global IN_GAME 102 | s.reset_snake() 103 | IN_GAME = True 104 | c.delete(BLOCK) 105 | c.itemconfigure(restart_text, state='hidden') 106 | c.itemconfigure(game_over_text, state='hidden') 107 | start_game() 108 | 109 | 110 | def start_game(): 111 | global s 112 | create_block() 113 | s = create_snake() 114 | # Reaction on keypress 115 | c.bind("", s.change_direction) 116 | main() 117 | 118 | 119 | def create_snake(): 120 | # creating segments and snake 121 | segments = [Segment(SEG_SIZE, SEG_SIZE), 122 | Segment(SEG_SIZE*2, SEG_SIZE), 123 | Segment(SEG_SIZE*3, SEG_SIZE)] 124 | return Snake(segments) 125 | 126 | 127 | # Setting up window 128 | root = Tk() 129 | root.title("PythonicWay Snake") 130 | 131 | 132 | c = Canvas(root, width=WIDTH, height=HEIGHT, bg="#003300") 133 | c.grid() 134 | # catch keypressing 135 | c.focus_set() 136 | game_over_text = c.create_text(WIDTH/2, HEIGHT/2, text="GAME OVER!", 137 | font='Arial 20', fill='red', 138 | state='hidden') 139 | restart_text = c.create_text(WIDTH/2, HEIGHT-HEIGHT/3, 140 | font='Arial 30', 141 | fill='white', 142 | text="Click here to restart", 143 | state='hidden') 144 | c.tag_bind(restart_text, "", clicked) 145 | start_game() 146 | root.mainloop() 147 | -------------------------------------------------------------------------------- /games/terminal_games/xos.py: -------------------------------------------------------------------------------- 1 | board = list(range(1,10)) 2 | 3 | def draw_board(board): 4 | print ("-" * 13) 5 | for i in range(3): 6 | print ("|", board[0+i*3], "|", board[1+i*3], "|", board[2+i*3], "|") 7 | print ("-" * 13) 8 | 9 | def take_input(player_token): 10 | valid = False 11 | while not valid: 12 | player_answer = input("Куда поставим " + player_token+"? ") 13 | try: 14 | player_answer = int(player_answer) 15 | except: 16 | print ("Некорректный ввод. Вы уверены, что ввели число?") 17 | continue 18 | if player_answer >= 1 and player_answer <= 9: 19 | if (str(board[player_answer-1]) not in "XO"): 20 | board[player_answer-1] = player_token 21 | valid = True 22 | else: 23 | print ("Эта клеточка уже занята") 24 | else: 25 | print ("Некорректный ввод. Введите число от 1 до 9 чтобы походить.") 26 | 27 | def check_win(board): 28 | win_coord = ((0,1,2),(3,4,5),(6,7,8),(0,3,6),(1,4,7),(2,5,8),(0,4,8),(2,4,6)) 29 | for each in win_coord: 30 | if board[each[0]] == board[each[1]] == board[each[2]]: 31 | return board[each[0]] 32 | return False 33 | 34 | def main(board): 35 | counter = 0 36 | win = False 37 | while not win: 38 | draw_board(board) 39 | if counter % 2 == 0: 40 | take_input("X") 41 | else: 42 | take_input("O") 43 | counter += 1 44 | if counter > 4: 45 | tmp = check_win(board) 46 | if tmp: 47 | print (tmp, "выиграл!") 48 | win = True 49 | break 50 | if counter == 9: 51 | print ("Ничья!") 52 | break 53 | draw_board(board) 54 | 55 | main(board) -------------------------------------------------------------------------------- /programs/gui/PyPaint.py: -------------------------------------------------------------------------------- 1 | from tkinter import * 2 | 3 | 4 | class Paint(Frame): 5 | 6 | def __init__(self, parent): 7 | Frame.__init__(self, parent) 8 | 9 | self.parent = parent 10 | self.color = "black" 11 | self.brush_size = 2 12 | self.setUI() 13 | 14 | def set_color(self, new_color): 15 | self.color = new_color 16 | 17 | def set_brush_size(self, new_size): 18 | self.brush_size = new_size 19 | 20 | def draw(self, event): 21 | self.canv.create_oval(event.x - self.brush_size, 22 | event.y - self.brush_size, 23 | event.x + self.brush_size, 24 | event.y + self.brush_size, 25 | fill=self.color, outline=self.color) 26 | 27 | def setUI(self): 28 | 29 | self.parent.title("Pythonicway PyPaint") # Устанавливаем название окна 30 | self.pack(fill=BOTH, expand=1) # Размещаем активные элементы на родительском окне 31 | 32 | self.columnconfigure(6, weight=1) # Даем седьмому столбцу возможность растягиваться, благодаря чему кнопки не будут разъезжаться при ресайзе 33 | self.rowconfigure(2, weight=1) # То же самое для третьего ряда 34 | 35 | self.canv = Canvas(self, bg="white") # Создаем поле для рисования, устанавливаем белый фон 36 | self.canv.grid(row=2, column=0, columnspan=7, 37 | padx=5, pady=5, sticky=E+W+S+N) # Прикрепляем канвас методом grid. Он будет находится в 3м ряду, первой колонке, и будет занимать 7 колонок, задаем отступы по X и Y в 5 пикселей, и заставляем растягиваться при растягивании всего окна 38 | self.canv.bind("", self.draw) # Привязываем обработчик к канвасу. означает "при движении зажатой левой кнопки мыши" вызывать функцию draw 39 | 40 | color_lab = Label(self, text="Color: ") # Создаем метку для кнопок изменения цвета кисти 41 | color_lab.grid(row=0, column=0, padx=6) # Устанавливаем созданную метку в первый ряд и первую колонку, задаем горизонтальный отступ в 6 пикселей 42 | 43 | red_btn = Button(self, text="Red", width=10, 44 | command=lambda: self.set_color("red")) # Создание кнопки: Установка текста кнопки, задание ширины кнопки (10 символов), функция вызываемая при нажатии кнопки. 45 | red_btn.grid(row=0, column=1) # Устанавливаем кнопку 46 | 47 | # Создание остальных кнопок повторяет ту же логику, что и создание 48 | # кнопки установки красного цвета, отличаются лишь аргументы. 49 | 50 | green_btn = Button(self, text="Green", width=10, 51 | command=lambda: self.set_color("green")) 52 | green_btn.grid(row=0, column=2) 53 | 54 | blue_btn = Button(self, text="Blue", width=10, 55 | command=lambda: self.set_color("blue")) 56 | blue_btn.grid(row=0, column=3) 57 | 58 | black_btn = Button(self, text="Black", width=10, 59 | command=lambda: self.set_color("black")) 60 | black_btn.grid(row=0, column=4) 61 | 62 | white_btn = Button(self, text="White", width=10, 63 | command=lambda: self.set_color("white")) 64 | white_btn.grid(row=0, column=5) 65 | 66 | clear_btn = Button(self, text="Clear all", width=10, 67 | command=lambda: self.canv.delete("all")) 68 | clear_btn.grid(row=0, column=6, sticky=W) 69 | 70 | size_lab = Label(self, text="Brush size: ") 71 | size_lab.grid(row=1, column=0, padx=5) 72 | one_btn = Button(self, text="Two", width=10, 73 | command=lambda: self.set_brush_size(2)) 74 | one_btn.grid(row=1, column=1) 75 | 76 | two_btn = Button(self, text="Five", width=10, 77 | command=lambda: self.set_brush_size(5)) 78 | two_btn.grid(row=1, column=2) 79 | 80 | five_btn = Button(self, text="Seven", width=10, 81 | command=lambda: self.set_brush_size(7)) 82 | five_btn.grid(row=1, column=3) 83 | 84 | seven_btn = Button(self, text="Ten", width=10, 85 | command=lambda: self.set_brush_size(10)) 86 | seven_btn.grid(row=1, column=4) 87 | 88 | ten_btn = Button(self, text="Twenty", width=10, 89 | command=lambda: self.set_brush_size(20)) 90 | ten_btn.grid(row=1, column=5) 91 | 92 | twenty_btn = Button(self, text="Fifty", width=10, 93 | command=lambda: self.set_brush_size(50)) 94 | twenty_btn.grid(row=1, column=6, sticky=W) 95 | 96 | 97 | def main(): 98 | root = Tk() 99 | root.geometry("850x500+300+300") 100 | app = Paint(root) 101 | root.mainloop() 102 | 103 | 104 | if __name__ == '__main__': 105 | main() 106 | -------------------------------------------------------------------------------- /programs/gui/q_calc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | __author__ = "Dmitriy Krasota aka g0t0wasd" 4 | 5 | # An example of Quadratic Calc using Tkinter. 6 | # More at http://pythonicway.com/index.php/python-examples/python-gui-examples/14-python-tkinter-quadratic-equations 7 | 8 | 9 | from Tkinter import * 10 | from math import sqrt 11 | 12 | def solver(a,b,c): 13 | """ Solves quadratic equation and returns the result in formatted string """ 14 | D = b*b - 4*a*c 15 | if D >= 0: 16 | x1 = (-b + sqrt(D)) / (2*a) 17 | x2 = (-b - sqrt(D)) / (2*a) 18 | text = "The discriminant is: %s \n X1 is: %s \n X2 is: %s \n" % (D, x1, x2) 19 | else: 20 | text = "The discriminant is: %s \n This equation has no solutions" % D 21 | return text 22 | 23 | def inserter(value): 24 | """ Inserts specified value into text widget """ 25 | output.delete("0.0","end") 26 | output.insert("0.0",value) 27 | 28 | def clear(event): 29 | """ Clears entry form """ 30 | caller = event.widget 31 | caller.delete("0", "end") 32 | 33 | def handler(): 34 | """ Get the content of entries and passes result to the text """ 35 | try: 36 | # make sure that we entered correct values 37 | a_val = float(a.get()) 38 | b_val = float(b.get()) 39 | c_val = float(c.get()) 40 | inserter(solver(a_val, b_val, c_val)) 41 | except ValueError: 42 | inserter("Make sure you entered 3 numbers") 43 | 44 | root = Tk() 45 | root.title("Quadratic calculator") 46 | root.minsize(325,230) 47 | root.resizable(width=False, height=False) 48 | 49 | 50 | frame = Frame(root) 51 | frame.grid() 52 | 53 | a = Entry(frame, width=3) 54 | a.grid(row=1,column=1,padx=(10,0)) 55 | a.bind("", clear) 56 | a_lab = Label(frame, text="x**2+").grid(row=1,column=2) 57 | 58 | b = Entry(frame, width=3) 59 | b.bind("", clear) 60 | b.grid(row=1,column=3) 61 | b_lab = Label(frame, text="x+").grid(row=1, column=4) 62 | 63 | c = Entry(frame, width=3) 64 | c.bind("", clear) 65 | c.grid(row=1, column=5) 66 | c_lab = Label(frame, text="= 0").grid(row=1, column=6) 67 | 68 | but = Button(frame, text="Solve", command=handler).grid(row=1, column=7, padx=(10,0)) 69 | 70 | output = Text(frame, bg="lightblue", font="Arial 12", width=35, height=10) 71 | output.grid(row=2, columnspan=8) 72 | 73 | root.mainloop() 74 | --------------------------------------------------------------------------------