File size: 3,498 Bytes
5f5fb32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from fasthtml.common import (
    fast_app,
    Div,
    serve,
    Script,
    Span,
)
from othello import Board, AlphaBeta


app, rt = fast_app(
    hdrs=[
        Script(src="https://cdn.tailwindcss.com"),
    ],
    pico=False,
    ws_hdr=True,
    live=True,
)
board = Board.default()
bot = AlphaBeta(7)


@rt("/")
def get():
    state = board.state
    cells = [make_cell(state.cells[i], i) for i in range(64)]

    return Div(
        Div(
            Div(
                "Black:",
                make_score(state.black_score, "black-score"),
                cls="bg-black text-white w-32 h-12 text-center content-center shadow-md",
            ),
            Div(make_status(state), cls="content-center"),
            Div(
                "White:",
                make_score(state.white_score, "white-score"),
                cls="bg-white text-black w-32 h-12 text-center content-center shadow-md",
            ),
            cls="mx-auto flex w-[32rem] justify-between",
        ),
        Div(
            *cells,
            cls="mx-auto mt-5 grid w-[32rem] grid-cols-8 gap-0 bg-green-300",
            hx_ext="ws",
            ws_connect="/wscon",
        ),
        cls="m-auto max-w-2xl bg-gray-200 p-12 mt-12",
    )


def make_cell(v, pos):
    stone = None
    if v == "?":
        stone = Div(
            hx_trigger="click",
            hx_vals=f'{{"pos": {pos}}}',
            ws_send=True,
            hx_swap_oob="true",
            id=f"cell-{pos}",
            # cls="h-16 w-16 cursor-pointer bg-purple-200 hover:bg-purple-400",
            cls="mx-2 my-2 h-12 w-12 rounded-full cursor-pointer bg-purple-200 hover:bg-purple-300",
        )
    elif v == "B":
        stone = Div(
            cls="mx-2 my-2 h-12 w-12 rounded-full bg-black shadow-sm shadow-white"
        )
    elif v == "W":
        stone = Div(
            cls="mx-2 my-2 h-12 w-12 rounded-full bg-white shadow-sm shadow-black"
        )
    return Div(
        stone,
        id=f"cell-{pos}",
        cls="h-16 w-16 border border-sky-100",
        hx_swap_oob="true",
    )


def make_score(v, id):
    return Span(v, id=id, hx_swap_oob="true")


def make_status(state):
    status = "Black turn"
    if state.ended:
        if state.white_score > state.black_score:
            status = "White won!"
        elif state.white_score < state.black_score:
            status = "Black won!"
        else:
            status = "Game draw!"
    elif state.player == "W":
        status = "White turn"
    return Span(status, id="status", hx_swap_oob="true")


@app.ws("/wscon")
async def ws(pos: int, send):
    # Human
    state = await move(pos, send)
    if state.ended:
        return

    # Bot
    while True:
        pos = bot.find_move(board) if state.can_move else -1
        state = await move(pos, send)
        if not state.can_move and not state.ended:
            # Human has no move
            await move(-1, send)
        else:
            break


async def move(pos: int, send):
    prev_state = board.state
    state = board.make_move(pos) if pos >= 0 else board.pass_move()

    await send(make_cell(state.cells[pos], pos))
    # await asyncio.sleep(1)

    for i, (c1, c2) in enumerate(zip(prev_state.cells, (state.cells))):
        if i != pos and c1 != c2:
            await send(make_cell(c2, i))

    await send(make_score(state.white_score, "white-score"))
    await send(make_score(state.black_score, "black-score"))
    await send(make_status(state))
    return state


serve()