File size: 5,847 Bytes
c64c726
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
Web-compatible action processing for CSGO actions
Converts web keyboard inputs to CSGO actions without pygame dependency
"""

from dataclasses import dataclass
from typing import Dict, List, Set, Tuple

import numpy as np
import torch

# Web key code to CSGO action mapping
WEB_KEYMAP = {
    'KeyW': "up",
    'KeyD': "right", 
    'KeyA': "left",
    'KeyS': "down",
    'Space': "jump",
    'ControlLeft': "crouch",
    'ShiftLeft': "walk",
    'Digit1': "weapon1",
    'Digit2': "weapon2",
    'Digit3': "weapon3",
    'KeyR': "reload",
    'ArrowUp': "camera_up",
    'ArrowRight': "camera_right",
    'ArrowLeft': "camera_left",
    'ArrowDown': "camera_down",
}

# Forbidden key combinations (same logic as original)
WEB_FORBIDDEN_COMBINATIONS = [
    {"up", "down"},
    {"left", "right"},
    {"weapon1", "weapon2"},
    {"weapon1", "weapon3"},
    {"weapon2", "weapon3"},
    {"camera_up", "camera_down"},
    {"camera_left", "camera_right"},
]

@dataclass
class WebCSGOAction:
    """Web-compatible CSGO action without pygame dependencies"""
    key_names: List[str]  # Use string names instead of pygame key codes
    mouse_x: float
    mouse_y: float
    l_click: bool
    r_click: bool

    def __post_init__(self) -> None:
        self.key_names = filter_web_keys_forbidden(self.key_names)
        self.process_mouse()

    def process_mouse(self) -> None:
        """Process mouse movement with discretization"""
        # Import mouse constants
        from .action_processing import MOUSE_X_POSSIBLES, MOUSE_Y_POSSIBLES, MOUSE_X_LIM, MOUSE_Y_LIM
        
        # Clip and match mouse to closest in list of possibles
        x = np.clip(self.mouse_x, MOUSE_X_LIM[0], MOUSE_X_LIM[1])
        y = np.clip(self.mouse_y, MOUSE_Y_LIM[0], MOUSE_Y_LIM[1])
        self.mouse_x = min(MOUSE_X_POSSIBLES, key=lambda x_: abs(x_ - x))
        self.mouse_y = min(MOUSE_Y_POSSIBLES, key=lambda x_: abs(x_ - y))
        
        # Use arrow keys to override mouse movements
        for key_name in self.key_names:        
            if key_name == "camera_left":
                self.mouse_x = -60
            elif key_name == "camera_right":
                self.mouse_x = +60
            elif key_name == "camera_up":
                self.mouse_y = -50
            elif key_name == "camera_down":
                self.mouse_y = +50

def filter_web_keys_forbidden(key_names: List[str]) -> List[str]:
    """Filter out forbidden key combinations"""
    names = set(key_names)
    filtered_names = []
    
    for key_name in key_names:
        # Check if adding this key would create a forbidden combination
        test_names = set(filtered_names + [key_name])
        is_forbidden = False
        
        for forbidden in WEB_FORBIDDEN_COMBINATIONS:
            if forbidden.issubset(test_names):
                is_forbidden = True
                break
        
        if not is_forbidden:
            filtered_names.append(key_name)
    
    return filtered_names

def web_keys_to_csgo_action_names(pressed_web_keys: Set[str]) -> List[str]:
    """Convert set of pressed web keys to CSGO action names"""
    action_names = []
    for web_key in pressed_web_keys:
        if web_key in WEB_KEYMAP:
            action_names.append(WEB_KEYMAP[web_key])
    return action_names

def encode_web_csgo_action(web_action: WebCSGOAction, device: torch.device) -> torch.Tensor:
    """Encode web CSGO action to tensor format (compatible with original encoding)"""
    from .action_processing import MOUSE_X_POSSIBLES, MOUSE_Y_POSSIBLES, N_KEYS, N_CLICKS, N_MOUSE_X, N_MOUSE_Y
    
    keys_pressed_onehot = np.zeros(N_KEYS)
    mouse_x_onehot = np.zeros(N_MOUSE_X)
    mouse_y_onehot = np.zeros(N_MOUSE_Y)
    l_click_onehot = np.zeros(1)
    r_click_onehot = np.zeros(1)

    # Map action names to one-hot encoding
    for action_name in web_action.key_names:
        if action_name == "up":  # w key
            keys_pressed_onehot[0] = 1
        elif action_name == "left":  # a key
            keys_pressed_onehot[1] = 1
        elif action_name == "down":  # s key
            keys_pressed_onehot[2] = 1
        elif action_name == "right":  # d key
            keys_pressed_onehot[3] = 1
        elif action_name == "jump":  # space
            keys_pressed_onehot[4] = 1
        elif action_name == "crouch":  # ctrl
            keys_pressed_onehot[5] = 1
        elif action_name == "walk":  # shift
            keys_pressed_onehot[6] = 1
        elif action_name == "weapon1":  # 1
            keys_pressed_onehot[7] = 1
        elif action_name == "weapon2":  # 2
            keys_pressed_onehot[8] = 1
        elif action_name == "weapon3":  # 3
            keys_pressed_onehot[9] = 1
        elif action_name == "reload":  # r
            keys_pressed_onehot[10] = 1

    l_click_onehot[0] = int(web_action.l_click)
    r_click_onehot[0] = int(web_action.r_click)

    mouse_x_onehot[MOUSE_X_POSSIBLES.index(web_action.mouse_x)] = 1
    mouse_y_onehot[MOUSE_Y_POSSIBLES.index(web_action.mouse_y)] = 1

    assert mouse_x_onehot.sum() == 1
    assert mouse_y_onehot.sum() == 1

    return torch.tensor(
        np.concatenate((
            keys_pressed_onehot,
            l_click_onehot,
            r_click_onehot,
            mouse_x_onehot,
            mouse_y_onehot,
        )),
        device=device,
        dtype=torch.float32,
    )

def print_web_csgo_action(action: WebCSGOAction) -> Tuple[str, str, str]:
    """Print web CSGO action in readable format"""
    action_names = [name for name in action.key_names if not name.startswith("camera_")]
    keys = " + ".join(action_names)     
    mouse = str((action.mouse_x, action.mouse_y)) * (action.mouse_x != 0 or action.mouse_y != 0)
    clicks = "L" * action.l_click + " + " * (action.l_click and action.r_click) + "R" * action.r_click
    return keys, mouse, clicks