Ich sah @ keisuke1111s "Anfängerprogrammierung studierte Python, ich habe Othello AI gemacht". Obwohl ich ein Anfänger war, war ich daran interessiert, herausfordernde Probleme anzugehen. Es schien jedoch, dass er große Probleme mit der starken Verwendung globaler Variablen, der redundanten Verarbeitung und der tiefen Verschachtelung hatte, die Anfängern gemeinsam sind. Daher dachte ich, dass dies eine Referenz für die Programmerstellung sein würde, und versuchte daher, jede Rolle so zu klassifizieren, dass keine globalen Variablen verwendet werden, und die Verarbeitung so aufzuteilen, dass eine Funktion oder Methode in 24 Zeilen auf einem Bildschirm des Terminals passt. Ich hoffe, dass es für die zukünftige Programmerstellung nützlich sein wird.
Ich selbst überarbeite durch Versuch und Irrtum. Wenn Sie also andere gute Ideen haben, würde ich mich freuen, wenn Sie einen Kommentar abgeben könnten.
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, "Sie")
#player1 = user = User(BLACK, "Sie")
#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("%s Hand: %s" % (player1.name, hand1))
print(board)
hand2 = player2.play(board)
print("%s Hand: %s" % (player2.name, hand2))
self.show_result(board)
end_time = time.time()
print("Spielzeit:" + str(end_time - start_time))
def show_result(self, board):
print("------------------RESULT-------------------") #Ergebnisansage
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)] #Definieren Sie das erste Board
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): #Eine Funktion, die zurückgibt, wie viele Steine es gibt
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 ist die Koordinate des zu platzierenden Steins und Stein ist WEISS oder SCHWARZ.
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): #Auf der Suche nach einem Treffpunkt
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 Sekunden" % interval) #Berechnungszeit anzeigen
return x, y
class User(Player):
def think(self, board, availables):
while True:
print("Wo du schlagen kannst(Y, X): " + str(availables)) #Intern x,X wird als y angezeigt,Beachten Sie, dass Y das Gegenteil ist
try:
line = input("Y X or quit: ")
except:
print("erzwungene Kündigung")
exit(1)
if line == "quit" or line == "exit":
print("Ende der Aufgabe")
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("Kann nicht dort setzen")
except:
print("Unbekannt")
def AlphaBeta(board, availables, own, opponent): #Suche nach Alpha Beta-Methode
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 = ( #Bewertungsgremium, das zeigt, wie viele Punkte sich auf welchem Quadrat befinden
( 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): #Berechnen Sie den Bewertungswert eines Steins auf einem beliebigen Brett
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): #Zählen Sie die Anzahl der bestätigten Steine
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()