J'ai vu @ keisuke1111 "La programmation débutante a étudié python, j'ai fait Othello AI". Même si j'étais un débutant, je voulais aborder des problèmes difficiles. Cependant, il semblait qu'il avait beaucoup de problèmes avec l'utilisation intensive de variables globales, le traitement redondant et l'imbrication profonde, qui sont communs aux débutants. Par conséquent, j'ai pensé que ce serait une référence pour la création de programme, j'ai donc essayé de classer chaque rôle afin que les variables globales ne soient pas utilisées, et de diviser le traitement de sorte qu'une fonction ou méthode tienne dans les 24 lignes sur un écran du terminal. J'espère que ce sera utile pour la création future du programme.
Je suis moi-même en train de refactoriser par essais et erreurs, donc si vous avez d'autres bonnes idées, je vous serais reconnaissant si vous pouviez commenter.
othello.py
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
'''
@author: Keisuke Ueda
http://qiita.com/keisuke1111/items/53353896a957136b1d7e
refactoring by @shiracamus
'''
import time
import random
import copy
class Othello:
def play(self):
start_time = time.time()
board = Board()
player1 = computer = Computer(BLACK, "PC")
player2 = user = User(WHITE, "tu")
#player1 = user = User(BLACK, "tu")
#player2 = computer = Computer(WHITE, "PC")
turn = 0
hand1 = hand2 = None
while board.is_playable() and not hand1 == hand2 == "pass":
turn += 1
print("TURN = %s" % turn)
print(board)
hand1 = player1.play(board)
print("%main de s: %s" % (player1.name, hand1))
print(board)
hand2 = player2.play(board)
print("%main de s: %s" % (player2.name, hand2))
self.show_result(board)
end_time = time.time()
print("Temps de match:" + str(end_time - start_time))
def show_result(self, board):
print("------------------RESULT-------------------") #Annonce des résultats
print(board)
computer_stones = board.count(computer.stone)
user_stones = board.count(user.stone)
print("Computer: %s" % computer_stones)
print("You: %s" % user_stones)
print()
if computer_stones > user_stones:
print("YOU LOST!!!!!")
elif computer_stones < user_stones:
print("YOU WON!!!!!")
else:
print("DRAW")
class Stone(str):
pass
BLACK = Stone("●")
WHITE = Stone("○")
BLANK = Stone("×")
OPPONENT = {BLACK: WHITE, WHITE: BLACK}
class Board:
SIZE = 8
DIRECTIONS_XY = ((-1, -1), (+0, -1), (+1, -1),
(-1, +0), (+1, +0),
(-1, +1), (+0, +1), (+1, +1))
def __init__(self):
size = self.SIZE
center = size // 2
square = [[BLANK for y in range(size)] for x in range(size)] #Définir le premier tableau
square[center - 1][center - 1:center + 1] = [WHITE, BLACK]
square[center + 0][center - 1:center + 1] = [BLACK, WHITE]
self.square = square
def __str__(self):
return '\n'.join(''.join(row) for row in self.square)
def __getitem__(self, x):
return self.square[x]
def is_playable(self):
return any(col != BLANK
for row in self.square
for col in row)
def count(self, stone): #Une fonction qui renvoie le nombre de pierres
return sum(col == stone # True is 1, False is 0
for row in self.square
for col in row)
def put(self, x, y, stone): # y,x est la coordonnée de la pierre à placer et la pierre est BLANCHE ou NOIRE.
self[x][y] = stone
# reverse
for dx, dy in Board.DIRECTIONS_XY:
n = self.count_reversible(x, y, dx, dy, stone)
for i in range(1, n + 1):
self[x + i * dx][y + i * dy] = stone
def count_reversible(self, x, y, dx, dy, stone):
size = self.SIZE
for n in range(size):
x += dx
y += dy
if not (0 <= x < size and 0 <= y < size):
return 0
if self[x][y] == BLANK:
return 0
if self[x][y] == stone:
return n
return 0
def is_available(self, x, y, stone):
if self[x][y] != BLANK:
return False
return any(self.count_reversible(x, y, dx, dy, stone) > 0
for dx, dy in self.DIRECTIONS_XY)
def availables(self, stone): #Recherche d'un endroit où frapper
return [(x, y)
for x in range(self.SIZE)
for y in range(self.SIZE)
if self.is_available(x, y, stone)]
class Player: # abstract class
def __init__(self, stone, name):
self.stone = stone
self.name = name
def play(self, board):
availables = board.availables(self.stone)
if not availables:
return "pass"
return self.think(board, availables)
class Computer(Player):
def think(self, board, availables):
starttime = time.time()
print(availables)
print("thinking……")
own = self.stone
opponent = OPPONENT[own]
evaluations, x, y = AlphaBeta(board, availables, own, opponent)
print(evaluations)
board.put(x, y, self.stone)
endtime = time.time()
interval = endtime - starttime
print("%s secondes" % interval) #Afficher le temps de calcul
return x, y
class User(Player):
def think(self, board, availables):
while True:
print("Où tu peux frapper(Y, X): " + str(availables)) #Interne x,X affiché comme y,Notez que Y est le contraire
try:
line = input("Y X or quit: ")
except:
print("résiliation forcée")
exit(1)
if line == "quit" or line == "exit":
print("Fin de l'abandon")
exit(1)
try:
x, y = map(int, line.split())
if (x, y) in availables:
board.put(x, y, self.stone)
return x, y
else:
print("Je ne peux pas mettre là")
except:
print("Inconnue")
def AlphaBeta(board, availables, own, opponent): #Recherche par méthode Alpha Beta
evaluations = AlphaBeta_evaluate1(board, availables, own, opponent)
maximum_evaluation_index = evaluations.index(max(evaluations))
x, y = availables[maximum_evaluation_index]
return evaluations, x, y
def AlphaBeta_evaluate1(board, availables, own, opponent):
def pruning2(max_evaluations3):
return len(evaluations1) > 0 and max(evaluations1) >= max_evaluations3
evaluations1 = []
for x, y in availables:
board1 = copy.deepcopy(board)
board1.put(x, y, own)
evaluations2 = AlphaBeta_evaluate2(board1, own, opponent, pruning2)
if len(evaluations2) > 0:
evaluations1 += [min(evaluations2)]
return evaluations1
def AlphaBeta_evaluate2(board, own, opponent, pruning):
def pruning3(min_evaluations4):
return len(evaluations2) > 0 and min(evaluations2) <= min_evaluations4
evaluations2 = []
for x, y in board.availables(opponent):
board2 = copy.deepcopy(board)
board2.put(x, y, opponent)
evaluations3 = AlphaBeta_evaluate3(board2, own, opponent, pruning3)
if len(evaluations3) > 0:
max_evaluations3 = max(evaluations3)
evaluations2 += [max_evaluations3]
if pruning(max_evaluations3):
break
return evaluations2
def AlphaBeta_evaluate3(board, own, opponent, pruning):
def pruning4(max_evaluations5):
return len(evaluations3) > 0 and max(evaluations3) >= max_evaluations5
evaluations3 = []
for x, y in board.availables(own):
board3 = copy.deepcopy(board)
board3.put(x, y, own)
evaluations4 = AlphaBeta_evaluate4(board3, own, opponent, pruning4)
if len(evaluations4) > 0:
min_evaluations4 = min(evaluations4)
evaluations3 += [min_evaluations4]
if pruning(min_evaluations4):
break
return evaluations3
def AlphaBeta_evaluate4(board, own, opponent, pruning):
def pruning5(evaluation5):
return len(evaluations4) > 0 and min(evaluations4) <= evaluation5
evaluations4 = []
for x, y in board.availables(opponent):
board4 = copy.deepcopy(board)
board4.put(x, y, opponent)
evaluations5 = AlphaBeta_evaluate5(board4, own, opponent, pruning5)
if len(evaluations5) > 0:
max_evaluation5 = max(evaluations5)
evaluations4 += [max_evaluation5]
if pruning(max_evaluation5):
break
return evaluations4
def AlphaBeta_evaluate5(board, own, opponent, pruning):
evaluations5 = []
for x, y in board.availables(own):
board5 = copy.deepcopy(board)
board5.put(x, y, own)
ev_own = evaluate(board5, own)
ev_opponent = evaluate(board5, opponent)
evaluation = ev_own - ev_opponent
evaluations5 += [evaluation]
if pruning(evaluation):
break
return evaluations5
# pp = [45,-11,-16,4,-1,2,-1,-3,-1,0]
EVALUATION_BOARD = ( #Tableau d'évaluation montrant combien de points il y a des pierres dans quelle case
( 45, -11, 4, -1, -1, 4, -11, 45),
(-11, -16, -1, -3, -3, -1, -16, -11),
( 4, -1, 2, -1, -1, 2, -1, 4),
( -1, -3, -1, 0, 0, -1, -3, -1),
( -1, -3, -1, 0, 0, -1, -3, -1),
( 4, -1, 2, -1, -1, 2, -1, 4),
(-11, -16, -1, -3, -3, -1, -16, -11),
( 45, -11, 4, -1, -1, 4, -11, 45))
def evaluate(board, stone): #Calculez la valeur d'évaluation de l'une ou l'autre pierre sur n'importe quelle planche
bp = 0
for x in range(board.SIZE):
for y in range(board.SIZE):
if board[x][y] == BLANK:
pass
elif board[x][y] == stone:
bp += EVALUATION_BOARD[x][y] * random.random() * 3
else:
bp -= EVALUATION_BOARD[x][y] * random.random() * 3
p = confirm_stone(board, stone)
q = confirm_stone(board, OPPONENT[stone])
fs = ((p - q) + random.random() * 3) * 11
b = board.availables(stone)
cn = (len(b) + random.random() * 2) * 10
evaluation = bp * 2 + fs * 5 + cn * 1
return evaluation
def confirm_stone(board, stone): #Comptez le nombre de pierres confirmées
forward = range(0, board.SIZE)
backward = range(board.SIZE - 1, -1, -1)
corners = ((+0, +0, forward, forward),
(+0, -1, forward, backward),
(-1, +0, backward, forward),
(-1, -1, backward, backward))
confirm = 0
for x, y, rangex, rangey in corners:
for ix in rangex:
if board[ix][y] != stone:
break
confirm += 1
for iy in rangey:
if board[x][iy] != stone:
break
confirm += 1
return confirm
if __name__ == '__main__':
Othello().play()
Recommended Posts