othello / app.py
Renecto's picture
Update app.py
edf941b verified
raw
history blame
4.12 kB
import gradio as gr
from PIL import Image, ImageDraw
import random
def choose_move(board, player):
"""
Simple AI: chooses a random valid move from all legal flips.
"""
valid_moves = []
for r in range(8):
for c in range(8):
if get_flips(board, r, c, player):
valid_moves.append((r, c))
if not valid_moves:
return None
return random.choice(valid_moves)
def initialize_board():
board = [[0 for _ in range(8)] for _ in range(8)]
board[3][3], board[4][4] = 1, 1
board[3][4], board[4][3] = -1, -1
return board
DIRECTIONS = [(-1, -1), (-1, 0), (-1, 1),
(0, -1), (0, 1),
(1, -1), (1, 0), (1, 1)]
def get_flips(board, row, col, player):
if board[row][col] != 0:
return []
flips = []
for dr, dc in DIRECTIONS:
r, c = row + dr, col + dc
buffer = []
while 0 <= r < 8 and 0 <= c < 8 and board[r][c] == -player:
buffer.append((r, c))
r += dr
c += dc
if buffer and 0 <= r < 8 and 0 <= c < 8 and board[r][c] == player:
flips.extend(buffer)
return flips
def apply_move(board, row, col, player):
flips = get_flips(board, row, col, player)
if not flips:
return False
board[row][col] = player
for r, c in flips:
board[r][c] = player
return True
def board_to_image(board, img_size=320):
cell_size = img_size // 8
img = Image.new('RGB', (img_size, img_size), 'green')
draw = ImageDraw.Draw(img)
for r in range(8):
for c in range(8):
x0, y0 = c * cell_size, r * cell_size
x1, y1 = x0 + cell_size, y0 + cell_size
draw.rectangle([x0, y0, x1, y1], outline='black')
if board[r][c] == 1:
draw.ellipse([x0+4, y0+4, x1-4, y1-4], fill='white')
elif board[r][c] == -1:
draw.ellipse([x0+4, y0+4, x1-4, y1-4], fill='black')
return img
def count_score(board):
black = sum(cell == -1 for row in board for cell in row)
white = sum(cell == 1 for row in board for cell in row)
return black, white
def play_move(x, y, state):
board, player = state
img_size = 320
cell_size = img_size / 8
col, row = int(x // cell_size), int(y // cell_size)
if not (0 <= row < 8 and 0 <= col < 8):
return board_to_image(board), f"Click inside the board.", state
if not apply_move(board, row, col, player):
return board_to_image(board), f"Invalid move at ({row+1},{col+1})", state
player = -player
black_score, white_score = count_score(board)
status = f"Black: {black_score} | White: {white_score} | {'Black' if player == -1 else 'White'} to move"
return board_to_image(board), status, (board, player)
def main():
with gr.Blocks() as demo:
gr.HTML("<h2>Othello (Reversi) Game</h2>")
state = gr.State((initialize_board(), -1))
image_box = gr.Image(value=board_to_image(initialize_board()), interactive=True, label="Click to place a stone")
status = gr.Text(value="Black: 2 | White: 2 | Black to move", interactive=False)
def click_handler(evt: gr.SelectData, state):
# ユーザーの手
board_img, status_text, state = play_move(evt.index[0], evt.index[1], state)
board, player = state
# AI(White)が動く
if player == 1:
ai_move = choose_move(board, player)
if ai_move:
apply_move(board, ai_move[0], ai_move[1], player)
player = -player
black_score, white_score = count_score(board)
status_text = f"Black: {black_score} | White: {white_score} | {'Black' if player == -1 else 'White'} to move"
board_img = board_to_image(board)
state = (board, player)
return board_img, status_text, state
image_box.select(click_handler, inputs=[state], outputs=[image_box, status, state])
demo.launch()
if __name__ == "__main__":
main()