File size: 8,420 Bytes
4a38cd3
 
 
778a2fd
 
4a38cd3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
778a2fd
4a38cd3
 
 
778a2fd
4a38cd3
778a2fd
 
 
 
 
 
4a38cd3
778a2fd
 
 
 
 
 
 
 
 
 
 
4a38cd3
778a2fd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4a38cd3
 
778a2fd
 
 
4a38cd3
 
 
778a2fd
 
 
 
 
 
 
 
 
4a38cd3
778a2fd
 
4a38cd3
778a2fd
 
 
4a38cd3
778a2fd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4a38cd3
778a2fd
 
 
 
 
 
 
 
 
4a38cd3
778a2fd
4a38cd3
778a2fd
 
4a38cd3
778a2fd
4a38cd3
778a2fd
4a38cd3
778a2fd
 
4a38cd3
 
778a2fd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4a38cd3
778a2fd
4a38cd3
778a2fd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4a38cd3
778a2fd
 
 
 
4a38cd3
 
 
778a2fd
 
4a38cd3
 
 
778a2fd
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
import gradio as gr
from huggingface_hub import hf_hub_download
from game_logic import AaduPulliEnv, NeuralNetwork, AlphaZeroAgent
import numpy as np
import time

REPO_ID = "AaduPulliAttam/apa-ray" 
MODEL_FILENAME = "alphazero_aadu_pulli_ray.weights.h5"
HUMAN_PLAYER = 0  # 0 for Goat, 1 for Tiger
AI_PLAYER = 1

ai_agent = None
try:
    print(f"Downloading model from {REPO_ID}...")
    model_path = hf_hub_download(repo_id=REPO_ID, filename=MODEL_FILENAME)
    print(f"Model downloaded to: {model_path}")

    env_for_init = AaduPulliEnv()
    action_space_size = env_for_init.action_space.n
    neural_net = NeuralNetwork(action_space_size)
    neural_net.model.load_weights(model_path)
    print("Model weights loaded successfully.")
    
    ai_agent = AlphaZeroAgent(env_for_init, neural_net, simulations_per_move=50)
except Exception as e:
    print(f"Could not load model. AI will be disabled. Error: {e}")

def get_pos_from_click(env, click_coords):
    """Maps pixel coordinates from a click to the closest board position."""
    if not click_coords: return None
    x_click, y_click = click_coords
    
    img_width, img_height = 800, 800 

    top_margin_ratio = 0.12
    bottom_margin_ratio = 0.12
    left_margin_ratio = 0.05
    right_margin_ratio = 0.05

    plot_width_px = img_width * (1 - left_margin_ratio - right_margin_ratio)
    plot_height_px = img_height * (1 - top_margin_ratio - bottom_margin_ratio)
    
    if not (img_width * left_margin_ratio < x_click < img_width * (1 - right_margin_ratio) and \
            img_height * top_margin_ratio < y_click < img_height * (1 - bottom_margin_ratio)):
        return None

    x_rel = x_click - (img_width * left_margin_ratio)
    y_rel = y_click - (img_height * top_margin_ratio)

    x_scaled = (x_rel / plot_width_px) * 24
    y_scaled = (1 - (y_rel / plot_height_px)) * 21 
    
    min_dist = float('inf')
    closest_pos = None
    for pos, (x_board, y_board) in env.board_points.items():
        dist = np.sqrt((x_scaled - x_board)**2 + (y_scaled - y_board)**2)
        if dist < min_dist:
            min_dist = dist
            closest_pos = pos
            
    if min_dist > 1.5: return None 
    return closest_pos

def check_game_over(info, human_player_side):
    """Checks for game over conditions and returns a message."""
    winner = info.get('winner', -1)
    if winner != -1:
        if winner == human_player_side: return "Congratulations, you won!", True
        elif winner == 1 - human_player_side: return "The AI has won. Better luck next time!", True
        else: return "The game is a draw!", True
    return None, False


def handle_click(env, selected_pos, game_over, human_player_side, evt: gr.SelectData):
    """
    Handles all user clicks on the board.
    - For placement, it executes the move and triggers the AI.
    - For movement, it handles both piece selection and destination clicking.
    """
    if game_over or env.player_turn != human_player_side:
        message = "Game is over." if game_over else "It's not your turn."
        return env, env.render(highlight_pos=selected_pos), message, selected_pos, game_over

    if not ai_agent:
        return env, env.render(), "AI is not available.", None, True

    clicked_pos = get_pos_from_click(env, evt.index)
    if clicked_pos is None:
        return env, env.render(highlight_pos=selected_pos), "Please click on a valid position.", selected_pos, game_over

    is_placement_phase = env.goats_placed_count < env.NUM_GOATS and human_player_side == 0
    
    if is_placement_phase:
        action = clicked_pos - 1
        is_valid, details = env.is_action_valid(action)
        if not is_valid:
            return env, env.render(), f"Invalid placement: {details.get('error')}", None, False
    
    else:
        if selected_pos is None:
            piece_type = 1 if human_player_side == 0 else 2
            if env.board[clicked_pos - 1] == piece_type:
                return env, env.render(highlight_pos=clicked_pos), f"Selected piece at {clicked_pos}. Click where to move.", clicked_pos, False
            else:
                return env, env.render(), "You must select one of your own pieces.", None, False
        
        else:
            from_pos, to_pos = selected_pos, clicked_pos
            move_tuple = (from_pos, to_pos)
            if move_tuple not in env._move_action_lookup:
                return env, env.render(), "Invalid move path.", None, False
            
            action = env.placement_actions + env._move_action_lookup[move_tuple]
            is_valid, details = env.is_action_valid(action)
            if not is_valid:
                return env, env.render(), f"Invalid move: {details.get('error')}", None, False

    # Human's move
    state, _, _, info = env.step(action)
    message, game_over = check_game_over(info, human_player_side)
    if game_over:
        return env, env.render(), message, None, True
    
    # AI's turn
    time.sleep(0.5)
    state = env._get_current_observation()
    ai_action = ai_agent.get_action(state, training=False)
    state, _, _, info = env.step(ai_action)
    
    action_type = "Placement" if ai_action < env.placement_actions else "Move"
    details_txt = ai_action + 1 if action_type == "Placement" else env._move_action_map.get(ai_action - env.placement_actions, "N/A")
    
    message, game_over = check_game_over(info, human_player_side)
    if game_over:
        return env, env.render(), message, None, game_over
        
    final_message = f"AI played {action_type} {details_txt}. Your turn."
    return env, env.render(), final_message, None, game_over


def start_game(player_choice):
    """Starts a new game and handles the initial AI move if necessary."""
    env = AaduPulliEnv()
    human_player_side = 0 if player_choice == "Goat" else 1
    
    message = "New game started. Your turn!"
    game_over = False
    
    if human_player_side == 1:
        if ai_agent:
            state = env._get_current_observation()
            ai_action = ai_agent.get_action(state, training=False)
            state, _, _, info = env.step(ai_action)
            print(f"state {state}")
            message = "New game started. AI placed a goat. Your turn to move a Tiger."
        else:
            message = "AI not available. Cannot start game as Tiger."
            game_over = True

    return {
        start_screen: gr.update(visible=False),
        game_screen: gr.update(visible=True),
        game_env_state: env,
        board_image: env.render(),
        status_message: message,
        selected_pos_state: None,
        game_over_state: game_over,
        human_player_state: human_player_side
    }


def go_to_start_screen():
    return {
        start_screen: gr.update(visible=True),
        game_screen: gr.update(visible=False),
    }

with gr.Blocks(theme=gr.themes.Soft()) as demo:
    gr.Markdown("# Aadu Puli Aattam: Human vs AlphaZero AI")

    game_env_state = gr.State()
    selected_pos_state = gr.State()
    game_over_state = gr.State(False)
    human_player_state = gr.State()

    with gr.Column(visible=True) as start_screen:
        gr.Markdown("## Choose Your Side")
        player_choice_radio = gr.Radio(["Goat", "Tiger"], label="Play as", value="Goat")
        start_button = gr.Button("Start Game")

    with gr.Column(visible=False) as game_screen:
        gr.Markdown("You play as the Goats (Blue) or the Tigers (Red). Your goal is to trap the opponent or capture their pieces.")
        with gr.Row():
            with gr.Column(scale=2):
                board_image = gr.Image(label="Game Board", interactive=True)
            with gr.Column(scale=1):
                status_message = gr.Markdown(label="Game Status")
                new_game_button = gr.Button("New Game")

    start_button.click(
        fn=start_game,
        inputs=[player_choice_radio],
        outputs=[start_screen, game_screen, game_env_state, board_image, status_message, selected_pos_state, game_over_state, human_player_state]
    )
    
    board_image.select(
        fn=handle_click,
        inputs=[game_env_state, selected_pos_state, game_over_state, human_player_state],
        outputs=[game_env_state, board_image, status_message, selected_pos_state, game_over_state]
    )
    
    new_game_button.click(
        fn=go_to_start_screen,
        outputs=[start_screen, game_screen]
    )

if __name__ == "__main__":
    demo.launch()