-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjogo_do_galo.py
484 lines (338 loc) · 13.4 KB
/
jogo_do_galo.py
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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
# 99266 Luis Fonseca
def eh_tabuleiro(var):
# eh_tabuleiro: universal -> booleano
"""Devolve se o seu argumento corresponde a um tabuleiro."""
if not (type(var) == tuple and len(var) == 3):
return False
for linha in var:
if not (type(linha) == tuple and len(linha) == 3):
return False
for cell in linha:
if not (type(cell) == int and -1 <= cell <= 1):
return False
return True
def eh_posicao(var):
# eh_posicao: universal -> booleano
"""Devolve se o seu argumento corresponde a uma posicao."""
return type(var) == int and 1 <= var <= 9
def obter_coluna(tab, n):
# obter_coluna: tabuleiro x inteiro -> tuplo
"""Devolve um tuplo com os valores da coluna correspondente ao inteiro
dado."""
if not (eh_tabuleiro(tab) and type(n) == int and 1 <= n <= 3):
raise ValueError('obter_coluna: algum dos argumentos e invalido')
vect = ()
for linha in tab:
vect += (linha[n-1],)
return vect
def obter_linha(tab, n):
# obter_linha: tabuleiro x inteiro -> tuplo
"""Devolve um tuplo com os valores da linha correspondente ao inteiro
dado."""
if not (eh_tabuleiro(tab) and type(n) == int and 1 <= n <= 3):
raise ValueError('obter_linha: algum dos argumentos e invalido')
return tab[n-1]
def obter_diagonal(tab, n):
# obter_diagonal: tabuleiro x inteiro -> tuplo
"""Devolve um tuplo com os valores da diagonal correspondente ao inteiro
dado."""
if not (eh_tabuleiro(tab) and type(n) == int and 1 <= n <= 2):
raise ValueError('obter_diagonal: algum dos argumentos e invalido')
if n == 1:
step = 1
elif n == 2:
n = 3
step = -1
vect = ()
for c in range(3):
vect += ( obter_coluna(tab, c + 1)[n-1], )
n += step
return vect
def tabuleiro_str(tab):
# tabuleiro_str: tabuleiro -> cad. caracteres
"""Devolve a cadeia de caracteres que representa o tabuleiro dado."""
if not eh_tabuleiro(tab):
raise ValueError('tabuleiro_str: o argumento e invalido')
tab_str = ''
for n in range(3):
tab_str += linha_str(obter_linha(tab, n + 1))
if n <= 1:
tab_str += '\n-----------\n'
return tab_str
def linha_str(linha):
# lin_str: tuplo -> cad. caracteres
"""Devolve a cadeia de caracteres que representa a linha dada."""
if not (type(linha) == tuple and len(linha) == 3):
raise ValueError('linha_str: o argumento e invalido')
symbols = { -1: 'O', 0: ' ', 1: 'X'}
lin_str = ''
for n in range(3):
lin_str += ' ' + symbols[linha[n]] + ' '
if n <= 1:
lin_str += '|'
return lin_str
def obter_valor_posicao(tab, pos):
# obter_valor_posicao: tabuleiro x posicao -> inteiro
"""Devolve um inteiro com o valor da marca na posicao dada."""
if not (eh_tabuleiro(tab) and eh_posicao(pos)):
raise ValueError('obter_valor_posicao: algum dos argumentos e invalido')
pos -= 1
return obter_linha(tab, (pos//3)+1)[(pos%3)]
def eh_posicao_livre(tab, pos):
# eh_posicao_livre: tabuleiro x posicao -> booleano
"""Devolve se a dada posicao se encontra livre."""
if not (eh_tabuleiro(tab) and eh_posicao(pos)):
raise ValueError('eh_posicao_livre: algum dos argumentos e invalido')
return obter_valor_posicao(tab,pos) == 0
def obter_posicoes_livres(tab):
# obter_posicoes_livres: tabuleiro -> tuplo
"""Devolve o tuplo ordenado com todas as posicoes livres do tabuleiro
dado."""
if not eh_tabuleiro(tab):
raise ValueError('obter_posicoes_livres: o argumento e invalido')
vect = ()
for n in range(1,10):
if eh_posicao_livre(tab,n):
vect += (n,)
return vect
def obter_linhas_colunas_diagonais(tab, pos):
# obter_linhas_colunas_diagonais: tabuleiro x posicao -> tuplo
"""Devolve um tuplo com os tuplos correspondentes a todas as linhas,
colunas e diagonais a que essa posicao pertence."""
if not (eh_tabuleiro(tab) and eh_posicao(pos)):
raise ValueError('obter_linhas_colunas_diagonais: algum dos argumentos e invalido')
lcds = ()
linha = ((pos-1)//3)+1
lcds += (obter_linha(tab, linha), )
coluna = ((pos-1)%3)+1
lcds += (obter_coluna(tab, coluna), )
if pos in (1,5,9):
lcds += (obter_diagonal(tab, 1), )
if pos in (3,5,7):
lcds += (obter_diagonal(tab, 2), )
return lcds
def jogador_ganhador(tab):
# jogador_ganhador: tabuleiro -> inteiro
"""Devolve o inteiro a correspondente ao jogador que ganhou a partida."""
if not eh_tabuleiro(tab):
raise ValueError('jogador_ganhador: o argumento e invalido')
def eh_vitoria(vect):
return vect[0] != 0 and vect[0] == vect[1] == vect[2]
for obter_fila in (obter_linha, obter_coluna, obter_diagonal):
for n in range(3):
if not (n == 2 and obter_fila == obter_diagonal):
fila = obter_fila(tab, n+1)
if eh_vitoria(fila):
return fila[0]
return 0
def marcar_posicao(tab, j, pos):
# marcar_posicao: tabuleiro x inteiro x posicao -> tabuleiro
"""Devolve um tabuleiro com a marca do jogador na posicao dada."""
if not (
eh_tabuleiro(tab) and
type(j) == int and j in (-1, 1) and
eh_posicao(pos) and eh_posicao_livre(tab,pos)
):
raise ValueError('marcar_posicao: algum dos argumentos e invalido')
new_tab = ()
for n in range(3):
linha = obter_linha(tab, n + 1)
if n == (pos-1)//3:
new_tab += (marcar_linha(linha , j, pos), )
else:
new_tab += (linha, )
return new_tab
def marcar_linha(vect, j, pos):
# marcar_linha: tuplo x inteiro x posicao -> tuplo
"""Devolve um tuplo representante de uma linha com a marca do jogador na
posicao dada."""
new_linha = ()
for n in range(3):
if n == (pos-1)%3:
new_linha += (j,)
else:
new_linha += (vect[n], )
return new_linha
def escolher_posicao_manual(tab):
# escolher_posicao_manual: tabuleiro -> posicao
"""Esta funcao realiza a leitura de uma posicao introduzida manualmente por
um jogador e devolve esta posicao escolhida."""
if not eh_tabuleiro(tab):
raise ValueError('escolher_posicao_manual: o argumento e invalido')
pos = int(input('Turno do jogador. Escolha uma posicao livre: '))
if not (eh_posicao(pos) and eh_posicao_livre(tab, pos)):
raise ValueError('escolher_posicao_manual: a posicao introduzida e invalida')
return pos
def escolher_posicao_auto(tab, j, strat):
# escolher_posicao_auto: tabuleiro x inteiro x cad. caracteres -> posicao
"""Devolve a posicao escolhida automaticamente de acordo com a estrategia
selecionada."""
if not (
eh_tabuleiro(tab) and
type(j) == int and j in (-1, 1) and
strat in ('basico', 'normal', 'perfeito')
):
raise ValueError('escolher_posicao_auto: algum dos argumentos e invalido')
acoes = (
(vitoria, 'normal'),
(bloqueio, 'normal'),
(bifurcacao, 'perfeito'),
(bloqueio_bifurcacao, 'perfeito'),
(centro, 'basico'),
(canto_oposto, 'normal'),
(canto_vazio, 'basico'),
(lateral_vazio, 'basico')
)
pos = None
for acao in acoes:
if eh_mais_avancada(strat, acao[1]):
pos = acao[0](tab, j)
if pos:
return pos
def eh_mais_avancada(strat, outra_strat):
# eh_mais_avancada: cad. caracteres x cad. caracteres -> booleano
"""Devolve se a primeira estrategia e mais ou igualmente avancada em
comparacao a segunda."""
strats = { 'basico': 0, 'normal': 1, 'perfeito': 2 }
return strats[strat] >= strats[outra_strat]
def vitoria(tab, j):
# vitoria: tabuleiro x inteiro -> posicao
"""Se o jogador tiver um dois em linha devolve a posicao livre restante."""
for pos in obter_posicoes_livres(tab):
for lcd in obter_linhas_colunas_diagonais(tab, pos):
if lcd.count(j) == 2:
return pos
return None
def bloqueio(tab, j):
# bloqueio: tabuleiro x inteiro -> posicao
"""Se o adversario tiver um dois em linha devolve a posicao livre
restante."""
# bloqueio utiliza a mesma logica que vitoria sendo assim possivel
# reutilizar o codigo -j e o oponente de j
return vitoria(tab, -j)
def bifurcacao(tab, j):
# bifurcacao: tabuleiro x inteiro -> posicao
"""Se o jogador tiver uma posicao de bifurcacao devolve essa posicao."""
for pos in obter_posicoes_livres(tab):
if eh_intersecao(tab, pos, j):
return pos
return None
def eh_intersecao(tab,pos,j):
# eh_intersecao: tabuleiro x posicao x inteiro -> booleano
"""Verifica se a posicao se encontra em duas ou mais linhas, colunas ou
diagonais com pecas do jogador."""
total = 0
for lcd in obter_linhas_colunas_diagonais(tab, pos):
if lcd.count(0) == 2:
total += lcd.count(j)
return total >= 2
def bloqueio_bifurcacao(tab, j):
# bloqueio_bifurcacao: tabuleiro x inteiro -> posicao
"""Se o oponente tiver apenas uma posicao de bifurcacao devolve essa
posicao. Caso exista mais do que uma bifurcacao devolve a primeira
posicao que impede o oponente de tirar partido da situacao."""
intersecoes = ()
for pos in obter_posicoes_livres(tab):
if eh_intersecao(tab, pos, -j):
intersecoes += (pos, )
if len(intersecoes) == 0:
return None
elif len(intersecoes) == 1:
return intersecoes[0]
else:
return bloqueio_bifurcacoes(intersecoes, tab, j)
def bloqueio_bifurcacoes(intersecoes, tab, j):
# bloqueio_bifurcacoes: tuplo x tabuleiro x inteiro -> posicao
"""Devolve a posicao que impede a criacao de uma bifurcacao quando existem
mais do que uma posicao que a crie."""
posicoes_livres = obter_posicoes_livres(tab)
possibilidades = ()
for pos in posicoes_livres:
if pos not in intersecoes:
# Forca o oponente a jogar numa posicao que nao seja intersecao
acao = forcar_jogada_lc(tab, j, pos)
if acao:
possibilidades += (acao, )
# Escolhe a primeira posicao que satisfaz o pretendido
for pos in posicoes_livres:
if pos in possibilidades:
return pos
return None
def forcar_jogada_lc(tab, j, pos):
# forcar_jogada_lc: tabuleiro x inteiro x posicao -> posicao
"""Devolve a posicao que forca o oponente a jogar na dada posicao no turno
seguinte. Apenas se aplica a linhas e colunas."""
linha = (pos-1)//3
coluna = (pos-1)%3
# Caso a linha tenha uma peca do jogador e dois vazios
# jogasse na posicao vazia que nao e a que se deseja forcar
lin_vect = obter_linha(tab, linha+1)
if j in lin_vect:
for c in range(3):
if c != coluna and lin_vect[c] == 0:
return (linha*3+c)+1
# Mesma logica para colunas
col_vect = obter_coluna(tab, coluna+1)
if j in col_vect:
for l in range(3):
if l != linha and col_vect[l] == 0:
return (l*3+coluna)+1
return None
def centro(tab, _):
# centro: tabuleiro -> posicao
"""Devolve a posicao central se esta estiver livre."""
if eh_posicao_livre(tab, 5):
return 5
return None
def canto_oposto(tab, j):
# canto_oposto: tabuleiro x inteiro -> posicao
"""Se o oponente tiver uma marca num canto do tabuleiro diagonalmente
oposto a uma posicao livre devolve essa posicao."""
for pos in (1,3,7,9):
if (
eh_posicao_livre(tab, pos) and
obter_valor_posicao(tab, 10-pos) == -j
):
return pos
return None
def canto_vazio(tab, _):
# canto_vazio: tabuleiro -> posicao
"""Devolve o primeiro canto que e uma posicao livre caso exista."""
for pos in (1,3,7,9):
if eh_posicao_livre(tab, pos):
return pos
return None
def lateral_vazio(tab, _):
# lateral_vazio: tabuleiro -> posicao
"""Devolve a primeira lateral que e uma posicao livre caso exista."""
for pos in (2,4,6,8):
if eh_posicao_livre(tab, pos):
return pos
return None
def jogo_do_galo(j, strat):
# jogo_do_galo: cad. caracteres x cad. caracteres -> cad. caracteres
"""Esta funcao corresponde a funcao principal que permite jogar um jogo
completo de Jogo do Galo de uma jogador contra o computador."""
if not (j in ('X','O') and strat in ('basico','normal','perfeito')):
raise ValueError('jogo_do_galo: algum dos argumentos e invalido')
print('Bem-vindo ao JOGO DO GALO.')
print('O jogador joga com \'{}\'.'.format(j))
turnos = {'X': 1, 'O': -1}
j = turnos[j]
tab = ((0,)*3,)*3
turno = 1
while jogador_ganhador(tab) == 0 and len(obter_posicoes_livres(tab)) > 0:
tab = jogar_turno(turno, strat, tab, j)
print(tabuleiro_str(tab))
turno *= -1
resultados = {1: 'X', 0: 'EMPATE', -1: 'O'}
return resultados[jogador_ganhador(tab)]
def jogar_turno(turno, strat, tab, j):
# jogar_turno: inteiro x cad. caracteres x tabuleiro x inteiro -> tabuleiro
"""Devolve o tabuleiro modificado apos o turno."""
if j == turno:
pos = escolher_posicao_manual(tab)
else:
print('Turno do computador ({}):'.format(strat))
pos = escolher_posicao_auto(tab, turno, strat)
return marcar_posicao(tab, turno, pos)