File size: 5,142 Bytes
c4367ba
93ed0ca
293933e
edf941b
c0fee0a
edf941b
293933e
 
 
 
 
 
 
 
 
 
c4367ba
cc9d79f
 
 
 
c4367ba
93ed0ca
cc9d79f
c0fee0a
 
 
 
 
 
 
c4367ba
 
cc9d79f
c0fee0a
93ed0ca
c0fee0a
 
c4367ba
 
c0fee0a
 
 
 
93ed0ca
c0fee0a
 
 
 
 
c4c68cc
c0fee0a
 
23f367b
293933e
 
c0fee0a
293933e
23f367b
c0fee0a
c4c68cc
293933e
 
23f367b
293933e
 
 
 
 
c4367ba
 
c0fee0a
 
23f367b
c0fee0a
23f367b
 
293933e
23f367b
c0fee0a
 
 
293933e
 
23f367b
c4c68cc
293933e
6ad4064
c4367ba
c4c68cc
606d9f2
 
c4c68cc
9b082e6
 
 
 
 
293933e
606d9f2
293933e
606d9f2
 
 
293933e
606d9f2
293933e
c0fee0a
606d9f2
 
293933e
606d9f2
b8ed266
293933e
 
 
 
 
 
 
606d9f2
 
 
 
 
c0fee0a
293933e
 
 
34d6098
b8ed266
c0fee0a
 
 
 
 
9b082e6
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import gradio as gr
from PIL import Image, ImageDraw, ImageFont
import random, time, os

DIRECTIONS = [(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)]

history_log = []

# Attempt to load larger font
try:
    FONT_L = ImageFont.truetype("DejaVuSans-Bold.ttf", 28)
    FONT_S = ImageFont.truetype("DejaVuSans-Bold.ttf", 16)
except:
    FONT_L = ImageFont.load_default()
    FONT_S = ImageFont.load_default()

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))

def board_to_image(board, state):
    board_state, player, last_user, last_ai, history = state
    size, cell = 400, 400//8
    img=Image.new('RGB',(size,size+cell*4),'darkgreen')
    draw=ImageDraw.Draw(img)

    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-180,2),f"WHITE: {w}",font=FONT_L,fill='white')

    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='darkred')
        draw.text((size//2-110,cell+2),winner,font=FONT_L,fill='yellow')
        draw.text((size//2-90,cell+30),"Click 'New Game' to restart",font=FONT_S,fill='white')

    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')

    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)

    y=cell*2+8*cell+10
    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):
    if state is None:
        state = (initialize_board(), -1, None, None, [])
    board, player, lu, la, history = state
    # Handle evt.index callable vs attribute
    if callable(evt.index):
        x, y = evt.index()
    else:
        x, y = evt.index
    cell = 400 // 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

    yield board_to_image(board, (board, player, lu, la, history)), (board, player, lu, la, history)

    if is_game_over(board):
        winner = "Draw" if count_score(board)[0] == count_score(board)[1] else ("Black" if count_score(board)[0] > count_score(board)[1] else "White")
        history.append(f"Game {len(history)+1}: {winner}")
        history_log.append(f"Game {len(history_log)+1}: {winner}")
        yield board_to_image(board, (board, player, lu, la, history)), (board, player, lu, la, history)
        return

    thinking_img = board_to_image(board, (board, player, lu, la, history))
    draw = ImageDraw.Draw(thinking_img)
    draw.text((150, 400 - 25), "AI Thinking...", font=FONT_S, fill='cyan')
    yield thinking_img, (board, player, lu, la, history)
    time.sleep(1.5)

    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():
    new_board = initialize_board()
    return new_board, -1, None, None, history_log.copy()

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()