File size: 4,384 Bytes
c4367ba
93ed0ca
c0fee0a
edf941b
c0fee0a
edf941b
c4367ba
cc9d79f
 
 
 
c4367ba
93ed0ca
cc9d79f
c0fee0a
 
 
 
 
 
 
c4367ba
 
cc9d79f
c0fee0a
93ed0ca
c0fee0a
 
c4367ba
 
c0fee0a
 
 
 
93ed0ca
c0fee0a
 
 
 
 
c4c68cc
c0fee0a
c4c68cc
 
93ed0ca
c0fee0a
23f367b
c4c68cc
c0fee0a
 
c4c68cc
23f367b
c0fee0a
c4c68cc
 
 
23f367b
c0fee0a
 
c4c68cc
 
c4367ba
 
c0fee0a
 
23f367b
c0fee0a
23f367b
 
c4c68cc
23f367b
c0fee0a
 
 
c4c68cc
23f367b
 
c4c68cc
6ad4064
c4367ba
c4c68cc
 
c0fee0a
 
c4c68cc
 
c0fee0a
c4c68cc
c0fee0a
c4c68cc
 
b8ed266
c4c68cc
c0fee0a
b8ed266
c0fee0a
c4c68cc
c0fee0a
 
 
 
34d6098
b8ed266
c0fee0a
 
 
 
 
c4367ba
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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)]

def initialize_board():
    b = [[0]*8 for _ in range(8)]
    b[3][3], b[4][4] = 1, 1
    b[3][4], b[4][3] = -1, -1
    return b

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, player):
    valid=[(r,c) for r in range(8) for c in range(8) if get_flips(board,r,c,player)]
    return random.choice(valid) if valid 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))

FONT_L = ImageFont.load_default()
FONT_S = ImageFont.load_default()

def board_to_image(board, state):
    board_state, player, last_user, last_ai, history = state
    size, cell = 360, 360//8
    img=Image.new('RGB',(size,size+cell*2),'darkgreen')
    draw=ImageDraw.Draw(img)
    # Scoreboard
    b,w=count_score(board_state)
    draw.rectangle([0,0,size,cell],fill='navy')
    draw.text((10,2),f"BLACK: {b}",font=FONT_L,fill='white')
    draw.text((size-160,2),f"WHITE: {w}",font=FONT_L,fill='white')
    # Game over
    if is_game_over(board_state):
        winner = "Draw" if b==w else ("Black Wins" if b>w else "White Wins")
        draw.rectangle([0,cell,size,cell*2],fill='maroon')
        draw.text((size//2-80,cell+2),winner,font=FONT_L,fill='yellow')
    # Board
    for r in range(8):
        for c in range(8):
            x0,y0=c*cell,cell*2+r*cell; x1,y1=x0+cell,y0+cell
            draw.rectangle([x0,y0,x1,y1],outline='black')
            if board_state[r][c]==0 and get_flips(board_state,r,c,player):
                draw.ellipse([x0+cell*0.4,y0+cell*0.4,x0+cell*0.6,y0+cell*0.6],fill='yellow')
            if board_state[r][c]==1: draw.ellipse([x0+4,y0+4,x1-4,y1-4],fill='white')
            if board_state[r][c]==-1: draw.ellipse([x0+4,y0+4,x1-4,y1-4],fill='black')
    # Last moves
    for mark,clr in ((last_user,'red'),(last_ai,'blue')):
        if mark:
            mr,mc=mark; x0,y0=mc*cell,cell*2+mr*cell; x1,y1=x0+cell,y0+cell
            draw.rectangle([x0,y0,x1,y1],outline=clr,width=4)
    # History
    y=cell*2+8*cell+2
    for res in history[-5:]:
        draw.text((10,y),res,font=FONT_S,fill='white'); y+=cell//2
    return img

def click_handler(evt, state):
    board, player, lu, la, history = state
    x,y=evt.index; cell=360//8; c,r=int(x//cell),int((y-cell*2)//cell)
    if 0<=r<8 and 0<=c<8 and not is_game_over(board):
        if apply_move(board,r,c,player): lu,player=( (r,c), -player )
    # after user
    yield board_to_image(board,(board,player,lu,la,history)),(board,player,lu,la,history)
    # if game over
    if is_game_over(board):
        history.append(f"Game {len(history)+1}: " + ("Draw" if count_score(board)[0]==count_score(board)[1] else ("Black" if count_score(board)[0]>count_score(board)[1] else "White")))
        yield board_to_image(board,(board,player,lu,la,history)),(board,player,lu,la,history)
        return
    # AI thinking
    yield board_to_image(board,(board,player,lu,la,history)),(board,player,lu,la,history)
    time.sleep(2)
    ai_mv=choose_move(board,player)
    if ai_mv: apply_move(board,ai_mv[0],ai_mv[1],player); la,player=( ai_mv, -player )
    yield board_to_image(board,(board,player,lu,la,history)),(board,player,lu,la,history)

def reset_handler():
    return initialize_board(),-1,None,None,[]

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_btn=gr.Button("New Game")
    img.select(click_handler,inputs=[state],outputs=[img,state])
    new_btn.click(fn=reset_handler,outputs=[state,img])
    demo.launch()