| import json
|
| import sys
|
| import model
|
| import model3p
|
| import numpy as np
|
|
|
| tiles_tenhou = {
|
| '1m': 0, '2m': 1, '3m': 2, '4m': 3, '5m': 4, '5mr': 4.5, '6m': 5, '7m': 6, '8m': 7, '9m': 8,
|
| '1p': 9, '2p': 10, '3p': 11, '4p': 12, '5p': 13, '5pr': 13.5, '6p': 14, '7p': 15, '8p': 16, '9p': 17,
|
| '1s': 18, '2s': 19, '3s': 20, '4s': 21, '5s': 22, '5sr': 22.5, '6s': 23, '7s': 24, '8s': 25, '9s': 26,
|
| 'E': 27, 'S': 28, 'W': 29, 'N': 30, 'P': 31, 'F': 32, 'C': 33
|
| }
|
|
|
| MASK_4P = [
|
| "1m", "2m", "3m", "4m", "5m", "6m", "7m", "8m", "9m",
|
| "1p", "2p", "3p", "4p", "5p", "6p", "7p", "8p", "9p",
|
| "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s", "9s",
|
| "E", "S", "W", "N", "P", "F", "C",
|
| '5mr', '5pr', '5sr',
|
| 'reach', 'chi_low', 'chi_mid', 'chi_high', 'pon', 'kan', 'hora', 'ryukyoku', 'none'
|
| ]
|
|
|
| MASK_3P = [
|
| "1m", "2m", "3m", "4m", "5m", "6m", "7m", "8m", "9m",
|
| "1p", "2p", "3p", "4p", "5p", "6p", "7p", "8p", "9p",
|
| "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s", "9s",
|
| "E", "S", "W", "N", "P", "F", "C",
|
| '5mr', '5pr', '5sr',
|
| 'reach', 'pon', 'kan', 'nukidora', 'hora', 'ryukyoku', 'none'
|
| ]
|
|
|
| def SoftMax(arr, temperature=1.0):
|
| arr = np.array(arr, dtype=float)
|
| if arr.size == 0:
|
| return arr
|
| if not temperature == 1.0:
|
| arr /= temperature
|
|
|
| max_val = np.max(arr)
|
| arr = arr - max_val
|
|
|
| exp_arr = np.exp(arr)
|
| sum_exp = np.sum(exp_arr)
|
| softmax_arr = exp_arr / sum_exp
|
| return softmax_arr
|
|
|
| def ToBinStr(mask_bits):
|
| binary_string = bin(mask_bits)[2:]
|
| binary_string = binary_string.zfill(46)
|
| return binary_string
|
|
|
|
|
| def ToBoolList(mask_bits):
|
| binary_string = ToBinStr(mask_bits)
|
| bool_list = []
|
| for bit in binary_string[::-1]:
|
| bool_list.append(bit == '1')
|
| return bool_list
|
|
|
| def ParseMeta(is_3p, meta):
|
| if is_3p:
|
| mask_list = MASK_3P
|
| else:
|
| mask_list = MASK_4P
|
|
|
| q_values = meta['q_values']
|
| mask_bits = meta['mask_bits']
|
| mask = ToBoolList(mask_bits)
|
| weight_values = SoftMax(q_values)
|
|
|
| q_value_idx = 0
|
| option_list = []
|
| for i in range(46):
|
| if mask[i]:
|
| option_list.append((mask_list[i], weight_values[q_value_idx]))
|
| q_value_idx += 1
|
|
|
| option_list = sorted(option_list, key=lambda x: x[1], reverse=True)
|
| return option_list
|
|
|
|
|
| class Bot:
|
| def __init__(self):
|
| self.player_id: int = None
|
| self.model = None
|
|
|
| def react(self, events: str):
|
| events = json.loads(events)
|
| return_action = None
|
| for e in events:
|
| if e["type"] == "start_game":
|
| self.player_id = e["id"]
|
| self.model = model3p.load_model(self.player_id)
|
| continue
|
| if self.model is None or self.player_id is None:
|
| raise Exception(f"Model is not loaded yet")
|
| continue
|
| if e["type"] == "end_game":
|
| self.player_id = None
|
| self.model = None
|
| continue
|
| return_action = self.model.react(json.dumps(e, separators=(",", ":")))
|
| return return_action
|
|
|
| class Bot4P:
|
| def __init__(self):
|
| self.player_id: int = None
|
| self.model = None
|
|
|
| def react(self, events: str, H = True):
|
| events = json.loads(events)
|
| return_action = None
|
| for e in events:
|
| if e["type"] == "start_game":
|
| self.player_id = e["id"]
|
| self.model = model.load_model(self.player_id)
|
| continue
|
| if self.model is None or self.player_id is None:
|
| raise Exception(f"Model is not loaded yet")
|
| continue
|
| if e["type"] == "end_game":
|
| self.player_id = None
|
| self.model = None
|
| continue
|
| return_action = self.model.react(json.dumps(e, separators=(",", ":")))
|
| if H:
|
| return return_action
|
| if return_action == None:
|
| return None
|
| original = json.loads(return_action)
|
| return Select(original, ParseMeta(False, original['meta']))
|
| false = False
|
| true = True
|
| if __name__ == '__main__':
|
| bot = Bot4P()
|
| print(bot)
|
|
|
| events = {
|
| "is3p": false,
|
| "model": "v2-a",
|
| "events": [
|
| {
|
| "id": 0,
|
| "type": "start_game"
|
| },
|
| {
|
| "oya": 0,
|
| "type": "start_kyoku",
|
| "honba": 0,
|
| "kyoku": 1,
|
| "bakaze": "E",
|
| "scores": [
|
| 25000,
|
| 25000,
|
| 25000,
|
| 0
|
| ],
|
| "tehais": [
|
| [
|
| "1m",
|
| "2s",
|
| "3s",
|
| "3p",
|
| "4p",
|
| "5p",
|
| "5s",
|
| "6s",
|
| "7s",
|
| "N",
|
| "N",
|
| "S",
|
| "W"
|
| ],
|
| [
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?"
|
| ],
|
| [
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?"
|
| ],
|
| [
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?",
|
| "?"
|
| ]
|
| ],
|
| "kyotaku": 0,
|
| "dora_marker": "9m"
|
| },
|
| {
|
| "pai": "1p",
|
| "type": "tsumo",
|
| "actor": 0
|
| }
|
| ],
|
| "timestamp": "2025-09-22T15:03:13.873Z"
|
| }
|
|
|
| res = bot.react(json.dumps(events['events']))
|
| print(res)
|
|
|