-
Notifications
You must be signed in to change notification settings - Fork 0
/
tic - tac - toe
229 lines (201 loc) · 10.1 KB
/
tic - tac - toe
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
import random
x_o_score = [0, 0] # счёт непрерывной игры
# БЛОК ПРИВЕТСТВИЯ игрока(ов)
def introduction(func):
def wrapper():
print("\t\t Приветствуем вас ")
print("\t\t в игре ")
print("\t\t крестики-нолики ")
func()
return wrapper
@introduction
def start(): # БЛОК ВВОДА стартовой информации
while True:
print("Какой режим выберете?")
mode = input(f"\n1. Игра против суперинтелекта\n2. Игра против человека\n")
try:
mode = int(mode)
except ValueError:
print("Некорректный ввод. Повторите пожалуйста")
continue
mode = int(mode)
if mode > 2 or mode < 1:
print("Некорректный ввод. Повторите пожалуйста")
continue
break
return main(mode, )
# БЛОК ВЫБОРА НАСТРОЕК игры
def main(mode):
board = list(range(1, 10))
if mode == 2:
return pvp_for_xo(board)
else:
return pve_for_xo(board)
# БЛОК УПРАВЛЕНИЯ игрой с человеком
def pvp_for_xo(board):
_counter = 0 # Счетчик ходов
win = False
while not win:
outline(board)
if _counter % 2 == 0:
play_mark("X", board)
else:
play_mark("O", board)
_counter += 1
if _counter > 4:
_tmp = check_win(board)
if _tmp:
print(_tmp, "выиграл!")
break
if _counter == 9:
print("Ничья!")
break
return new_game()
# БЛОК УПРАВЛЕНИЯ игрой с компьютером
def pve_for_xo(board):
_counter = 0 # Счетчик ходов
win = False
while True:
pl_xo = input(f"Выберите:\n1. Крестик\n2. Нолик")
try:
pl_xo = int(pl_xo)
except ValueError:
print("Некорректный ввод. Повторите пожалуйста")
continue
pl_xo = int(pl_xo) - 1
if pl_xo > 1 or pl_xo < -1:
print('Некорректный ввод. Повторите пожалуйста')
continue
break
while not win:
outline(board)
if pl_xo == 0:
if _counter % 2 == 0: # если человек выбрал Х и ход четный(+ нулевой)
play_mark("X", board)
else:
ai_xo(board, "X") # В Блок вычисления хода Ai передается "X". Символ используется блоком.
else:
if _counter % 2 == 0: # если человек выбрал 0 и ход четный -- ходит Ai
ai_xo(board, "O") # В Блок вычисления хода Ai передается "O".
else:
play_mark("O", board) # Если человек выбрал "O" и ход нечетный -- его очередь.
_counter += 1
if _counter > 4:
_tmp = check_win(board)
if _tmp:
score(_tmp)
print(_tmp, "выиграл!")
break
if _counter == 9:
print("Ничья!")
break
return new_game()
# БЛОК ОТРИСОВКИ игровой матрицы и оформления
def outline(board):
if len(board) == 9:
print("-" * 13)
for i in range(3):
print("]", board[0 + i * 3], "|", board[1 + i * 3], "|", board[2 + i * 3], "[")
print("-" * 13)
# БЛОК ИЗМЕНЕНИЯ значений игровой матрицы(board)
def play_mark(mark, board):
_choice = False
while not _choice:
if type(mark) != list: # Если передана строка, значит ход человека. Нужно спросить куда. Если Список -- AI
answer = input(f" Ход {mark}! ")
try:
answer = int(answer)
except ValueError:
print("Ошибка ввода. Повторите пожалуйста")
continue
else:
answer = mark[0]
mark = mark[1]
answer = int(answer)
if 1 <= answer <= 9:
if str(board[answer - 1]) not in "XO":
board[answer - 1] = mark
_choice = True
else:
print("Клетка занята")
else:
print("Ошибка ввода. Повторите пожалуйста")
# БЛОК ВЫЧИСЛЕНИЯ ХОДА Ai
def ai_xo(board, pl_xo): # pl_xo = X => Ai = O
perception = [board[i:i + 3] for i in range(0, len(board), 3)]
horizontal = perception.copy()
vertical = perception.copy()
# Формируем список позиций, в которых Ai должен сделать вынужденный ход(X,X,О! - защита; O,O,О! - победа)
# [[123],[..][..]], если есть pl_xo вместо числа -- удаляем. Оцениваем по длине списка и кол-вy знаков AI
horizontal_ = [[f for f in element if f != pl_xo] for i, element in enumerate(horizontal)] # [[.],[.],[.],[.].... ]
vertical_ = [list(reversed(col)) for col in zip(*vertical)] # Источник № 2. Поворот матрицы на 90 градусов
vertical_ = [[f for f in element if f != pl_xo] for i, element in enumerate(vertical_)] # -//-
diagonal_board = [board[0], board[4], board[8], board[2], board[4], board[6]] # создаем 2 подсписка для диагоналей
perception_diagonal = [diagonal_board[i:i + 3] for i in range(0, len(diagonal_board), 3)]
diagonal = [[f for f in element if f != pl_xo] for i, element in enumerate(perception_diagonal)]
diagonal_ = diagonal.copy()
positions = horizontal_ + vertical_ + diagonal_ # объединяем в базу все возможные варианты
if pl_xo == "X": # Выбор человека
ai_sim = "O" # Символ Ai
else:
ai_sim = "X"
for cell in positions:
if (cell.count(ai_sim) == 2) and (len(cell) == 3):
cell.remove(ai_sim) # Победа в приоритете. Если ход и 2 символа Ai, то он ставит третий
cell.remove(ai_sim)
mark = [cell[0], ai_sim]
return play_mark(mark, board)
for cell in positions: # тк символы противника удаляются, если длина ряда = 1, но это не ai_sim, то защищается
if (len(cell) == 1) and (ai_sim not in cell):
mark = [cell[0], ai_sim]
return play_mark(mark, board)
count = [] # хранилище рядов
for cell in positions: # смотрит куда сходил человек, складирует ряды, будет бить в один из них
if len(cell) == 2 and (ai_sim not in cell): # Исключает ряды где есть и "Х", и "О" ['X', 'O', 4].
count.append(cell)
if (count != []) and (str(perception[1][1]) not in "XO"): # если Ai ходит второй, обязательный ход в центр
mark = [perception[1][1], ai_sim] # иначе проигрыш при правильной игре человека
return play_mark(mark, board)
if count: # если вынужденных ходов нет, и хранилище не пустое, тогда мешаем человеку
mark = [random.choice(random.choice(count)), ai_sim] # берем рандомный ряд и не занятую позицию
return play_mark(mark, board)
if not count: # Remains(остаток) отслеживает сколько чисел в поле. Хранилище пустое при 1 ходе и при явной ничье.
remains = [[f for f in element if f != "X" if f != "O"] for i, element in enumerate(perception)]
if len(remains[0] + remains[1] + remains[2]) == 9:
mark = [random.choice(random.choice(diagonal)), ai_sim] # AI в первый ход эффективнее сходить в любую
else: # позицию на диагоналях
mark = [random.choice(remains[0] + remains[1] + remains[2]), ai_sim] # ходим в любое поле из оставшихся
return play_mark(mark, board)
# БЛОК СРАВНЕНИЯ текущих значений в матрице с выигрышными
def check_win(board):
victory_condition = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6))
for combination in victory_condition:
if board[combination[0]] == board[combination[1]] == board[combination[2]]:
return board[combination[0]] # победителя определим например по первому символу ряда
return False
# БЛОК ПОДСЧЕТА ОЧКОВ
def score(_tmp):
if _tmp == 'X':
x_o_score[0] += 1
else:
x_o_score[1] += 1
print(f'X vs O is {x_o_score[0]}:{x_o_score[1]}')
# БЛОК ВЫБОРА финального действия
def new_game():
while True:
restart = input("1. Новая игра\n2. Выход из игры")
try:
restart = int(restart)
except ValueError:
print("Некорректный ввод. Повторите пожалуйста")
continue
restart = int(restart)
if restart > 2 or restart < 1:
print('Некорректный ввод. Повторите пожалуйста')
continue
break
if restart == 1:
return start()
else:
print("Завершение работы...")
start()