碁ならべ ver5.1

python

まだ白石(PC)は弱いので強化中!

# 碁ならべver5.py ChatGPT-6 OK
# 白は黒の4x3、4x4を見逃している--禁止手 7/27/2025
# 白は飛び3を見逃す

import FreeSimpleGUI as sg
import random
import time

sg.theme('DarkBrown7')

BOARD_SIZE = 15 #15x15が標準size
EMPTY = ''   # 碁盤目には何も書き込まない状態
BLACK = '●'
WHITE = '○'

def create_layout(): #layoutを設定する
    board_layout = [[
        sg.Button(EMPTY, size=(2, 1), font=(None, 18), pad=(0, 0), key=f'{y},{x}', button_color=('black', '#C8B560'))
        for x in range(BOARD_SIZE)] for y in range(BOARD_SIZE)
    ]
    control_layout = [[sg.Button('Again', key='AGAIN'), sg.Button('End', key='END')]] # 15x15の碁盤を作成している
    return board_layout + control_layout # 碁盤作成後に「AGAIN」「END」のボタンを追加している
def reset_board(window): # elif event == 'AGAIN':の項で碁盤を元へ戻す    
    for y in range(BOARD_SIZE):
        for x in range(BOARD_SIZE):
            window[f'{y},{x}'].update(EMPTY, button_color=('black', '#C8B560'), disabled=False)

def is_valid(y, x):
    return 0 <= y < BOARD_SIZE and 0 <= x < BOARD_SIZE

def get_board(window):
    return [[window[f'{y},{x}'].GetText() for x in range(BOARD_SIZE)] for y in range(BOARD_SIZE)]

def check_winner(board, stone):
    directions = [(0,1), (1,0), (1,1), (-1,1)]
    for y in range(BOARD_SIZE):
        for x in range(BOARD_SIZE):
            for dy, dx in directions:
                count = 0
                for i in range(5):
                    ny, nx = y + dy*i, x + dx*i
                    if is_valid(ny, nx) and board[ny][nx] == stone:
                        count += 1
                    else:
                        break
                if count == 5:
                    return True
    return False

def block_open_three(board, stone):
    directions = [(0,1), (1,0), (1,1), (-1,1)]
    for y in range(BOARD_SIZE):
        for x in range(BOARD_SIZE):
            for dy, dx in directions:
                coords = [(y + dy*i, x + dx*i) for i in range(-1, 4)]
                if all(is_valid(cy, cx) for cy, cx in coords):
                    line = [board[cy][cx] for cy, cx in coords]
                    if line == [EMPTY, stone, stone, stone, EMPTY]:
                        for idx in [0, 4]:
                            ny, nx = coords[idx]
                            if board[ny][nx] == EMPTY:
                                return ny, nx
    return None

def check_line(board, stone, count_needed):
    directions = [(0,1), (1,0), (1,1), (-1,1)]
    for y in range(BOARD_SIZE):
        for x in range(BOARD_SIZE):
            if board[y][x] != EMPTY:
                continue
            for dy, dx in directions:
                for i in range(5):
                    sy, sx = y - dy*i, x - dx*i
                    coords = [(sy + dy*j, sx + dx*j) for j in range(5)]
                    if all(is_valid(cy, cx) for cy, cx in coords):
                        line = [board[cy][cx] for cy, cx in coords]
                        if line.count(stone) == count_needed and line.count(EMPTY) == 5 - count_needed:
                            return y, x
    return None

def pc_move(window):
    board = get_board(window)

    # 1. 勝てる手
    move = check_line(board, WHITE, 4)
    if move:
        y, x = move
        flash_white(window, y, x)
        return

    # 2. 黒の4連をブロック
    move = check_line(board, BLACK, 4)
    if move:
        y, x = move
        flash_white(window, y, x)
        return

    # 3. 両三(□●●●□)をブロック
    move = block_open_three(board, BLACK)
    if move:
        y, x = move
        flash_white(window, y, x)
        return

    # 4. 黒の3連ブロック
    move = check_line(board, BLACK, 3)
    if move:
        y, x = move
        flash_white(window, y, x)
        return

    # 5. 黒石の近くに寄せる(スコア)
    scores = {}
    for y in range(BOARD_SIZE):
        for x in range(BOARD_SIZE):
            if board[y][x] != EMPTY:
                continue
            score = 0
            for dy in [-1,0,1]:
                for dx in [-1,0,1]:
                    ny, nx = y + dy, x + dx
                    if is_valid(ny, nx):
                        if board[ny][nx] == BLACK:
                            score += 2
                        elif board[ny][nx] == WHITE:
                            score += 1
            scores[(y,x)] = score

    if scores:
        y, x = max(scores, key=scores.get)
        flash_white(window, y, x)
        return

    # 6. ランダム
    empty = [(y,x) for y in range(BOARD_SIZE) for x in range(BOARD_SIZE) if board[y][x] == EMPTY]
    if empty:
        y, x = random.choice(empty)
        flash_white(window, y, x)

def flash_white(window, y, x):
    key = f'{y},{x}'
    window[key].update('○', button_color=('black', 'yellow'))
    window.refresh()
    time.sleep(1)
    window[key].update('○', button_color=('black', '#C8B560'), disabled=True)

# --- メイン ---
layout = create_layout()
window = sg.Window('五目並べver5.1', layout)

while True:
    event, values = window.read()
    if event in (sg.WINDOW_CLOSED,):
        break
    elif event == 'END':
        confirm = sg.popup_yes_no('終了しますか?', title='確認')
        if confirm == 'Yes':
            break
        else:
            continue
    elif event == 'AGAIN':
        reset_board(window)
        continue
    
    elif ',' in event:
        if window[event].GetText() == EMPTY:
            window[event].update('●', button_color=('white', '#333333'), disabled=True)
            board = get_board(window)
            if check_winner(board, BLACK):
                sg.popup('黒の勝ちです!')
                continue
            pc_move(window)
            board = get_board(window)
            if check_winner(board, WHITE):
                sg.popup('白の勝ちです!')

window.close()