This repository has been archived by the owner on Jul 21, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
jogo_do_moinho.py
673 lines (556 loc) · 23.5 KB
/
jogo_do_moinho.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
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
# 99311 Rafael Serra e Oliveira
"""JOGO DO MOINHO
2o Projeto de Fundamentos da Programacao 2020/2021
Licenciatura em Engenharia Informatica e de Computadores (Alameda)
Instituto Superior Tecnico
"""
from functools import reduce
##### TAD posicao ##############################################################
### Representacao Interna: lista de 2 cad. de caracteres [c, l] onde c e a ###
### coluna ('a', 'b' ou 'c') e l e a linha ('1', '2' ou '3'). ###
### Por exemplo, a primeira posicao e dada por ['a', '2'] ###
### ###
### Operacoes Basicas: cria_posicao : str x str -> posicao ###
### cria_copia_posicao : posicao -> posicao ###
### obter_pos_c : posicao -> str ###
### obter_pos_l : posicao -> str ###
### eh_posicao : universal -> booleano ###
### posicoes_iguais : posicao x posicao -> booleano ###
### posicao_para_str : posicao -> str ###
################################################################################
## Baixo Nivel
def cria_posicao(c, l):
# str x str -> posicao
"""Constroi uma posicao.
Recebe como argumentos duas cadeias de caracteres correspondentes
a coluna e a linha de uma posicao e devolve a posicao correspondente.
Gera um ValueError caso algum dos argumentos seja invalido.
"""
if c in ('a', 'b', 'c') and l in ('1', '2', '3'):
return [c, l]
raise ValueError('cria_posicao: argumentos invalidos')
def cria_copia_posicao(p):
# posicao -> posicao
"""Constroi uma posicao identica a outra.
Recebe como argumento uma posicao e devolve outra posicao que e
uma copia da dada.
"""
if eh_posicao(p):
return p.copy()
raise ValueError('cria_copia_posicao: argumento invalido')
def obter_pos_c(p):
# posicao -> str
"""Obtem a coluna correspondente a uma posicao.
Recebe como argumento uma posicao e devolve a sua componente coluna.
"""
return p[0]
def obter_pos_l(p):
# posicao -> str
"""Obtem a linha correspondente a uma posicao.
Recebe como argumento uma posicao e devolve a sua componente linha.
"""
return p[1]
def eh_posicao(arg):
# universal -> booleano
"""Determina se um objeto e uma posicao.
Recebe um argumento e devolve True se esse argumento for uma posicao,
devolvendo False caso contrario.
"""
return (isinstance(arg, list) and len(arg) == 2
and arg[0] in ('a', 'b', 'c') and arg[1] in ('1', '2', '3'))
def posicoes_iguais(p1, p2):
# posicao x posicao -> booleano
"""Determina se duas posicoes sao iguais.
Recebe duas posicoes como argumentos e devolve True se sao identicas
ou False caso contrario.
"""
return eh_posicao(p1) and p1 == p2
def posicao_para_str(p):
# posicao -> str
"""Obtem a representacao em cadeia de caracteres de uma posicao.
Recebe uma posicao como argumento e devolve a cadeia de caracteres
que a representa, na forma 'cl' (sendo c a sua coluna e l a sua linha).
"""
return p[0] + p[1]
## Alto Nivel
def obter_posicoes_adjacentes(p):
# posicao -> tuplo de posicoes
"""Obtem as posicoes adjacentes a uma posicao.
Recebe uma posicao como argumento e devolve um tuplo com todas as
posicoes adjacentes a esta, por ordem de leitura do tabuleiro.
"""
adj = {
'a1': ('b1', 'a2', 'b2'),
'b1': ('a1', 'c1', 'b2'),
'c1': ('b1', 'b2', 'c2'),
'a2': ('a1', 'b2', 'a3'),
'b2': ('a1', 'b1', 'c1', 'a2', 'c2', 'a3', 'b3', 'c3'),
'c2': ('c1', 'b2', 'c3'),
'a3': ('a2', 'b2', 'b3'),
'b3': ('b2', 'a3', 'c3'),
'c3': ('b2', 'c2', 'b3'),
}
return tuple(cria_posicao(x[0], x[1]) for x in adj[posicao_para_str(p)])
##### TAD peca #################################################################
### Representacao Interna: lista de 1 elemento [s], onde s e uma das ###
### seguintes cadeias de caracteres: 'X', 'O' ou ' ' (no ultimo caso, se ###
### representar uma peca livre) ###
### Por exemplo, ['X'] e uma peca do jogador X ###
### ###
### Operacoes Basicas: cria_peca : str -> peca ###
### cria_copia_peca : peca -> peca ###
### eh_peca : universal -> booleano ###
### pecas_iguais : peca x peca -> booleano ###
### peca_para_str : peca -> str ###
################################################################################
## Baixo Nivel
def cria_peca(s):
# str -> peca
"""Constroi uma peca.
Recebe como argumento uma cadeia de caracteres correspondente a um dos
dois jogadores ('X' ou 'O') ou a uma peca livre (' ') e devolve uma
nova peca que lhe corresponde.
Gera um ValueError se o argumento for invalido.
"""
if s in ('X', 'O', ' '):
return [s]
raise ValueError('cria_peca: argumento invalido')
def cria_copia_peca(j):
# peca -> peca
"""Constroi uma peca identica a outra.
Recebe como argumento uma peca e devolve outra peca que e uma copia desta.
"""
if eh_peca(j):
return j.copy()
raise ValueError('cria_copia_peca: argumento invalido')
def eh_peca(arg):
# universal -> booleano
"""Determina se um objeto e uma peca.
Recebe um argumento e devolve True se esse argumento for uma peca,
devolvendo False caso contrario.
"""
return isinstance(arg, list) and len(arg) == 1 and arg[0] in ('X', 'O', ' ')
def pecas_iguais(j1, j2):
# peca x peca -> booleano
"""Determina se duas posicoes sao iguais.
Recebe duas posicoes como argumentos e devolve True se sao identicas
ou False caso contrario.
"""
return eh_peca(j1) and j1 == j2
def peca_para_str(j):
# peca -> str
"""Obtem a representacao em cadeia de caracteres de uma peca.
Recebe uma peca como argumento e devolve a cadeia de caracteres
que a representa, ou seja, '[O]', '[X]' ou '[ ]'.
"""
return '[' + j[0] + ']'
# Alto Nivel
def peca_para_inteiro(j):
# peca -> N
"""Obtem a representacao em inteiro de uma peca.
Recebe uma peca como argumento e devolve o inteiro que a representa, ou
seja, 1, -1 ou 0 para uma peca do jogador 'X', 'O' ou livre, respetivamente.
"""
return {'[O]': -1, '[ ]': 0, '[X]': 1}[peca_para_str(j)]
##### TAD tabuleiro ############################################################
### Representacao Interna: dicionario cujas chaves sao as representacoes em ###
### cadeia de caracteres de todas as posicoes. O valores associado a cada ###
### chave e a peca que esta nessa posicao. Por exemplo, o tabuleiro inicial ###
### e dado por {'a1': <peca ' '>, 'b1': <peca ' '>, <...>, 'c3': <peca ' '>} ###
### ###
### Operacoes Basicas: cria_tabuleiro : {} -> tabuleiro ###
### cria_copia_tabuleiro : tabuleiro -> tabuleiro ###
### obter_peca : tabuleiro x posicao -> peca ###
### obter_vetor : tabuleiro x str -> tuplo de pecas ###
### coloca_peca : tabuleiro x peca x posicao -> tabuleiro ###
### remove_peca : tabuleiro x posicao -> tabuleiro ###
### move_peca : tabuleiro x posicao x posicao -> tabuleiro###
### eh_tabuleiro : universal -> booleano ###
### eh_posicao_livre : tabuleiro x posicao -> booleano ###
### tabuleiros_iguais : tabuleiro x tabuleiro -> booleano ###
### tabuleiro_para_str : tabuleiro -> str ###
### tuplo_para_tabuleiro : tuplo -> tabuleiro ###
################################################################################
# Baixo Nivel
def cria_tabuleiro():
# {} -> tabuleiro
"""Constroi um tabuleiro.
Nao recebe argumentos. Devolve um tabuleiro de jogo do moinho 3x3
com todas as suas posicoes livres.
"""
return {s : cria_peca(' ') for \
s in ('a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'a3', 'b3', 'c3')}
def cria_copia_tabuleiro(t):
# tabuleiro -> tabuleiro
"""Constroi um tabuleiro identico a outro.
Recebe como argumento um tabuleiro e devolve outro tabuleiro que e
uma copia deste.
"""
if eh_tabuleiro(t):
return {k : cria_copia_peca(t[k]) for k in t.keys()}
raise ValueError('cria_copia_tabuleiro: argumento invalido')
def obter_peca(t, p):
# tabuleiro x posicao -> peca
"""Obtem a peca numa posicao de um tabuleiro.
Recebe como argumento um tabuleiro e uma posicao, devolvendo a peca
que esta na posicao indicada desse tabuleiro.
"""
return t[posicao_para_str(p)]
def obter_vetor(t, s):
# tabuleiro x str -> tuplo de pecas
"""Obtem uma linha ou uma coluna de um tabuleiro.
Recebe como argumentos um tabuleiro e uma cadeia de caracteres que
identifica uma linha ou uma coluna. Devolve um tuplo de todas as pecas
nessa linha/coluna do tabuleiro dado.
"""
return tuple([t[ps] for ps \
in ('a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'a3', 'b3', 'c3') if s in ps])
def coloca_peca(t, j, p):
# tabuleiro x peca x posicao -> tabuleiro
"""Coloca uma peca numa posicao de um tabuleiro.
Recebe como argumentos um tabuleiro, uma peca e uma posicao. Altera
destrutivamente o tabuleiro, colocando na posicao a peca dada.
Devolve o tabuleiro.
"""
t[posicao_para_str(p)] = j
return t
def remove_peca(t, p):
# tabuleiro x posicao -> tabuleiro
"""Remove a peca que esta numa posicao de um tabuleiro.
Recebe como argumentos um tabuleiro e uma posicao. Altera destrutivamente
o tabuleiro, colocando uma peca livre nessa posicao do tabuleiro.
Devolve o tabuleiro.
"""
t[posicao_para_str(p)] = cria_peca(' ')
return t
def move_peca(t, p1, p2):
# tabuleiro x posicao x posicao -> tabuleiro
"""Move a peca que esta numa posicao de um tabuleiro para outra.
Recebe como argumentos um tabuleiro, uma posicao de origem e uma posicao de
destino. Altera destrutivamente esse tabuleiro, movendo a peca que esta na
primeira posicao para a segunda. Devolve o tabuleiro.
"""
j = obter_peca(t, p1)
return coloca_peca(remove_peca(t, p1), j, p2)
def eh_tabuleiro(arg):
# universal -> booleano
"""Determina se um objeto e um tabuleiro.
Recebe um argumento e devolve True se esse argumento for um tabuleiro,
devolvendo False caso contrario.
"""
if not (isinstance(arg, dict) and len(arg) == 9):
return False
peca_o, peca_x, peca_livre = cria_peca('O'), cria_peca('X'), cria_peca(' ')
total_o, total_x, chaves_usadas = 0, 0, {}
for p in arg.keys():
if (p not in ('a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'a3', 'b3', 'c3')
or p in chaves_usadas.keys()):
return False
chaves_usadas[p] = True
if pecas_iguais(arg[p], peca_x):
total_x += 1
elif pecas_iguais(arg[p], peca_o):
total_o += 1
elif not pecas_iguais(arg[p], peca_livre):
return False
if max(total_o, total_x) > 3 or abs(total_x - total_o) > 1:
return False
ganhador = obter_ganhador(arg)
if (not pecas_iguais(ganhador, peca_livre)
and not pecas_iguais(obter_ganhador(remove_peca(arg.copy(), \
obter_posicoes_jogador(arg, ganhador)[0])), peca_livre)):
return False # dois ganhadores
return True
def eh_posicao_livre(t, p):
# tabuleiro x posicao -> booleano
"""Determina se uma posicao esta livre num tabuleiro.
Recebe um tabuleiro e uma posicao. Devolve True se essa posicao estiver
livre no tabuleiro dado, caso contrario devolve False.
"""
return pecas_iguais(obter_peca(t, p), cria_peca(' '))
def tabuleiros_iguais(t1, t2):
# tabuleiro x tabuleiro -> booleano
"""Determina se dois tabuleiros sao iguais.
Recebe dois tabuleiros como argumentos e devolve True se sao identicos
ou False caso contrario.
"""
return (eh_tabuleiro(t1) and eh_tabuleiro(t2)
and not len([k for k in t1.keys() if not pecas_iguais(t1[k], t2[k])]))
def tabuleiro_para_str(t):
# tabuleiro -> str
"""Obtem a cadeia de caracteres que representa um tabuleiro.
Recebe como argumento um tabuleiro e devolve a sua representacao externa.
"""
def linha_str(l): # str (numerico) -> str
return (reduce(lambda s, peca : s + peca_para_str(peca) + '-', \
obter_vetor(t, l), l + ' '))[:-1]
return ' a b c\n' + linha_str('1') + '\n' + ' | \\ | / |\n' \
+ linha_str('2') + '\n' + ' | / | \\ |\n' + linha_str('3')
def tuplo_para_tabuleiro(t):
# tuplo -> tabuleiro
"""Transforma um tuplo num tabuleiro.
Recebe como argumento um tuplo de tuplos (linhas), cada um com 3 inteiros
(1, -1 ou 0). Devolve o tabuleiro correspondente.
"""
tab = {}
for s in ('a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'a3', 'b3', 'c3'):
inteiro = t[int(s[1]) - 1][ord(s[0]) - ord('a')]
jog = 'X' if inteiro == 1 else 'O' if inteiro == -1 else ' '
tab[s] = cria_peca(jog)
return tab
# Alto Nivel
def obter_ganhador(t):
# tabuleiro -> peca
"""Determina o ganhador num tabuleiro.
Recebe como argumento um tabuleiro e devolve uma peca do jogador com 3 pecas
em linha ou coluna. Se nao existir ganhador, devolve uma peca livre.
"""
peca_livre = cria_peca(' ')
for i in ('a', 'b', 'c', '1', '2', '3'):
v = obter_vetor(t, i)
if (not pecas_iguais(v[0], peca_livre) and pecas_iguais(v[0], v[1])
and pecas_iguais(v[0], v[2])):
return v[0]
return peca_livre
def obter_posicoes_peca(t, j):
# tabuleiro x peca -> tuplo de posicoes
"""Obtem todas as posicoes com pecas iguais a dada num tabuleiro.
Recebe como argumento um tabuleiro e uma peca, devolvendo um tuplo com todas
as posicoes em que esse tabuleiro tenha pecas iguais a peca dada, por ordem.
"""
return tuple([p for p in [cria_posicao(s[0], s[1]) \
for s in ('a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'a3', 'b3', 'c3')] \
if pecas_iguais(obter_peca(t, p), j)])
def obter_posicoes_livres(t):
# tabuleiro -> tuplo de posicoes
"""Obtem todas as posicoes livres num tabuleiro.
Recebe como argumento um tabuleiro e devolve um tuplo com todas as posicoes
livres nele, por ordem de leitura.
"""
return obter_posicoes_peca(t, cria_peca(' '))
def obter_posicoes_jogador(t, j):
# tabuleiro x peca -> tuplo de posicoes
"""Obtem todas as posicoes de um jogador num tabuleiro.
Recebe como argumento um tabuleiro e uma peca de um dos dois jogadores,
devolvendo um tuplo com todas as posicoes em que esse tabuleiro tenha pecas
desse jogador, por ordem de leitura.
"""
return obter_posicoes_peca(t, j)
##### Funcoes Adicionais #####
def obter_fase(t, j):
# tabuleiro x peca -> str
"""Determina a fase de jogo para um jogador com base num tabuleiro.
Recebe como argumentos um tabuleiro e uma peca, devolvendo uma cadeia de
caracteres ('movimento' ou 'colocacao') correspondente a fase de jogo em
que esse jogador se encontra, relativamente ao tabuleiro dado.
"""
return 'movimento' if len(obter_posicoes_jogador(t,j)) == 3 else 'colocacao'
def pode_mov_redundante(t, j):
# tabuleiro x peca -> booleano
"""Determina se um jogador pode executar o movimento redundante.
Recebe como argumentos um tabuleiro e uma peca de um jogador, devolvendo
True se o jogador nao pode mover nenhuma das suas pecas para outra posicao,
tendo assim que jogar de forma redundante; False caso contrario.
"""
peca_livre = cria_peca(' ')
for p in obter_posicoes_jogador(t, j):
for adj in obter_posicoes_adjacentes(p):
if pecas_iguais(obter_peca(t, adj), peca_livre):
return False
return True
def obter_movimento_manual(t, j):
# tabuleiro x peca -> tuplo de posicoes
"""Pede uma jogada ao utilizador.
Recebe como argummentos um tabuleiro e uma peca correspondente ao jogador
humano. Na fase de colocacao, devolve um tuplo com uma posicao onde o humano
deseja colocar uma peca. Na fase de movimento, devolve um tuplo com duas
posicoes: por esta ordem, a de origem e a de destino.
"""
fase = obter_fase(t, j)
if fase == 'colocacao':
p_str = input('Turno do jogador. Escolha uma posicao: ')
if len(p_str) == 2:
if p_str[0] in ('a', 'b', 'c') and p_str[1] in ('1', '2', '3'):
p = cria_posicao(p_str[0], p_str[1])
if eh_posicao_livre(t, p):
return (p,)
elif fase == 'movimento':
p_str = input('Turno do jogador. Escolha um movimento: ')
if len(p_str) == 4:
if p_str[0] in ('a', 'b', 'c') and p_str[1] in ('1', '2', '3') \
and p_str[2] in ('a', 'b', 'c') and p_str[3] in ('1', '2', '3'):
p1 = cria_posicao(p_str[0], p_str[1])
p2 = cria_posicao(p_str[2], p_str[3])
if (posicoes_iguais(p1, p2) and pode_mov_redundante(t, j)
or (pecas_iguais(obter_peca(t, p1), j)
and eh_posicao_livre(t,p2) and len([x for x in \
obter_posicoes_adjacentes(p1) if posicoes_iguais(x, p2)]))):
return (p1, p2)
raise ValueError('obter_movimento_manual: escolha invalida')
# <Criterios de Colocacao Automatica>
def criterio_colocacao_vitoria(t, j):
# tabuleiro x peca -> posicao/nenhum
"""Tenta aplicar o criterio de colocacao "vitoria".
Recebe como argumentos um tabuleiro e uma peca. Devolve a posicao em que o
jogador a que corresponde a peca pode jogar para ganhar o jogo. Caso nao
exista uma posicao nesta situacao, devolve None.
"""
for s in ('a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'a3', 'b3', 'c3'):
p = cria_posicao(s[0], s[1])
if not eh_posicao_livre(t, p):
continue
t_tmp = cria_copia_tabuleiro(t)
if pecas_iguais(obter_ganhador(coloca_peca(t_tmp, j, p)), j):
return p
return None
def criterio_colocacao_bloqueio(t, j):
# tabuleiro x peca -> posicao/nenhum
"""Tenta aplicar o criterio de colocacao "bloqueio".
Recebe como argumentos um tabuleiro e uma peca. Devolve a posicao em que o
jogador a que corresponde a peca pode jogar para bloquear uma vitoria do
adversario. Caso nao exista uma posicao nesta situacao, devolve None.
"""
outro = cria_peca('X') if 'O' in peca_para_str(j) else cria_peca('O')
return criterio_colocacao_vitoria(t, outro)
def criterio_colocacao_centro(t, j):
# tabuleiro x peca -> posicao/nenhum
"""Tenta aplicar o criterio de colocacao "centro".
Recebe como argumentos um tabuleiro e uma peca. Devolve a posicao central do
tabuleiro se esta estiver livre; caso contrario, devolve None.
"""
p = cria_posicao('b', '2')
return p if eh_posicao_livre(t, p) else None
def criterio_colocacao_canto_vazio(t, j):
# tabuleiro x peca -> posicao/nenhum
"""Tenta aplicar o criterio de colocacao "canto vazio".
Recebe como argumentos um tabuleiro e uma peca. Devolve a posicao do
primeiro canto, por ordem de leitura, que esta livre no tabuleiro, ou None
se nenhum estiver.
"""
for s in ('a1', 'c1', 'a3', 'c3'):
p = cria_posicao(s[0], s[1])
if eh_posicao_livre(t, p):
return p
return None
def criterio_colocacao_lateral_vazio(t, j):
# tabuleiro x peca -> posicao/nenhum
"""Tenta aplicar o criterio de colocacao "lateral vazio".
Recebe como argumentos um tabuleiro e uma peca. Devolve a posicao da
primeira lateral, por ordem de leitura, que esta livre no tabuleiro, ou None
se nenhuma estiver.
"""
for s in ('b1', 'a2', 'c2', 'b3'):
p = cria_posicao(s[0], s[1])
if eh_posicao_livre(t, p):
return p
return None
# </Criterios de Colocacao Automatica>
def escolher_posicao_colocacao_auto(t, j):
# tabuleiro x peca -> posicao
"""Escolhe uma posicao onde colocar uma peca.
Recebe um tabuleiro e uma peca e devolve a posicao identificada como otima
pelo primeiro criterio aplicavel (gerando um RuntimeError se nenhum o for).
"""
criterios = (criterio_colocacao_vitoria, criterio_colocacao_bloqueio, \
criterio_colocacao_centro, criterio_colocacao_canto_vazio,
criterio_colocacao_lateral_vazio)
for crit in criterios:
p = crit(t, j)
if p is not None:
return p
raise RuntimeError('escolher_posicao_colocacao_auto: ' \
+ 'nenhum criterio de colocacao e aplicavel')
def escolher_movimento_facil_auto(t, j):
# tabuleiro x peca -> posicao
"""Escolhe um movimento na dificuldade 'facil'.
Recebe um tabuleiro e uma peca e devolve a primeira posicao adjacente a uma
posicao do jogador correspondente a peca dada que esteja livre, ou None.
"""
for p in obter_posicoes_jogador(t, j):
for adj in obter_posicoes_adjacentes(p):
if eh_posicao_livre(t, adj):
return (p, adj)
return None
def minimax(t, j, profundidade, seq_movimentos):
# tabuleiro x peca x inteiro x tuplo -> tuplo(inteiro x tuplo)
"""Implementa o algoritmo minimax.
Recebe um tabuleiro, uma peca, uma profundidade e sequencia de movimentos.
Analisa as jogadas possiveis ate a dada profundidade, devolvendo o resultado
do melhor cenario para o jogador da peca dada, tal como a sequencia de
movimentos a efetuar para chegar a essa situacao.
"""
ganhador_int = peca_para_inteiro(obter_ganhador(t))
if ganhador_int or profundidade == 0:
return ganhador_int, seq_movimentos
j_int = peca_para_inteiro(j)
melhor_resultado = -j_int
melhor_seq_movimentos = ()
for p in obter_posicoes_jogador(t, j):
for adj in obter_posicoes_adjacentes(p):
if eh_posicao_livre(t, adj):
novo_t = move_peca(cria_copia_tabuleiro(t), p, adj)
(novo_resultado, nova_seq_movimentos) = minimax(novo_t, \
cria_peca('X' if j_int == -1 else 'O'), profundidade - 1, \
seq_movimentos + ((p, adj),))
if (not melhor_seq_movimentos
or ((j_int * novo_resultado) > (j_int * melhor_resultado))):
melhor_resultado = novo_resultado
melhor_seq_movimentos = nova_seq_movimentos
return (melhor_resultado, melhor_seq_movimentos)
def obter_movimento_auto(t, j, dificuldade):
# tabuleiro x peca x str -> tuplo de posicoes
"""Calcula o movimento para um jogador num tabuleiro numa dificuldade.
Recebe um tabuleiro, uma peca de um jogador e uma cadeia de caracteres de
uma dificuldade ('facil', 'normal', 'dificil'), devolvendo um tuplo com uma
(na fase de colocacoes) ou duas (na fase de movimento) posicoes que
corresponde a uma jogada do computador.
"""
fase = obter_fase(t, j)
pj = obter_posicoes_jogador(t, j)
mov_redundante = 2*(pj[0],) if pj else None
if fase == 'colocacao':
return (escolher_posicao_colocacao_auto(t, j),)
elif dificuldade == 'facil':
return escolher_movimento_facil_auto(t, j) or mov_redundante
else:
(_, seq_movs) = minimax(t, j, 1 if dificuldade == 'normal' else 5, ())
return seq_movs[0] if seq_movs else mov_redundante
def faz_jogada(t, j, movimento):
# tabuleiro x peca x tuplo -> {}
"""Executa uma jogada para um jogador num tabuleiro.
Recebe como argumentos um tabuleiro, a peca de um jogador e um tuplo com um
(na fase de colocacao) ou dois elementos (na fase de movimento), executando
essa jogada. Nao devolve nada.
"""
if len(movimento) == 2:
move_peca(t, movimento[0], movimento[1])
else:
coloca_peca(t, j, movimento[0])
def moinho(ext_peca, dific):
# str x str -> str
"""Corre um jogo completo do Jogo do Moinho.
Recebe como argumentos a representacao externa da peca com que o humano
deseja jogar ('[X]', '[O]') e a dificuldade da partida ('facil', 'normal' ou
'dificil'). Devolve a representacao externa de uma peca do jogador ganhador.
"""
if not (ext_peca in ('[X]', '[O]')
and dific in ('facil', 'normal', 'dificil')):
raise ValueError('moinho: argumentos invalidos')
peca_humano, peca_computador = cria_peca('X'), cria_peca('O')
if ext_peca == '[O]':
peca_humano, peca_computador = peca_computador, peca_humano
print('Bem-vindo ao JOGO DO MOINHO. Nivel de dificuldade ' + dific + '.')
t, peca_livre = cria_tabuleiro(), cria_peca(' ')
ganhador, jogada_humano = peca_livre, ext_peca == '[X]'
print(tabuleiro_para_str(t))
while pecas_iguais(ganhador, peca_livre):
j = peca_humano if jogada_humano else peca_computador
if jogada_humano:
faz_jogada(t, j, obter_movimento_manual(t, j))
else:
print('Turno do computador (' + dific + '):')
faz_jogada(t, j, obter_movimento_auto(t, j, dific))
print(tabuleiro_para_str(t))
jogada_humano = not jogada_humano
ganhador = obter_ganhador(t)
return peca_para_str(ganhador)