import gradio as gr from PIL import Image, ImageDraw, ImageFont import random time DIRECTIONS = [(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)] # Game logic def initialize_board(): board = [[0]*8 for _ in range(8)] board[3][3], board[4][4] = 1, 1 board[3][4], board[4][3] = -1, -1 return board def get_flips(board, r, c, p): if board[r][c] != 0: return [] flips = [] for dr, dc in DIRECTIONS: rr, cc = r+dr, c+dc; buf=[] while 0<=rr<8 and 0<=cc<8 and board[rr][cc] == -p: buf.append((rr,cc)); rr+=dr; cc+=dc if buf and 0<=rr<8 and 0<=cc<8 and board[rr][cc] == p: flips += buf return flips def apply_move(board, r, c, p): f = get_flips(board, r, c, p) if not f: return False board[r][c] = p for rr, cc in f: board[rr][cc] = p return True def choose_move(board, p): moves = [(r,c) for r in range(8) for c in range(8) if get_flips(board,r,c,p)] return random.choice(moves) if moves else None def count_score(board): b = sum(cell==-1 for row in board for cell in row) w = sum(cell==1 for row in board for cell in row) return b, w def is_game_over(board): return not any(get_flips(board,r,c,p) for p in (-1,1) for r in range(8) for c in range(8)) # Rendering def board_to_image(board, state): board_state, player, last_user, last_ai = state size = 400; board_size = 360; cell = board_size//8 img = Image.new('RGB',(size, size), 'darkgreen') draw = ImageDraw.Draw(img) # Load font try: font = ImageFont.truetype('arial.ttf', 24) except: font = ImageFont.load_default() # Scoreboard b, w = count_score(board_state) draw.text((10, 10), f"Black: {b}", font=font, fill='white') draw.text((size-150, 10), f"White: {w}", font=font, fill='white') # Winner display if is_game_over(board_state): result = 'DRAW' if b==w else ('BLACK WINS' if b>w else 'WHITE WINS') w_font = ImageFont.truetype('arial.ttf', 36) if hasattr(ImageFont, 'truetype') else font draw.text((size//2 - 100, size//2 - 18), result, font=w_font, fill='yellow') # Draw grid and stones offset = 40 for r in range(8): for c in range(8): x0 = offset + c*cell y0 = offset + r*cell x1, y1 = x0+cell, y0+cell draw.rectangle([x0,y0,x1,y1], outline='black') val = board_state[r][c] if val == 1: draw.ellipse([x0+4,y0+4,x1-4,y1-4], fill='white') elif val == -1: draw.ellipse([x0+4,y0+4,x1-4,y1-4], fill='black') return img # Handlers def click_handler(evt: gr.SelectData, state): board, player, last_user, last_ai = state # Calculate cell x,y = evt.index; offset=40; cell=360//8 c = int((x-offset)//cell); r = int((y-offset)//cell) if 0<=r<8 and 0<=c<8 and not is_game_over(board): if apply_move(board, r, c, player): last_user=(r,c); player=-player # AI move after delay time.sleep(1) if not is_game_over(board): ai_mv = choose_move(board, player) if ai_mv: apply_move(board, ai_mv[0], ai_mv[1], player) last_ai = ai_mv; player=-player return board_to_image(board, (board, player, last_user, last_ai)), (board, player, last_user, last_ai) def reset_game(): return (initialize_board(), -1, None, None) # UI with gr.Blocks() as demo: state = gr.State((initialize_board(), -1, None, None)) img = gr.Image(value=board_to_image(initialize_board(), (initialize_board(),-1,None,None)), interactive=True) new_game = gr.Button("New Game") img.select(click_handler, inputs=[state], outputs=[img, state]) new_game.click(fn=lambda: (initialize_board(), -1, None, None), outputs=[state]) demo.launch()