J'ai refactoré "J'ai essayé de faire d'Othello AI lorsque les débutants en programmation ont étudié python"

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

J'ai refactoré "J'ai essayé de faire d'Othello AI lorsque les débutants en programmation ont étudié python"
J'ai essayé de faire de l'IA pour Smash Bra
[Python] J'ai essayé de créer une IA Shiritori qui améliore le vocabulaire grâce aux batailles
Quand j'ai essayé d'introduire python3 dans atom, je suis resté coincé
Python que je voudrais recommander aux débutants en programmation
Suite ・ J'ai essayé de créer Slackbot après avoir étudié Python3
J'ai essayé de créer diverses "données factices" avec Python faker
J'ai essayé de toucher Python (installation)
J'ai essayé de faire d'Othello AI que j'ai appris 7,2 millions de mains par apprentissage profond avec Chainer
les débutants en python ont essayé de le découvrir
[5e] J'ai essayé de créer un certain outil de type Authenticator avec python
J'ai essayé de résoudre l'édition du débutant du livre des fourmis avec python
[2nd] J'ai essayé de créer un certain outil de type Authenticator avec python
J'ai essayé de créer une expression régulière de "montant" en utilisant Python
[Python] J'ai essayé d'implémenter un tri stable, alors notez
J'ai essayé de créer une expression régulière de "temps" en utilisant Python
[3ème] J'ai essayé de créer un certain outil de type Authenticator avec python
J'ai essayé de créer une expression régulière de "date" en utilisant Python
[Pandas] J'ai essayé d'analyser les données de ventes avec Python [Pour les débutants]
J'ai essayé de faire un processus d'exécution périodique avec Selenium et Python
J'ai essayé de créer une application de notification de publication à 2 canaux avec Python
[Introduction] Je veux créer un robot Mastodon avec Python! 【Débutants】
J'ai essayé de créer une application todo en utilisant une bouteille avec python
[4th] J'ai essayé de créer un certain outil de type Authenticator avec python
[Python] Japonais simple ⇒ J'ai essayé de créer un outil de traduction en anglais
[1er] J'ai essayé de créer un certain outil de type Authenticator avec python
J'ai essayé d'enseigner Python à des programmeurs inexpérimentés
J'ai essayé de créer une fonction de similitude d'image avec Python + OpenCV
J'ai essayé de créer Othello AI avec tensorflow sans comprendre la théorie de l'apprentissage automatique ~ Introduction ~
J'ai essayé de créer Othello AI avec tensorflow sans comprendre la théorie de l'apprentissage automatique ~ Implémentation ~
J'ai essayé de résumer la gestion des exceptions Python
J'ai essayé d'implémenter PLSA en Python
J'ai fait Othello pour enseigner Python3 aux enfants (4)
[Épisode 2] Les débutants ont essayé Numeron AI avec python
[Épisode 3] Les débutants ont essayé Numeron AI avec python
J'ai essayé d'implémenter PLSA dans Python 2
Entrée standard Python3 que j'ai essayé de résumer
J'ai fait Othello pour enseigner Python3 aux enfants (5)
J'ai essayé d'implémenter ADALINE en Python
[Épisode 0] Un débutant a essayé Numeron AI avec python
[Épisode 1] Un débutant a essayé Numeron AI avec python
J'ai essayé d'implémenter PPO en Python
J'ai créé une API Web
[Python] J'ai essayé de calculer TF-IDF régulièrement
J'ai essayé de toucher Python (syntaxe de base)
J'ai fait Othello pour enseigner Python3 aux enfants (3)
J'ai fait Othello pour enseigner Python3 aux enfants (1)
J'ai essayé de faire un diagnostic de visage AI pour les golfeuses professionnelles ①
[AWS] [GCP] J'ai essayé de rendre les services cloud faciles à utiliser avec Python
J'ai essayé de faire un signal avec Raspeye 4 (édition Python)
[Zaif] J'ai essayé de faciliter le commerce de devises virtuelles avec Python
[Python] Quand j'ai essayé de créer un outil de décompression avec un fichier zip que je connaissais juste, j'étais accro à sys.exit ()
J'ai essayé de créer Othello AI avec tensorflow sans comprendre la théorie de l'apprentissage automatique ~ Battle Edition ~
J'ai essayé de prédire l'année prochaine avec l'IA
J'ai essayé de faire un processus périodique avec CentOS7, Selenium, Python et Chrome
[Python] J'ai essayé de résoudre 100 questions passées que les débutants et les intermédiaires devraient résoudre [Partie 5/22]
J'ai fait une application d'envoi de courrier simple avec tkinter de Python
[Python] J'ai essayé de résoudre 100 questions passées que les débutants et les intermédiaires devraient résoudre [Partie 7/22]
Quand j'ai essayé de créer un VPC avec AWS CDK mais que je n'ai pas pu le faire
Les débutants en IA essaient de faire des étudiants professionnels Bot