読者です 読者をやめる 読者になる 読者になる

Python3.5.2でオセロを作る②

プログラミング

前回のプログラムを改良して、ターン時点での最大の手を取るようにプログラムを改良した。

なんか簡単そうに思えたが、とんでもなく時間がかかった。そしてプログラムのエントロピーは増大する傾向にあるのだった。ちゃんちゃん

最終的には高度な機械学習とかになるのかな。そんな日が来るのはいつのことやら

main.py

from ai import Ai
from random_ai import RandomAi

black = 1
white = 2
map = [[0 for x in range(10)] for y in range(10)]
black_ai = Ai()
white_ai = RandomAi()
black_win = 0
white_win = 0
draw = 0


def init():
    # 初期化処理 #
    for x in range(10):
        for y in range(10):
            if x != 0 and y != 0 and y != len(map) - 1 and x != len(map) - 1:
                map[x][y] = 0
            else:
                map[x][y] = -1  # 番兵
    map[4][4] = white
    map[4][5] = black
    map[5][4] = black
    map[5][5] = white


def map_print():
    for x in map:
        for y in x:
            if y == -1:
                print('#', end='')
            elif y == 0:
                print('・', end='')
            elif y == 2:
                print("○", end='')
            else:
                print("●", end='')
        print("")

for z in range(5000):
    # ゲームの繰り返し回数
    print(str(z) + "回目...")
    init()
    while 1:
        black_ai.turn(map, black)
        white_ai.turn(map, white)
        if black_ai.game_end(map, black) and white_ai.game_end(map, white):
            break
    if black_ai.get_score(map, black) == white_ai.get_score(map, white):
        draw += 1
    elif black_ai.get_score(map, black) > white_ai.get_score(map, white):
        black_win += 1
    else:
        white_win += 1

print("BLACK_WIN:" + str(black_win))
print("WHITE_WIN:" + str(white_win))
print("DRAW:" + str(draw))

ai.py

import random


class Ai(object):
    def get_score(self, map, color):
        # 指定した色の個数を返す
        count = 0
        for x in range(10):
            for y in range(10):
                if map[y][x] == color:
                    count += 1
        return count

    def grid(self, map, color):
        # 置ける場所の候補地を絞って、座標をリストで返す関数
        count = []
        for x in range(10):
            for y in range(10):
                if map[y][x] == 0:
                    count.append(self.up_scan_conform(map, color, y, x))
                    count.append(self.right_scan_conform(map, color, y, x))
                    count.append(self.down_scan_conform(map, color, y, x))
                    count.append(self.left_scan_comform(map, color, y, x))
                    count.append(self.upper_left_scan_conform(map, color, y, x))
                    count.append(self.upper_right_scan_conform(map, color, y, x))
                    count.append(self.lower_left_scan_conform(map, color, y, x))
                    count.append(self.lower_right_scan_conform(map, color, y, x))
        count = [x for x in count if x != 0]
        count = self.remove_duplicate_data(count)
        return count

    def up_scan_conform(self, map, color, y, x):
        # 上側に返せる石があったら、その場所の座標を返す
        reverse_flag = False
        for con in range(1, 10):
            # 上方向に走査して、colorがあればTrueを返す。
            if map[y - con][x] == -1 or map[y - con][x] == 0:
                break
            elif not reverse_flag and map[y - (con + 1)][x] == color:
                reverse_flag = True
                break
        for z in range(1, 10):
            if map[y - z][x] == -1 or map[y - z][x] == 0:
                return 0
            elif map[y - z][x] == color:
                return 0
            elif reverse_flag and map[y - z][x] != color:
                return [y, x]

    def right_scan_conform(self, map, color, y, x):
        # 右側に返せる石があったら、その場所の座標を返す
        reverse_flag = False
        for con in range(1, 10):
            if map[y][x + con] == -1 or map[y][x + con] == 0:
                break
            elif not reverse_flag and map[y][x + (con + 1)] == color:
                reverse_flag = True
                break
        for z in range(1, 10):
            if map[y][x + z] == -1 or map[y][x + z] == 0:
                return 0
            elif map[y][x + z] == color:
                return 0
            elif reverse_flag and map[y][x + z] != color:
                return [y, x]

    def down_scan_conform(self, map, color, y, x):
        # 下側に返せる石があったら、その場所の座標を返す
        reverse_flag = False
        for con in range(1, 10):
            if map[y + con][x] == -1 or map[y + con][x] == 0:
                break
            elif not reverse_flag and map[y + (con + 1)][x] == color:
                reverse_flag = True
                break
        for z in range(1, 10):
            if map[y + z][x] == -1 or map[y + z][x] == 0:
                return 0
            elif map[y + z][x] == color:
                return 0
            elif reverse_flag and map[y + z][x] != color:
                return [y, x]

    def left_scan_comform(self, map, color, y, x):
        # 左側に返せる石があったら、その場所の座標を返す
        reverse_flag = False
        for con in range(1, 10):
            if map[y][x - con] == -1 or map[y][x - con] == 0:
                break
            elif not reverse_flag and map[y][x - (con + 1)] == color:
                reverse_flag = True
                break
        for z in range(1, 10):
            if map[y][x - z] == -1 or map[y][x - z] == 0:
                return 0
            elif map[y][x - z] == color:
                return 0
            elif reverse_flag and map[y][x - z] != color:
                return [y, x]

    def upper_left_scan_conform(self, map, color, y, x):
        # 左上側に返せる石があったら、その場所の座標を返す
        reverse_flag = False
        for con in range(1, 10):
            if map[y - con][x - con] == -1 or map[y - con][x - con] == 0:
                break
            elif not reverse_flag and map[y - (con + 1)][x - (con + 1)] == color:
                reverse_flag = True
                break
        for z in range(1, 10):
            if map[y - z][x - z] == -1 or map[y - z][x - z] == 0:
                return 0
            elif map[y - z][x - z] == color:
                return 0
            elif reverse_flag and map[y - z][x - z] != color:
                return [y, x]

    def lower_left_scan_conform(self, map, color, y, x):
        # 左下側に返せる石があったら、その場所の座標を返すStatus Help
        reverse_flag = False
        for con in range(1, 10):
            if map[y + con][x - con] == -1 or map[y + con][x - con] == 0:
                break
            elif not reverse_flag and map[y + (con + 1)][x - (con + 1)] == color:
                reverse_flag = True
                break
        for z in range(1, 10):
            if map[y + z][x - z] == -1 or map[y + z][x - z] == 0:
                return 0
            elif map[y + z][x - z] == color:
                return 0
            elif reverse_flag and map[y + z][x - z] != color:
                return [y, x]

    def upper_right_scan_conform(self, map, color, y, x):
        # 右上側に返せる石があったら、その場所の座標を返す
        reverse_flag = False
        for con in range(1, 10):
            if map[y - con][x + con] == -1 or map[y - con][x + con] == 0:
                break
            elif not reverse_flag and map[y - (con + 1)][x + (con + 1)] == color:
                reverse_flag = True
                break
        for z in range(1, 10):
            if map[y - z][x + z] == -1 or map[y - z][x + z] == 0:
                return 0
            elif map[y - z][x + z] == color:
                return 0
            elif reverse_flag and map[y - z][x + z] != color:
                return [y, x]

    def lower_right_scan_conform(self, map, color, y, x):
        # 右下側に返せる石があったら、その場所の座標を返す
        reverse_flag = False
        for con in range(1, 10):
            if map[y + con][x + con] == -1 or map[y + con][x + con] == 0:
                break
            elif not reverse_flag and map[y + (con + 1)][x + (con + 1)] == color:
                reverse_flag = True
                break
        for z in range(1, 10):
            if map[y + z][x + z] == -1 or map[y + z][x + z] == 0:
                return 0
            elif map[y + z][x + z] == color:
                return 0
            elif reverse_flag and map[y + z][x + z] != color:
                return [y, x]

    def remove_duplicate_data(self, grid):
        # gridから重複データを削除する
        molding_grid = []
        for x in grid:
            if x not in molding_grid:
                molding_grid.append(x)
        return molding_grid

    def turn(self, map, color):
        # ターンを管理する関数
        grid = self.grid(map, color)
        # 全ての候補座標
        grid = self.max_grid(grid, map, color)
        # 評価が最大の座標
        if grid == 0:
            return
        else:
            y = int(grid[0])
            x = int(grid[1])
            map[y][x] = color
            self.up_scan(map, color, y, x)
            self.right_scan(map, color, y, x)
            self.down_scan(map, color, y, x)
            self.left_scan(map, color, y, x)
            self.upper_left_scan(map, color, y, x)
            self.lower_left_scan(map, color, y, x)
            self.upper_right_scan(map, color, y, x)
            self.lower_right_scan(map, color, y, x)

    def max_grid(self, grid, map, color):
        # 座標の中から最も効果的な一手を求める関数
        score = 0
        max_score = 0
        grid_list = 0
        for x in grid:
            score = self.grid_evaluation(x, map, color)
            if max_score <= score:
                max_score = score
                grid_list = x
            # print("max_score:" + str(max_score))
            # print("score:" + str(score))

        return grid_list

    def grid_evaluation(self, grid, map, color):
        # 座標を評価して、その値を返す関数
        evaluation = 0  # 評価値
        y = grid[0]
        x = grid[1]
        reverse_flag = False
        # 上側の評価
        for con in range(1, 10):
            if map[y - con][x] == -1 or map[y - con][x] == 0:
                break
            elif map[y - con][x] == color:
                reverse_flag = True

        for z in range(1, 10):
            if map[y - z][x] == -1 or map[y - z][x] == 0:
                break
            elif map[y - z][x] == color:
                break
            elif reverse_flag and map[y - z][x] != color:
                evaluation += 1

        reverse_flag = False
        # 右側の評価
        for con in range(1, 10):
            if map[y][x + con] == -1 or map[y][x + con] == 0:
                break
            elif map[y][x + con] == color:
                reverse_flag = True

        for z in range(1, 10):
            if map[y][x + z] == -1 or map[y][x + z] == 0:
                break
            elif map[y][x + z] == color:
                break
            elif reverse_flag and map[y][x + z] != color:
                evaluation += 1

        reverse_flag = False
        # 下側の評価
        for con in range(1, 10):
            if map[y + con][x] == -1 or map[y + con][x] == 0:
                break
            elif map[y + con][x] == color:
                reverse_flag = True

        for z in range(1, 10):
            if map[y + z][x] == -1 or map[y + z][x] == 0:
                break
            elif map[y + z][x] == color:
                break
            elif reverse_flag and map[y + z][x] != color:
                evaluation += 1

        reverse_flag = False
        # 左側の評価
        for con in range(1, 10):
            if map[y][x - con] == -1 or map[y][x - con] == 0:
                break
            elif map[y][x - con] == color:
                reverse_flag = True

        for z in range(1, 10):
            if map[y][x - z] == -1 or map[y][x - z] == 0:
                break
            elif map[y][x - z] == color:
                break
            elif reverse_flag and map[y][x - z] != color:
                evaluation += 1

        reverse_flag = False
        # 左上側の評価
        for con in range(1, 10):
            if map[y - con][x - con] == -1 or map[y - con][x - con] == 0:
                break
            elif map[y - con][x - con] == color:
                reverse_flag = True

        for z in range(1, 10):
            if map[y - z][x - z] == -1 or map[y - z][x - z] == 0:
                break
            elif map[y - z][x - z] == color:
                break
            elif reverse_flag and map[y - z][x - z] != color:
                evaluation += 1

        reverse_flag = False
        # 右上側の評価
        for con in range(1, 10):
            if map[y - con][x + con] == -1 or map[y - con][x + con] == 0:
                break
            elif map[y - con][x + con] == color:
                reverse_flag = True

        for z in range(1, 10):
            if map[y - z][x + z] == -1 or map[y - z][x + z] == 0:
                break
            elif map[y - z][x + z] == color:
                break
            elif reverse_flag and map[y - z][x + z] != color:
                evaluation += 1

        reverse_flag = False
        # 右下側の評価
        for con in range(1, 10):
            if map[y + con][x + con] == -1 or map[y + con][x + con] == 0:
                break
            elif map[y + con][x + con] == color:
                reverse_flag = True

        for z in range(1, 10):
            if map[y + z][x + z] == -1 or map[y + z][x + z] == 0:
                break
            elif map[y + z][x + z] == color:
                break
            elif reverse_flag and map[y + z][x + z] != color:
                evaluation += 1

        reverse_flag = False
        # 左下側の評価
        for con in range(1, 10):
            if map[y + con][x - con] == -1 or map[y + con][x - con] == 0:
                break
            elif map[y + con][x - con] == color:
                reverse_flag = True

        for z in range(1, 10):
            if map[y + z][x - z] == -1 or map[y + z][x - z] == 0:
                break
            elif map[y + z][x - z] == color:
                break
            elif reverse_flag and map[y + z][x - z] != color:
                evaluation += 1

        return evaluation

    def game_end(self, map, color):
        # ゲーム終了を判断する関数
        if self.grid(map, color):
            return 0
        else:
            return 1

    def up_scan(self, map, color, y, x):
        # 上側に返せる石があったら返す
        stack = 0
        for count in range(1, 10):
            if map[y - count][x] == -1 or map[y - count][x] == 0:
                break
            elif map[y - count][x] != color:
                stack += 1
            if map[y - (count + 1)][x] == color and stack >= 1:
                for z in range(1, stack + 1):
                    map[y - z][x] = color

    def right_scan(self, map, color, y, x):
        # 右側に返せる石があったら返す
        stack = 0
        for count in range(1, 10):
            if map[y][x + count] == -1 or map[y][x + count] == 0:
                break
            elif map[y][x + count] != color:
                stack += 1
            if map[y][x + (count + 1)] == color and stack >= 1:
                for z in range(1, stack + 1):
                    map[y][x + z] = color

    def down_scan(self, map, color, y, x):
        # 下側に返せる石があったら返す
        stack = 0
        for count in range(1, 10):
            if map[y + count][x] == -1 or map[y + count][x] == 0:
                break
            elif map[y + count][x] != color:
                stack += 1
            if map[y + (count + 1)][x] == color and stack >= 1:
                for z in range(1, stack + 1):
                    map[y + z][x] = color

    def left_scan(self, map, color, y, x):
        # 左側に返せる石があったら返す
        stack = 0
        for count in range(1, 10):
            if map[y][x - count] == -1 or map[y][x - count] == 0:
                break
            elif map[y][x - count] != color:
                stack += 1
            if map[y][x - (count + 1)] == color and stack >= 1:
                for z in range(1, stack + 1):
                    map[y][x - z] = color

    def upper_left_scan(self, map, color, y, x):
        # 左上側に返せる石があったら返す
        stack = 0
        for count in range(1, 10):
            if map[y - count][x - count] == -1 or map[y - count][x - count] == 0:
                break
            elif map[y - count][x - count] != color:
                stack += 1
            if map[y - (count + 1)][x - (count + 1)] == color and stack >= 1:
                for z in range(1, stack + 1):
                    map[y - z][x - z] = color

    def lower_left_scan(self, map, color, y, x):
        # 左下側に返せる石があったら返す
        stack = 0
        for count in range(1, 10):
            if map[y + count][x - count] == -1 or map[y + count][x - count] == 0:
                break
            elif map[y + count][x - count] != color:
                stack += 1
            if map[y + (count + 1)][x - (count + 1)] == color and stack >= 1:
                for z in range(1, stack + 1):
                    map[y + z][x - z] = color

    def upper_right_scan(self, map, color, y, x):
        # 右上側に返せる石があったら返す
        stack = 0
        for count in range(1, 10):
            if map[y - count][x + count] == -1 or map[y - count][x + count] == 0:
                break
            elif map[y - count][x + count] != color:
                stack += 1
            if map[y - (count + 1)][x + (count + 1)] == color and stack >= 1:
                for z in range(1, stack + 1):
                    map[y - z][x + z] = color

    def lower_right_scan(self, map, color, y, x):
        # 右下側に返せる石があったら返す
        stack = 0
        for count in range(1, 10):
            if map[y + count][x + count] == -1 or map[y + count][x + count] == 0:
                break
            elif map[y + count][x + count] != color:
                stack += 1
            if map[y + (count + 1)][x + (count + 1)] == color and stack >= 1:
                for z in range(1, stack + 1):
                    map[y + z][x + z] = color

あんまり一つのクラスが膨大になるのは良くないらしい。反省。

とりあえず完全ランダムの手を取るAIと戦わせてみた。5000回回してみた。

BLACK_WIN:3061 # 改良したAI WHITE_WIN:1792 # 完全ランダム手のAI DRAW:147

強い!圧倒している・・・

random_ai.py

from ai import Ai
import random


class RandomAi(Ai):
    def turn(self, map, color):
        # ターンを管理する関数
        grid = self.grid(map, color)
        # 全ての候補座標
        if len(grid) < 1:
            return
        else:
            grid = random.choice(grid)
            y = int(grid[0])
            x = int(grid[1])
            map[y][x] = color
            self.up_scan(map, color, y, x)
            self.right_scan(map, color, y, x)
            self.down_scan(map, color, y, x)
            self.left_scan(map, color, y, x)
            self.upper_left_scan(map, color, y, x)
            self.lower_left_scan(map, color, y, x)
            self.upper_right_scan(map, color, y, x)
            self.lower_right_scan(map, color, y, x)