kaggle-environments 1.17.12__py2.py3-none-any.whl → 1.18.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kaggle-environments might be problematic. Click here for more details.
- kaggle_environments/__init__.py +1 -1
- kaggle_environments/api.py +5 -13
- kaggle_environments/envs/cabt/cabt.js +164 -0
- kaggle_environments/envs/cabt/cabt.json +28 -0
- kaggle_environments/envs/cabt/cabt.py +119 -0
- kaggle_environments/envs/cabt/cg/__init__.py +0 -0
- kaggle_environments/envs/cabt/cg/cg.dll +0 -0
- kaggle_environments/envs/cabt/cg/game.py +70 -0
- kaggle_environments/envs/cabt/cg/libcg.so +0 -0
- kaggle_environments/envs/cabt/cg/sim.py +44 -0
- kaggle_environments/envs/open_spiel/games/chess/chess.js +25 -22
- kaggle_environments/envs/open_spiel/open_spiel.py +53 -1
- kaggle_environments/envs/open_spiel/test_open_spiel.py +85 -1
- kaggle_environments/helpers.py +126 -86
- kaggle_environments/main.py +29 -44
- kaggle_environments/static/player.html +71 -31
- kaggle_environments/utils.py +8 -12
- {kaggle_environments-1.17.12.dist-info → kaggle_environments-1.18.0.dist-info}/METADATA +2 -71
- {kaggle_environments-1.17.12.dist-info → kaggle_environments-1.18.0.dist-info}/RECORD +23 -15
- {kaggle_environments-1.17.12.dist-info → kaggle_environments-1.18.0.dist-info}/WHEEL +1 -1
- {kaggle_environments-1.17.12.dist-info → kaggle_environments-1.18.0.dist-info}/entry_points.txt +0 -0
- {kaggle_environments-1.17.12.dist-info → kaggle_environments-1.18.0.dist-info}/licenses/LICENSE +0 -0
- {kaggle_environments-1.17.12.dist-info → kaggle_environments-1.18.0.dist-info}/top_level.txt +0 -0
kaggle_environments/__init__.py
CHANGED
|
@@ -20,7 +20,7 @@ from .core import *
|
|
|
20
20
|
from .main import http_request
|
|
21
21
|
from . import errors
|
|
22
22
|
|
|
23
|
-
__version__ = "1.
|
|
23
|
+
__version__ = "1.18.0"
|
|
24
24
|
|
|
25
25
|
__all__ = ["Agent", "environments", "errors", "evaluate", "http_request",
|
|
26
26
|
"make", "register", "utils", "__version__",
|
kaggle_environments/api.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import requests
|
|
2
2
|
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import List
|
|
4
4
|
|
|
5
5
|
base_url = "https://www.kaggle.com/requests/EpisodeService/"
|
|
6
6
|
get_url = base_url + "GetEpisodeReplay"
|
|
@@ -8,30 +8,22 @@ list_url = base_url + "ListEpisodes"
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def get_episode_replay(episode_id: int):
|
|
11
|
-
body = {
|
|
12
|
-
"EpisodeId": episode_id
|
|
13
|
-
}
|
|
11
|
+
body = {"EpisodeId": episode_id}
|
|
14
12
|
|
|
15
13
|
response = requests.post(get_url, json=body)
|
|
16
14
|
return response.json()
|
|
17
15
|
|
|
18
16
|
|
|
19
17
|
def list_episodes(episode_ids: List[int]):
|
|
20
|
-
return __list_episodes({
|
|
21
|
-
"Ids": episode_ids
|
|
22
|
-
})
|
|
18
|
+
return __list_episodes({"Ids": episode_ids})
|
|
23
19
|
|
|
24
20
|
|
|
25
21
|
def list_episodes_for_team(team_id: int):
|
|
26
|
-
return __list_episodes({
|
|
27
|
-
"TeamId": team_id
|
|
28
|
-
})
|
|
22
|
+
return __list_episodes({"TeamId": team_id})
|
|
29
23
|
|
|
30
24
|
|
|
31
25
|
def list_episodes_for_submission(submission_id: int):
|
|
32
|
-
return __list_episodes({
|
|
33
|
-
"SubmissionId": submission_id
|
|
34
|
-
})
|
|
26
|
+
return __list_episodes({"SubmissionId": submission_id})
|
|
35
27
|
|
|
36
28
|
|
|
37
29
|
def __list_episodes(body):
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
|
|
2
|
+
function renderer(context) {
|
|
3
|
+
const step = context.step
|
|
4
|
+
const visList = context.environment.steps[0][0].visualize
|
|
5
|
+
const energyText = "CGRWLPFDM A"
|
|
6
|
+
|
|
7
|
+
const info = context.environment.info
|
|
8
|
+
const players = [
|
|
9
|
+
info?.TeamNames?.[0] || "Player 0",
|
|
10
|
+
info?.TeamNames?.[1] || "Player 1"
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
let canvas = context.parent.querySelector("canvas")
|
|
14
|
+
if (!canvas) {
|
|
15
|
+
container = document.createElement("div")
|
|
16
|
+
container.style.position = "relative"
|
|
17
|
+
context.parent.appendChild(container)
|
|
18
|
+
|
|
19
|
+
canvas = document.createElement("canvas")
|
|
20
|
+
canvas.width = 750
|
|
21
|
+
canvas.height = 700
|
|
22
|
+
container.appendChild(canvas)
|
|
23
|
+
|
|
24
|
+
if (visList) {
|
|
25
|
+
for (let k = 0; k < 2; k++) {
|
|
26
|
+
const button = document.createElement("button")
|
|
27
|
+
button.style.width = "120px"
|
|
28
|
+
button.style.height = "50px"
|
|
29
|
+
button.style.left = k == 0 ? "240px" : "380px"
|
|
30
|
+
button.style.top = "10px"
|
|
31
|
+
button.style.position = "absolute"
|
|
32
|
+
button.style.zIndex = 1
|
|
33
|
+
button.innerHTML = "Open Visualizer<br>" + players[k]
|
|
34
|
+
button.addEventListener("click", (e) => {
|
|
35
|
+
for (let i = 0; i < visList.length; i++) {
|
|
36
|
+
for (let j = 0; j < 2; j++) {
|
|
37
|
+
visList[i].current.players[j].ramainingTime = context.environment.steps[i][j].observation.remainingOverageTime
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
visList[0].ps = players
|
|
41
|
+
|
|
42
|
+
const input = document.createElement("input")
|
|
43
|
+
input.type = "hidden"
|
|
44
|
+
input.name = "json"
|
|
45
|
+
input.value = JSON.stringify(visList)
|
|
46
|
+
|
|
47
|
+
const form = document.createElement("form")
|
|
48
|
+
form.method = "POST"
|
|
49
|
+
form.action = "https://ptcgvis.heroz.jp/Visualizer/Replay/"
|
|
50
|
+
if (info.EpisodeId == null) {
|
|
51
|
+
form.action += k
|
|
52
|
+
} else {
|
|
53
|
+
form.action += info.EpisodeId + "/" + k
|
|
54
|
+
}
|
|
55
|
+
form.target = "_blank"
|
|
56
|
+
form.appendChild(input)
|
|
57
|
+
|
|
58
|
+
document.body.appendChild(form)
|
|
59
|
+
form.submit()
|
|
60
|
+
})
|
|
61
|
+
container.appendChild(button)
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
const ctx = canvas.getContext("2d")
|
|
65
|
+
ctx.strokeStyle = "#ccc"
|
|
66
|
+
ctx.fillStyle = "#fff"
|
|
67
|
+
ctx.font = "30px sans-serif"
|
|
68
|
+
ctx.fillText("No visualize data.", 10, 100)
|
|
69
|
+
const error = context.environment.steps[0][0].error
|
|
70
|
+
if (error) {
|
|
71
|
+
ctx.fillText(error, 10, 150)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (visList.length <= step) {
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
const vis = visList[step]
|
|
80
|
+
const state = vis.current
|
|
81
|
+
|
|
82
|
+
const ctx = canvas.getContext("2d")
|
|
83
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
|
84
|
+
|
|
85
|
+
ctx.strokeStyle = "#ccc"
|
|
86
|
+
ctx.fillStyle = "#fff"
|
|
87
|
+
ctx.lineWidth = 2
|
|
88
|
+
|
|
89
|
+
ctx.font = "20px sans-serif"
|
|
90
|
+
if (state.result >= 0) {
|
|
91
|
+
if (state.result == 2) {
|
|
92
|
+
ctx.fillText("Draw", 330, 70)
|
|
93
|
+
} else {
|
|
94
|
+
ctx.fillText(players[state.result] + " Win", 310, 120)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
ctx.font = "12px sans-serif"
|
|
99
|
+
|
|
100
|
+
const drawCard = (x, y, card) => {
|
|
101
|
+
ctx.beginPath()
|
|
102
|
+
ctx.rect(x, y, 80, 60)
|
|
103
|
+
ctx.stroke()
|
|
104
|
+
nm = card.name
|
|
105
|
+
nm2 = null
|
|
106
|
+
if (nm.length >= 13) {
|
|
107
|
+
for (let i = 0; i < nm.length; i++) {
|
|
108
|
+
if (nm[i] == " ") {
|
|
109
|
+
nm2 = nm.substring(i + 1)
|
|
110
|
+
nm = nm.substring(0, i)
|
|
111
|
+
break
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
ctx.fillText(nm, x + 5, y + 13)
|
|
116
|
+
if (nm2 != null) {
|
|
117
|
+
ctx.fillText(nm2, x + 5, y + 27)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const drawField = (x, y, card) => {
|
|
121
|
+
drawCard(x, y, card)
|
|
122
|
+
ctx.fillText("HP " + card.hp, x + 5, y + 41)
|
|
123
|
+
energy = ""
|
|
124
|
+
for (let e of card.energies) {
|
|
125
|
+
energy = energy + energyText[e]
|
|
126
|
+
}
|
|
127
|
+
ctx.fillText(energy, x + 5, y + 55)
|
|
128
|
+
}
|
|
129
|
+
const posY = (index, len) => {
|
|
130
|
+
const center = 290
|
|
131
|
+
let height
|
|
132
|
+
if (len <= 8) {
|
|
133
|
+
height = 35 * len
|
|
134
|
+
} else {
|
|
135
|
+
height = 280
|
|
136
|
+
}
|
|
137
|
+
return center + height * (2 * index + 1 - len) / len
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
for (let j = 0; j < state.stadium.length; j++) {
|
|
141
|
+
drawCard(330, 420, state.stadium[j])
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
for (let i = 0; i < 2; i++) {
|
|
145
|
+
const ps = state.players[i]
|
|
146
|
+
|
|
147
|
+
ctx.fillText("Active", i == 0 ? 245 : 425, 270)
|
|
148
|
+
ctx.fillText("Bench", i == 0 ? 145 : 525, 10)
|
|
149
|
+
ctx.fillText("Hand", i == 0 ? 15 : 655, 10)
|
|
150
|
+
ctx.fillText("Deck " + ps.deckCount, i == 0 ? 258 : 438, 150)
|
|
151
|
+
ctx.fillText("Discard " + ps.discard.length, i == 0 ? 245 : 425, 170)
|
|
152
|
+
ctx.fillText("Prize " + ps.prize.length, i == 0 ? 258 : 438, 200)
|
|
153
|
+
|
|
154
|
+
for (let j = 0; j < ps.active.length; j++) {
|
|
155
|
+
drawField(i == 0 ? 240 : 420, posY(j, ps.active.length), ps.active[j])
|
|
156
|
+
}
|
|
157
|
+
for (let j = 0; j < ps.bench.length; j++) {
|
|
158
|
+
drawField(i == 0 ? 140 : 520, posY(j, ps.bench.length), ps.bench[j])
|
|
159
|
+
}
|
|
160
|
+
for (let j = 0; j < ps.hand.length; j++) {
|
|
161
|
+
drawCard(i == 0 ? 10 : 650, posY(j, ps.hand.length), ps.hand[j])
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cabt",
|
|
3
|
+
"title": "Card Battle",
|
|
4
|
+
"description": "Limited Card Battle.",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"agents": [2],
|
|
7
|
+
"configuration": {
|
|
8
|
+
"episodeSteps": 10000,
|
|
9
|
+
"actTimeout": 0,
|
|
10
|
+
"runTimeout": 3000
|
|
11
|
+
},
|
|
12
|
+
"reward": {
|
|
13
|
+
"description": "Lost:-1, Won:1, Draw:0",
|
|
14
|
+
"enum": [-1, 0, 1],
|
|
15
|
+
"default": 0
|
|
16
|
+
},
|
|
17
|
+
"observation": {
|
|
18
|
+
"remainingOverageTime": 600
|
|
19
|
+
},
|
|
20
|
+
"action": {
|
|
21
|
+
"description": "List of option index.",
|
|
22
|
+
"type": "array",
|
|
23
|
+
"default": []
|
|
24
|
+
},
|
|
25
|
+
"status": {
|
|
26
|
+
"defaults": ["INACTIVE", "INACTIVE"]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import random
|
|
2
|
+
import os
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from .cg.sim import Battle
|
|
6
|
+
from .cg.game import battle_start, battle_finish, battle_select, visualize_data
|
|
7
|
+
|
|
8
|
+
deck = [5,5,5,5,5,5,5,5,5,5,9,9,77,77,77,77,156,156,156,156,157,157,157,157,331,331,331,331,408,408,408,408,474,474,474,474,528,528,528,528,530,530,530,530,532,554,554,554,576,576,576,576,585,585,585,585,630,630,630,630]
|
|
9
|
+
|
|
10
|
+
def random_agent(obs: dict) -> list[int]:
|
|
11
|
+
if obs["select"] == None:
|
|
12
|
+
return deck
|
|
13
|
+
return random.sample(list(range(len(obs["select"]["option"]))), obs["select"]["maxCount"])
|
|
14
|
+
|
|
15
|
+
def first_agent(obs: dict) -> list[int]:
|
|
16
|
+
if obs["select"] == None:
|
|
17
|
+
return deck
|
|
18
|
+
return list(range(obs["select"]["maxCount"]))
|
|
19
|
+
|
|
20
|
+
agents = {"random": random_agent, "first": first_agent}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def finish(env):
|
|
24
|
+
if len(env.steps) > 0:
|
|
25
|
+
env.steps[0][0]["visualize"] = json.loads(visualize_data())
|
|
26
|
+
battle_finish()
|
|
27
|
+
|
|
28
|
+
def interpreter(state, env):
|
|
29
|
+
if env.done:
|
|
30
|
+
Battle.battle_ptr = None
|
|
31
|
+
for i in range(2):
|
|
32
|
+
state[i].status = "ACTIVE"
|
|
33
|
+
o = state[i].observation
|
|
34
|
+
o["select"] = None
|
|
35
|
+
o["logs"] = []
|
|
36
|
+
o["current"] = None
|
|
37
|
+
o["search_begin_input"] = None
|
|
38
|
+
return state
|
|
39
|
+
elif Battle.battle_ptr == None:
|
|
40
|
+
decks = [state[0].action, state[1].action]
|
|
41
|
+
error = False
|
|
42
|
+
for i in range(2):
|
|
43
|
+
if state[i].status == "TIMEOUT" or state[i].status == "ERROR":
|
|
44
|
+
error = True
|
|
45
|
+
continue
|
|
46
|
+
if len(decks[i]) != 60:
|
|
47
|
+
state[i].status = "INVALID"
|
|
48
|
+
env.steps[0][0]["error"] = f"Player {i}'s deck does not have 60 cards."
|
|
49
|
+
error = True
|
|
50
|
+
if not error:
|
|
51
|
+
_, start_data = battle_start(state[0].action, state[1].action)
|
|
52
|
+
if start_data.errorPlayer >= 0:
|
|
53
|
+
state[start_data.errorPlayer].status = "INVALID"
|
|
54
|
+
env.steps[0][0]["error"] = f"Player {i}'s deck error."
|
|
55
|
+
error = True
|
|
56
|
+
if error:
|
|
57
|
+
for i in range(2):
|
|
58
|
+
if state[i].status == "ACTIVE":
|
|
59
|
+
state[i].status = "DONE"
|
|
60
|
+
return state
|
|
61
|
+
if Battle.battle_ptr == None:
|
|
62
|
+
raise ValueError("battle_ptr None.")
|
|
63
|
+
else:
|
|
64
|
+
error = False
|
|
65
|
+
select_player = Battle.obs["current"]["yourIndex"]
|
|
66
|
+
if state[select_player].status == "TIMEOUT" or state[select_player].status == "ERROR":
|
|
67
|
+
error = True
|
|
68
|
+
else:
|
|
69
|
+
try:
|
|
70
|
+
battle_select(state[select_player].action)
|
|
71
|
+
except:
|
|
72
|
+
state[select_player].status = "INVALID"
|
|
73
|
+
error = True
|
|
74
|
+
|
|
75
|
+
if error:
|
|
76
|
+
state[select_player].reward = -1
|
|
77
|
+
state[1 - select_player].status = "DONE"
|
|
78
|
+
state[1 - select_player].reward = 1
|
|
79
|
+
finish(env)
|
|
80
|
+
return state
|
|
81
|
+
|
|
82
|
+
obs = Battle.obs
|
|
83
|
+
s = obs["current"]
|
|
84
|
+
if s["result"] >= 0:
|
|
85
|
+
state[0].status = "DONE"
|
|
86
|
+
state[1].status = "DONE"
|
|
87
|
+
if s["result"] == 0:
|
|
88
|
+
state[0].reward = 1
|
|
89
|
+
state[1].reward = -1
|
|
90
|
+
elif s["result"] == 1:
|
|
91
|
+
state[0].reward = -1
|
|
92
|
+
state[1].reward = 1
|
|
93
|
+
else:
|
|
94
|
+
state[0].reward = 0
|
|
95
|
+
state[1].reward = 0
|
|
96
|
+
finish(env)
|
|
97
|
+
else:
|
|
98
|
+
index = s["yourIndex"]
|
|
99
|
+
state[index].status = "ACTIVE"
|
|
100
|
+
state[1 - index].status = "INACTIVE"
|
|
101
|
+
o = state[index].observation
|
|
102
|
+
o["select"] = obs["select"]
|
|
103
|
+
o["logs"] = obs["logs"]
|
|
104
|
+
o["current"] = obs["current"]
|
|
105
|
+
o["search_begin_input"] = obs["search_begin_input"]
|
|
106
|
+
return state
|
|
107
|
+
|
|
108
|
+
def renderer(state, env):
|
|
109
|
+
return json.dumps(Battle.obs)
|
|
110
|
+
|
|
111
|
+
def html_renderer():
|
|
112
|
+
jspath = os.path.abspath(os.path.join(os.path.dirname(__file__), "cabt.js"))
|
|
113
|
+
with open(jspath, encoding="utf-8") as f:
|
|
114
|
+
return f.read()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
jsonpath = os.path.abspath(os.path.join(os.path.dirname(__file__), "cabt.json"))
|
|
118
|
+
with open(jsonpath) as f:
|
|
119
|
+
specification = json.load(f)
|
|
File without changes
|
|
Binary file
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from .sim import StartData, lib, Battle
|
|
5
|
+
|
|
6
|
+
def _get_battle_data() -> dict:
|
|
7
|
+
"""Retrieve the current state.
|
|
8
|
+
|
|
9
|
+
Returns:
|
|
10
|
+
dict: Current observation.
|
|
11
|
+
"""
|
|
12
|
+
sd = lib.GetBattleData(Battle.battle_ptr)
|
|
13
|
+
Battle.obs = json.loads(sd.json.decode())
|
|
14
|
+
Battle.obs["search_begin_input"] = ctypes.string_at(sd.data, sd.count).decode('ascii')
|
|
15
|
+
return Battle.obs
|
|
16
|
+
|
|
17
|
+
def battle_start(deck0: list[int], deck1: list[int]) -> tuple[dict, StartData]:
|
|
18
|
+
"""Start the battle.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
deck0: List of card IDs included in the first player’s deck.
|
|
22
|
+
deck1: List of card IDs included in the second player’s deck.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
tuple: A tuple containing:
|
|
26
|
+
- dict: First observation.
|
|
27
|
+
- StartData: Battle start data.
|
|
28
|
+
"""
|
|
29
|
+
if len(deck0) != 60 or len(deck1) != 60:
|
|
30
|
+
raise ValueError("The deck must contain 60 cards.")
|
|
31
|
+
cards = deck0 + deck1
|
|
32
|
+
arg = (ctypes.c_int*len(cards))(*cards)
|
|
33
|
+
start_data = lib.BattleStart(arg)
|
|
34
|
+
Battle.battle_ptr = start_data.battlePtr
|
|
35
|
+
if Battle.battle_ptr == None or Battle.battle_ptr == 0:
|
|
36
|
+
return (None, start_data)
|
|
37
|
+
else:
|
|
38
|
+
return (_get_battle_data(), start_data)
|
|
39
|
+
|
|
40
|
+
def battle_finish():
|
|
41
|
+
"""End the battle and free the memory used during it."""
|
|
42
|
+
lib.BattleFinish(Battle.battle_ptr)
|
|
43
|
+
|
|
44
|
+
def battle_select(select_list: list[int]) -> dict:
|
|
45
|
+
"""Select option.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
select_list:
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
dict: Next observation.
|
|
52
|
+
"""
|
|
53
|
+
if not isinstance(select_list, list) or not all(isinstance(i, int) for i in select_list):
|
|
54
|
+
raise ValueError("select_list is not list[int]")
|
|
55
|
+
arg = (ctypes.c_int*len(select_list))(*select_list)
|
|
56
|
+
err = lib.Select(Battle.battle_ptr, arg, len(select_list))
|
|
57
|
+
if err != 0:
|
|
58
|
+
if err == 30:
|
|
59
|
+
raise ValueError("battle_ptr broken.")
|
|
60
|
+
else:
|
|
61
|
+
raise IndexError()
|
|
62
|
+
return _get_battle_data()
|
|
63
|
+
|
|
64
|
+
def visualize_data() -> str:
|
|
65
|
+
"""Retrieve the data to be used by the visualizer.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
str: The data to be used by the visualizer.
|
|
69
|
+
"""
|
|
70
|
+
return lib.VisualizeData(Battle.battle_ptr).decode()
|
|
Binary file
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
class StartData(ctypes.Structure):
|
|
5
|
+
_fields_ = [
|
|
6
|
+
("battlePtr", ctypes.c_void_p),
|
|
7
|
+
("errorPlayer", ctypes.c_int),
|
|
8
|
+
("errorType", ctypes.c_int),
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
class SerialData(ctypes.Structure):
|
|
12
|
+
_fields_ = [
|
|
13
|
+
("json", ctypes.c_char_p),
|
|
14
|
+
("data", ctypes.POINTER(ctypes.c_ubyte)),
|
|
15
|
+
("count", ctypes.c_int),
|
|
16
|
+
("selectPlayer", ctypes.c_int)
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
if os.name == 'nt':
|
|
20
|
+
lib_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "cg.dll")
|
|
21
|
+
else:
|
|
22
|
+
lib_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "libcg.so")
|
|
23
|
+
lib = ctypes.cdll.LoadLibrary(lib_path)
|
|
24
|
+
|
|
25
|
+
lib.GameInitialize()
|
|
26
|
+
|
|
27
|
+
lib.BattleStart.restype = StartData
|
|
28
|
+
lib.BattleStart.argtypes = [ctypes.POINTER(ctypes.c_int)]
|
|
29
|
+
|
|
30
|
+
lib.BattleFinish.argtypes = [ctypes.c_void_p]
|
|
31
|
+
|
|
32
|
+
lib.GetBattleData.restype = SerialData
|
|
33
|
+
lib.GetBattleData.argtypes = [ctypes.c_void_p]
|
|
34
|
+
|
|
35
|
+
lib.Select.restype = ctypes.c_int
|
|
36
|
+
lib.Select.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_int), ctypes.c_int]
|
|
37
|
+
|
|
38
|
+
lib.VisualizeData.restype = ctypes.c_char_p
|
|
39
|
+
lib.VisualizeData.argtypes = [ctypes.c_void_p]
|
|
40
|
+
|
|
41
|
+
class Battle:
|
|
42
|
+
battle_ptr = None
|
|
43
|
+
obs = None
|
|
44
|
+
raminingTime = [[], []]
|
|
@@ -3,20 +3,21 @@ function renderer(options) {
|
|
|
3
3
|
|
|
4
4
|
const DEFAULT_NUM_ROWS = 8;
|
|
5
5
|
const DEFAULT_NUM_COLS = 8;
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
k: '
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
K: '
|
|
6
|
+
const PIECE_IMAGES_SRC = {
|
|
7
|
+
P: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAKaSURBVGiB7Zo9axVBFIafNyYoGhSNYFDUoI1CGtEgooIo/oGAhWApWlhoivgLgp32tlqJBEEsBMXGr0ILkYiVhRaCICL4gUXisbi5QaIkOzvnzL25975wujnvnIezOzs7uzIzukl9rS6gtHrAna4ecKerB9zp6jrg/lZMKmknMAbsAF4DL83sa5HJzaxYACPAA8AWxSxwBVgdXYNKbS0lHQbuA4NLDJsBDpnZ96g6itzDkgaBGywNCzAKXI2spdSiNQnsqjj2nKR9UYWUAj4SPL6ySgGndmzFd/hn8PjKKgX8Jnh8ZZUCvpMw9jdwN6qQcGBJJ4GphJQ+YFrSlpB6IjcekvYAL1j++fs/PQaOm9msZ01hHZY0AExTDxbgKI3tpqvCOizpDHAz0+YXsN3MPjuUBMTewxcdPNYA5x18FhQCLGkDcMDJ7oSTDxDX4ar75ira7egVBrzN0WtYkrzMooDfOnrNmOPKGgJsZu+AT052z518gNhV+qmTzzMnHyAW+JGDxxzwxMFnQZHA12lsD3N02cw+eBSzoOBTymHgI/+eUlaJmxE1Re6l1wIXgE01LY5JOuVYUkNBnd0LvKdeZxfHPRzPqyNgtzrCNuMW8y86bQUMrAdeOcM241pbAQMDwMMg2GZMtBPwVDCs0TjvGms5MI1TjS8FgA24nVOr12PpLLDRyWs5jUuq/cqYDSypH7iU65OgPmCidrbD5XyaMpfy3/EDGGrVJT3p4JGq5i4uWVnAkkYI/PC1jMbrJOV2+GBmfo5GJa1LTVrJwKuA/alJucCjmfm5Sp4/F3goMz9XyfP3gBO1OTM/V8nz1wae/zqYvEo6K3k7m/Pr4RytewY39S01odifeO2irvubtgfc6eoBd7q6DvgP/gH4mMhDFawAAAAASUVORK5CYII=',
|
|
8
|
+
R: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAJSSURBVGiB7ZqxixNBFMa/b/HOFBGbSDRgoRCQK01IndrG1j/BI0QQ7JLG2sIg5AQ7QbCystByIYVFKq1sUh0kmyKolWE37rPwxDOG252ZzcabnR+8YsObt+/bmXkzO1mKCIqEt+sE8sYJth0n2HacYNtxgm1HWzDJEsn3JGWDHZGkgr+Q/ETyyob7HJKM0/onIiJaBqANQM6wq4r+AuD+hvsEKv5JdkH5CaXnLslvp64PUrRpkry39lspw5y22sN5mHIPu6JlO+ddMJNd/sZE8CXf97VrgKnN53N4nnddNWnqnniQ3G+1Wp9rtdoNrQCGLBaLr6PR6KaIfFFqaPKUAewBeIH8q/M7AJd1ctbu4dOQfADgab/f95rNpnG8TUynU3Q6HYjIAMAjEfmhEyeTjYeIPCO5H0XRk+VymUXIfwjDECLySkQeGgXKqoggn42I8kbDbTx2nUDeOMG24wTbjhNsO06w7TjBtuME206WB/EXG40G6vV6hiH/EAQBfN8vGwfK8H34te/7si1ms5mQ/GicZ0ZibwEIkc951h2TXI2HNMlSpVJ5MxgM9spl8xF3FnEco9frvSR5W0SOdWJkMYffrlargzAMsa3zrN9EUYQwDCsAPpCsi8h31RjGp5YkAwBVoyB6XBORQLVR4Zalwgk2nsPVatVrt9tY+8Jhq4zHY0wmE73GGkvQIYAYJ8tEt9s1X2QVGQ6H60vVEU7qUZIpF60dFqkkUhWxws1hJ9h2dAQ/xq9C8T/xHMA8jWMm/w+fJ9yQth0n2HacYNspnOCf287BmtabMhEAAAAASUVORK5CYII=',
|
|
9
|
+
B: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAARpSURBVGiB7Zo/bFtVFMZ/x7EhUdN2cFSkDMSCdEnKgkSH0IAZACkgAgpL2yhCDGz8UZNgRYINhpQgQAgQIwNiARRRJJOJAVhAuEhtBUIijhpRZJyAGpw4jRMfhmcjU/XZ78XnvbhuPunIkt/1ud/ne8699517RVW5lRDZawJhY19wu2NfcLtjTwSLSEJEBkUk9P5D7VBEhkXkZyALXATyIvJcmBxQ1VAMuB8oAXoDS4XFQ8LaeIjIRWDQ5XEJSKjqlaB5hBLSItKLu1iAGPBgGFzCyuHbPbTpDJwFIQlW1Szwe4Nm34XBJcxZ+qU6z95T1V/DIBGm4C+Bj3Fm5Vr8CMyGxiKE5agTOAP8wY2XJAWKwDtAb+B8AhZ7J3C+jtDr7W/g4ZtSMHACyPkQW7Vt4PmbSjDwLHBtF2Jr7UOgo+UFA48A5SbFVu31lhYM9ABXGgmZnZ3VdDqtkUikkeAdYLiVBc97GblsNquqqn19fV5GeQk4bMXRbB0WkceAUY9t//fZAH3AK7smdh2iVo6A024PRISpqSkGB533h56eHgDm5uYoFAqsr68zMzPD2tqam4uTIvKyVsKoKRiFchfwDy5hGY/HdXt7W+thbGysUWifsOBqNcKPAt1uD1dXV0kmk/T39wPOyMbjcaanp1lZWSGfz5NOpxv18TTwbdNMjUb4DD6Wm6WlJVVVTSQSfpaoL1pp0rrNT+OtrS0AyuVyYH24wSqkY34ap1IpBgYGWF5eDqwPVxiF9DPY7Kzq2UetFNLncDb9QWLewomJYFVdBb628OWCIrBg4ciy4nEW56UhCLypqhsmnizyoiaXp7HP3XNAxIyjpeCK6LNAaWhoSHO5nG5ubvqyjY0NnZycrIr9Cjhkyc9yL11FCjja2dn5ZDwep6Ojw9ePVZVoNAqwCJxSVdcN9m5gftQiIi8CbwNEo9Eqec8ol8v/bUyAeVV9ypSgcTjfjTOjWubwaUuO1nXpUeyPTE5aOjPN4fHx8dGRkREiEbv/MZPJPCQiola5ZxjOsYWFhS01RrFY1O7u7geseFqOcGxiYiKWTCa9lm48IZPJUCgUDlj5M52lRSSPU7m0xjFVvWThyHrS+tzYH8AlwO5k0So3KpESAV6gTn3Lh5WBDzDeaQVyx0NEDgKP49ShhnHC3EtirwEXgM+AT1XVV4XAE7fdChZnZuoFjuBUI2rtQOX7OyqfvTiHa0dc3G3gFOiywJ8VywFXcS681NpV4LKqlnbF26tgETkOPAHcC9wFJPB2dyMI7OBcoVgEfgLeVdVFT7/0kJeHgE8IvoTTjJWAV6kMYF09DcR2Ad+0gCCvlgbizQh+vwVE+LXzzey0Cg2e+8VvwA/A9zhL133AceAYdvv6Qr29d8NJS0ROAW/gzLwXgF+AyzWWw1lyokBHxQRnYqnaNpBT1b9c+ujCOSWs9VH9A2p97AAHK22rdhS4BzgMvAW8pqrXXPUEsQ7vBby+UbWNYK/YvxHf7tgX3O645QT/Cym/GLJbVZClAAAAAElFTkSuQmCC',
|
|
10
|
+
Q: 'data:image/png;base64,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',
|
|
11
|
+
n: 'data:image/png;base64,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',
|
|
12
|
+
k: 'data:image/png;base64,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',
|
|
13
|
+
p: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAASLSURBVGiB7ZrLS2NnGIefL6YJEuM4GaOiUvDCgJIKI0Y7KFJ3BSm00j9gil1ULbRi+1cUV92U7gahSLvUzeAiVG3QCoOggxmvgRlRY2uKSLSivl3oV2JJgyfnYrw8EDgh3+X35P3ycc7JUSLCXcJ13QGc5l74tnMvfNu5F77t3Dlh93VMqpQKAmGgEVgGfheRbUcmFxHHXkAx8CMgGV4/AaV2Z1BOnVoqpaqAKPCux+Ohra2NUChELBZjZmaGw8NDgF2gXURWbAviYHVfANLS0iJLS0uSTjwel87OTl3pKFBgWw6HZD8BJBAIyNbWlmQimUxKZWWllv7MrixO7dIdAAMDA1RUVGRsUFJSwtDQ0KX2duCU8BOAcDictVHa50/sCuKUcArg5OQka6PT01N9eGRXEKeEXwFMTExkbRSJRPRhzLYkDm1a7wPi8XgkHo9n3LR2d3fF7/frTevDG7tpKaUeA88BPB4P8Xg8Y7u3b9/idv974veDUsqe37HNlS0GXgMSCoUkFotlrK5mY2NDwuGwrvImUGZ5JpuFfwakqalJDg4Osspqjo6OpKOjQ0tP3Bhhzi8MpKioSNbW1q4kq0kkElJaWqqlO6zMZedv+EuA3t5eamtrDXUMBoP09/frt19ZmsrGCq8DMj8/b6i6ms3NTV3hPStz2XK1pJRyA0cul6sglUrh9XpzGicQCJBMJgECIpK0IptdS7oMKCgpKclZFs6FL6iyIhRg65L+A5CNjY2clvTe3p4opQQ4BgqtymXnpjUDMD09nVPnaDSqv7iXInJoVSg7haMAk5OTOXWemprSh79ZlOccG5d0OyBer1dmZ2cNLedYLCbFxcV6l/7I0lx2CV9Ifw9IdXW17OzsXEl2f39fGhoatOwvlmeyWdgNRADp7OyU4+PjrLJnZ2fS09OjZReBohsjzPn+8AXwJyBut1vGxsayCkejUfF6vVr4L+BrwJ33wsBDYPYiuHR1dcni4mJWWc3q6qp0d3en369eACryVhjwAr8CUlVVJaOjo1cS/S/j4+NSV1enpV8C/rwTBhQwqjeqN2/e5CSrSSQSUl9fr6VfAO/km/B3gDx48EAWFhZMyWpWV1elrKxMSz/PG2Ggh4v7VpFIxBJZzdzcnPh8Pi39eb4IzwEyPDxsqaxmZGREC78GXNcqDHzAxV8pV72VY5STkxOpqanR0h/nmtWqc+lv4PyvFJ/PZ9GQlykoKGBwcPDSfDlhQXUbgbPCwkJJJBK2VFdzcHAggUBAV/npdVV4CFDPnj0jGAxaMNz/4/P56Ovr02+/zWkQCyq8A8jy8rKt1dVsb2+Ly+US4G/AazivSdk6QCorKx2R1TQ2Nupl3WY0s9kl/RSgtbXV5DDGSJuvzWhfs8LvATQ3N5scxhhp84WM9jUrXApQXl5uchhjpM33yGhfs8KP4NLtVEdIm8+wsKkb8UqpaaC9trYWv9+f8zhGSaVSrKysALwSEUPL2uyTeA8B1tfXTQ5jbn4jmBX+lPML/usi+0MjGXDsSbx84c49TXsvfNu5F77t3DnhfwDnGIncxPZT9AAAAABJRU5ErkJggg==',
|
|
14
|
+
r: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAMiSURBVGiB7ZqxTxNRHMc/P2jC2dDCYmhtB8cuJgQaWQwFRx1kdHAQHCr/ghOyMLAQBnRhI4Q4MTBoTEzYJDQhONIOGG3aktCQGHFo6HOAMyBX2nv3oHq9T/JLmrv3fvf73nv37t7vV1FK0Ul0tTuAmyYQ7HcCwX4nEOx3AsF+R1uwiFgi8l5ElIMtiYi4aK9E5IuI3Ha4zrSI1Ftt3xSllJYBY4C6wmIu2yvgpcN1ym7aNzPPUzqTyVxwODAwYJ96IiJPbQMeOrVXSpHNZu0+6fN9zvpZAKVSyam9a0LaPRvQ399PpVIBeNvo/BXHXpzZBXp6erAsy0h8xgWvr68zOztLvV6/dM6yLObn5y8dn5mZ4fj4mIODA0efU1NTjjdKB+OCU6kUq6urrvpYlsXi4qLpUBzxLLhcLrO2tmYilpbZ29vT7+xhlX5G81X3um3Zbdyim/EQkTCQB+6Ew2HGx8fp7e3V8tUqtVqNzc1NDg8PAX4AQ0qpgisnuiN8dqP6gM+ASiaTKp/Pq+vi6OhIjYyM2CO7D9zViVl7hG1EpA/4AIwkk0nm5uYIhYyvhSwsLLC1tQXwFRhTSu3r+PEsGP6I/hSNRoeGh4fPf3wYoVqtsru7S6VSKQIPdMUC3qb0X9P7MaAymYzx6ZzNZu2p/MprnCZ3Sz8N+mpE1auDYHvodwLBficQ7HcCwX4nEOx3AsF+p+MEm9yp9wDs7OwwMTFh0C3kcjn7Z8SrL5OCnwOk02kmJycNuoVIJMLKygqcJg4vJ7bdYGjznwJqXV1dant723gCoFQqqWg0aicBHnmJ1fMIi4gFvANCg4ODFAoFCgV3icRWGB0dZWNjA2BZRO4rpb5pOTIwuh+54Xy0iHwHbrVlhIF7AIlEgu7ubgPurqZYLHJycpLgNEX8y21/Y4tWLpcjFouZcteQWCxmVye16Lj3cMcJ1lmkpoE67S+knbclzooKzcx15UFEyoDZ0oIZ4kqpcrNG2lP6/H8u2mluyzod9wxrv5bi8bjJOG4MnRF+zelC8S/xBmjp5WykXPo/0XHPcCDY7wSC/U4g2O/8BtFWHbaczITCAAAAAElFTkSuQmCC',
|
|
15
|
+
b: 'data:image/png;base64,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',
|
|
16
|
+
q: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAnKSURBVGiB7ZprbBTXFcd/dx9erzfZ4MYYamNiW07kxhEP4z6QI9QEIih5qW6wSqSYSEALal6gpk0qqkilbUiiVgYqpYIo6QM1JSZVRKKIqo0iBUg/bBKpihYkEjDYa2MDsr32Pu21Tz/szDDenX0ajEo40tXcnfOfe89/zmPuzF0lInyVxHatDZhtuUH4epcbhK93uSaElVIOpdSt12LuWSWslCpRSj0PnAUuKaU+VErdN5s2ICIzaiRv2v3A20AXsAZQGbB7AAHE6XSK1p8EvpljjgrAMVNbReSKED6kkzC1Axa4m4FRQA4fPixjY2PS0dGh4/+WYezvAUc1TA/wLOC8ZoSBpYB4vV55+eWX5aWXXhKPx6OTuDMFuwKQhoYG0eXYsWM6tt9i7LuBqZRoEOCVa0n494Bs27bNILF161bdsN+mYG8CgoB0dXXJ0NCQPProozr2LxZj/wOQzZs3SygUkq6uLh0bBG66KoQBB3BrFv2zgKxdu9YgvHLlSt2wJy3wv9M9pZTScQmg2QI7BMgnn3xijL1gwQL9mhXF2JuRMFACPA8EtAk+BO6zwH0NCANSW1srtbW1Zi/cnMGgbUBUw/mzGP8WII8//rgMDw/LgQMH9LGHAU8x9mYjnHc1BdYDA6YcOw/8IEtU2ICQhn09C265Nqc5GtJSpWB7LS4uppqWmAx6Nkea3GnC/jcH9l6S1VmAMeBJwD4Te60mKaiamq7TMSdykHjMhJ0A3FmwZcCIhj2YAVNY9bcYoKBqakFYgNYsuM4U7HeyYDeacH/PgCms+mcYxKimNpvNnBNp1TQD4T9lwR0DzBX3J1mwvlyEs9hrWf0zraV/DmwHzk5NTYl2LgZcyIA3xGazAaxTSt2SqlNK2YAlABs3btRPL7MaRym1DGjJNZ8mX2jHSc3eI8C9IvJZGjJHvtmARVy+y4dzeXj16tU6dqsFpgmQ+vp6OXr0aNbCBewH5LbbbssV0l/ncp4fJ8eiJCthE+moifS6bIRNK6LPLDAdgLS3t0soFNLDL61wAV4gpJSSnTt35iL8tsm2/bn45Hw9FJEp4KTp1F6lVHkm/MMPP8zcuXMBliqlmlPUywCWLVuGx+OhsbERkouRxSm4xwDPqlWraGhoyGibUur7QJvplD8Hnbzfh/1g5Oc84JVMQKfTSUdHh/5zc4q6BZKEzUfS83gLwJYtWzIapNWIP5jsAjiRmYImuUJAC5vnAHnwwQfF7Xbr4fNdq5AWETl58qSOCQJlmt6OtgwdGhoSEZHdu3fruNdN47QCUlVVJRMTE/Lmm29ahjTwR0BWrFgh8+bN0zHVMw5pTfwA8XicF154QT+3TylVagVubGyktbUVkrnYrp3+BlBWX19PeXkyIzJ4eCvApk2bcDgclsYope4GfuRyudi1axeDg4MAQRHpy8kkTw/XA1JdXS0TExOyePHitHUtJg+LiLzxxhs65rim3wDIunXrDEw4HBa73W4ULuBWIGa326W3t1dEJM3DgItkTZGdO3fKRx99pOs/zotLnoRtaOE4PDwsPp/PbOgiK8LhcFi8Xq9uzJ1oC/xdu3aJWZqamowVF/BTQB566CFDb0H4V4DcddddMj4+Lq+++qqufy0fLnmFtLlSnzhxgpaWFp566ilIVtjXlFL21GvKyspYv369/nMTKQVLl5YWY23RAvwYMhcrpVQT8JzNZmP//v04nU78fqMw56zQOpl8vfxnQPbt2yciIqFQyLwo2EaKh0VEfD6frr9ESsHSZc+ePTrmC0Dq6upkcnLSysNvAR8D8sQTTxj6e+65R9evvmIhrRH+GSBPP/20Mdn777+vTxayIiwi5nyXurq6NP3x48fNa3B58cUXp+lNhEOA1NTUyOjoqKGvrKzU9QuuNOH7AVm1atU0g0xvJpaE9+7da+geeeSRNL2pcInT6ZTBwcFMhAWQd99919BdvHjRePzly8O67luLHzDnDACdnZ0cOXKEoaGhaefj8ThDQ0M0NzfjcDhIJBIkEgl2795NMBgkHA5js9nweDyUl5dz6dIlmpubOX36NCMjI1RUVOD1eqeN2d7ezgMPPHDZoMu25F5waKI07+UGKqVIfnXwBAIBvvzySz799FP8fj8ffPAB586dAzDIXQ1pampi6dKlLFmyhOXLl+Pz+XjmmWcguXDZmOt6KIAwgFLqFHB7Plin00lZWRkejwePx5PWLykpIRKJEIlECIfDhMPhaf1oNMrU1FS+pu0Qkd/kAywkpAH6gduVUsyfP5+6ujrq6+vTjvPnz8fpdBY4dLqEw2HOnTtHd3c3Z86cmXbs7u5mbGxMh57Ke9B8k12LhC2ArFy5UqamptIK0GzKqVOnxOVyCcndiVus7LVqhRKeB1wEZMeOHbNK0Cyjo6PmFVraPla2VlAOAyil7gX+CTgOHjxIe3u7oUskEvT09BAIBIhGo8TjceLxOLFYzOjrvxOJBC6XC5fLRWlpqdE3//Z6vdTW1lJZWTktItva2njnnXcgWZ2/LSKhvO0vlLBG+klgT0lJCWvWrCEYDNLd3U1fXx+Tk5MFj5dLysrKqK2tpa6ujgsXLuDz+SC5FfMtETldkO3FEAZQSr1HcjFiiN1up7q6mpqaGjwezzTPpfYdDodlBJj7oVCIs2fPMjAwYGXCfSLy74INLyT+ZXo+u9Hyefv27XLmzBmZmJi4KjkbiUTE7/dLY2Ojnrd/LdruYi8UU9Vua2u7KkTNMjo6qpOdwGKjLt9W6HM4VUoBqqqq6O/vp7e312iBQICRkRFisRjRaJRoNDqtn0gkKC0tpbS0FLfbjdvtntafO3cuNTU109qcOXMYGRlxaNE1lt20KxzSmof/BRiL/6vdTPNYfirOp82kaP0CMJZzFRUVLFy40PDGwoULKS8vNzyW2hwOh+Fxs+ej0SiRSISBgQF6e3vp6ekxoiYSiejTXSS5jRIo2PAiPdsATNjtduns7JRIJHKlUjWr9Pf3S2trq+7lXxdle5GEfwnIhg0bZoWoWQ4dOqQTfq8Y2wv+Y5q2IfZDSO4yzLa43W6926CUKrzoFnqHgLWQ3OSKxWKz5lldYrGY3HHHHbqXNxRqfzF/PRwHmJycxO/3mwvJVRcRIRgMsmjRIv1URaFjFPPy4AT+g2m3YM6cOVRXVxutqqqKyspK4zlr1fSlZSwWs2zhcJjz588TCATo6+ujr6+P/v5+xsfH9WnHgRYR+bwgAoWGhHaDbib52fYsEGcWnsGmFgQ+B9qKsb3o57Au2reuCqAaqNJaNcltk1KL5tKODu1mxUxHc4sAg0AfyS8tfUCfiIRnZO9MCf+/yY1/xF/vcoPw9S5fOcL/AzHnOtrDXjPpAAAAAElFTkSuQmCC',
|
|
17
|
+
N: 'data:image/png;base64,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',
|
|
18
|
+
K: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAkySURBVGiB7VprTFNbGl2bIkT0om0BaZURxFxFJb4IgWjGDHHCnfESRNErJDOM4CMRozc611GDMZqZkRmTCcboVTQgPgbJgFGC0Yn4QK9FARkfKJGgVTRW0BYFbZHWrvnRh4g8SgvlxuuXfOnp6bf3t1bX3t/ZZ58jSOKXZB6DDcDd9oXw525fCH/uNiiEhRBSIcTvByP3YCm8C8BpIcRsdyceLMK+1k8/dycW7lp4CCH+DOBb69fJsJC9C+AlgPcAMkiWDzQOz4FO0MFSAYR1Oje5w/HvAAw4YXcqPAofCP8VwCwAWwD8BKANwHW6AYzbFCbZCKARAIQQL62na0hechcG4Mt1eOBNCOEBQGb9GuDu/CDpNgcQC+AFAHbwnwAEuA2DG8kuBNDeiazN6wEo3YHDLVVaCCEDUAdA3kNYPsnkgcbirjmcgZ7JAkCSECJyoIG4RFgI8ZUQIkQI4dVLqKM3Ct3GWXOFOpCrZ3NiLn4DoAjAAwBmWObgewANAEoBLAPg0yF+GLqet115cYd2wQD+CaAKluWnLcYIy5L0OIDfDtgcFkIEAsjy8PD4bsGCBYiMjIRMJoNcLserV69QUVGB69ev4/bt2zCZTK8B5FkBa2ApVhIH0hwHkAVgM4BvhRAeEyZMQHR0NKKjozFq1ChotVrodDrcvXsXx48fh8FgOAvgB5I1DhFxUNUgIUTT0qVLWVdXx56submZmZmZVCgUBKAD8B2A53BMYRUAk7e3N1etWsXHjx/3mOvFixfcvn075XK5EcD8flHYulAo3b17929Wr14NAHj48CFycnKg0Wig1WqhUCgQERGByMhIhIeHAwDa29tx9OhRbNq0CU1NTc8AKHv784UQWLVqFTZv3gyl0hLe0NCAq1evoqKiAmq1GnK5HAEBAYiPj0dUVBQAQK1WY86cOYYnT57MIVnpksIAfkhLSyNJtrW1MSUlhRKJpFuVIiIimJ+fT6PRSJLUaDSMiYnpVV2ZTMaSkhK7ehUVFUxISKAQots20dHRrKqqIknev3+fCoWiCcDYHvn0QtZ30qRJbe/evSNJpqSkOFp8GBISwsuXL5Mk379/z+3bt3cLfsaMGXz06JF9mMbFxTmcRyqV8tatWyTJyspKAihwhfDCLVu2kCQPHz7sMAibSyQSbt26lSaTiSSZlZX1SUxoaChfvnxJkiwvL2dQUFCf8wQEBLCpqYkkGRYW1gLA01nCORcvXiRJzp8/v89AbB4TE0ODwUCS3Lhxo/38yJEjWVtbS5I8d+4chwwZ4nSOgoICkmRqaioBzHaWcG1bWxtJUi6XOw0GAOPj4+1KJycnUwjBc+fOkSRv3bpFX19fl/pft24dSfLHH38kgO+749TbSuvNmzdveglxzE6dOgVblc/KysKKFSswd+5caDQazJs3Dy0tLS71L5NZ7ji1Wi1guQx2bb0ofPrGjRskyenTp7ukgM1VKpW9kHUYgi77mTNnSJKJiYkEEOHskP7bwYMHuy04zvisWbNos9ra2h4vcY76hAkTaDQaaTKZOGbMGD2AYc4SDgoPDzeZzWYaDAYqlcp+IV1WVkaSTEtL65f+Tp48SZLct28fAWT2yKmnH62k/33ixAmSpEqlore3t8sACwsLSZILFy50uS/bZbOlpYWBgYGtAOSuEg7w9/d//vDhQ5LkmTNnKJVKXQJpu4QsXrzYpX7S09Pt9cA6d//YK5/eAqykfx0eHm5qbGwkST548IBTp051GJhEIqFSqeTMmTMZFxfHvLw86nQ6JiUlcdy4cRw6dGifiPr4+DA3N9deC1avXk0A/3CIiyNBVtKxY8aMaSkvLydJ6vV6rl+//qMh7u3tzZiYGO7YsYMlJSWsrq6mRqOxV+SerLm5mffu3WNpaSkPHDjApKQk+vn5fUI2LCyMNTU1JMnW1lYmJycTwD4AHv1K2Ep6kre3t3rPnj12oDU1NUxISODp06f59u3bLsm0tbVRrVZTpVKxqKiIe/fu5bFjx3jhwgXW1tby1atXXbYzm82sqqrihg0bGBQUxG3bttlz3L59mxMnTmwHsLxPHPoSbCXtD6AsNjaWjx8/pu3GgrTMpWvXrnHbtm2MjY3l5MmTKZPJHB6moaGhnD17NtPT01lcXMzW1taP+rZZdnY2fXx8ngGI6jP+vjawkhaLFi3KtQGor6/nmjVrXF5+dnYvLy/GxcWxtLTUTvbIkSMEkA9glDPYnd6m9fPz+0NKSsrh+vp6lJSUwGw2f/T78OHDoVQqoVAooFAo7MdKpRIBAQF4/fo1NBoNNBoNnj179tGxVqtFZ1xTpkzBvHnzcPbs2f/evHnzG6dAA84pbAWzBB3UkEqlTExMZHZ2NtVqNV2xlpYWnjp1iunp6Rw/fnxn5Y87i9klhYUQSwDkz5gxA2vXrsWSJUvg5fVhB1Wv13+inO24sbERI0aMsKvfeRTI5R9vYZeXl2PXrl0oKiqCyWQqILnEKdCA8woHBwf/6fz583ZVjEYjL168yI0bN3L69Ok9bs305qNHj2ZqaioLCgrY3Nxsz9HQ0MAFCxacd0VhpxumpaVtMJvN1Gq1zMzMdHinQghBuVxOLy8vh+KHDh3K5cuX886dOyTJ/fv33xm0IR0cHJzf2NgIg8FgPy+RSBAREYHw8PAui1ZgYCA8PT1BElqttsuipVarceXKlU/ukceNGweNRvMfvV6/2CnQQP8ULR8fH6amprKwsJA6na7rSmQ1s9lMnU5n39XszoxGI69cucKMjAyGhIT8PIrW2LFj89PT07Fs2TJIpVL7b3V1dVCpVHj69OknRev58+dob2+HEAL+/v5dFq2pU6ciKioKnp6WNzLMZjOKi4uRlZWFsrKywSlaixYtWqvX6+2KXLp0iStXrmRwcHC/LDp8fX2ZkJDAQ4cO2TcASXLnzp3VrijsdMO8vLyvq6ur3+Xm5nLatGn9usLq7P7+/szIyGBlZaU5Jyfn74NC2DoVhgH4Cz5+ujcQbgJwGMB4V/C6TLgD8a8AfA/L+xrv+5GoGsC/AHzdHzjpStHqzqyPVeMBTAPwKwBB1s8RPTR7B+ApLM+YG2B55+M0yf/1Kzi49008X3wgrwTQDOAJLASb6CYgbiP8c7Evb+J97vaF8OduvzjC/wfZAaeBxqTRygAAAABJRU5ErkJggg=='
|
|
19
19
|
};
|
|
20
|
+
|
|
20
21
|
const LIGHT_SQUARE_COLOR = '#f0d9b5';
|
|
21
22
|
const DARK_SQUARE_COLOR = '#b58863';
|
|
22
23
|
|
|
@@ -98,7 +99,7 @@ function renderer(options) {
|
|
|
98
99
|
alignItems: 'center'
|
|
99
100
|
});
|
|
100
101
|
const whitePawnImg = document.createElement('img');
|
|
101
|
-
whitePawnImg.src =
|
|
102
|
+
whitePawnImg.src = PIECE_IMAGES_SRC.P;
|
|
102
103
|
Object.assign(whitePawnImg.style, { height: '30px', marginRight: '8px' });
|
|
103
104
|
const whitePlayerName = document.createElement('span');
|
|
104
105
|
whitePlayerName.textContent = environment.info?.TeamNames?.[1] || 'Player 2';
|
|
@@ -134,7 +135,7 @@ function renderer(options) {
|
|
|
134
135
|
fontWeight: 'bold'
|
|
135
136
|
});
|
|
136
137
|
const blackPawnImg = document.createElement('img');
|
|
137
|
-
blackPawnImg.src =
|
|
138
|
+
blackPawnImg.src = PIECE_IMAGES_SRC.p;
|
|
138
139
|
Object.assign(blackPawnImg.style, { height: '30px', marginLeft: '8px' });
|
|
139
140
|
blackPlayerContainer.appendChild(blackPlayerName);
|
|
140
141
|
blackPlayerContainer.appendChild(blackPawnImg);
|
|
@@ -167,7 +168,6 @@ function renderer(options) {
|
|
|
167
168
|
justifyContent: 'center',
|
|
168
169
|
alignItems: 'center',
|
|
169
170
|
flexGrow: '1',
|
|
170
|
-
overflow: 'hidden',
|
|
171
171
|
width: '100%',
|
|
172
172
|
minHeight: '0'
|
|
173
173
|
});
|
|
@@ -197,7 +197,7 @@ function renderer(options) {
|
|
|
197
197
|
// Status Container
|
|
198
198
|
const statusContainer = document.createElement('div');
|
|
199
199
|
Object.assign(statusContainer.style, {
|
|
200
|
-
padding: '
|
|
200
|
+
padding: '5px',
|
|
201
201
|
backgroundColor: 'white',
|
|
202
202
|
borderRadius: '8px',
|
|
203
203
|
boxShadow: '0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -1px rgba(0,0,0,0.06)',
|
|
@@ -205,7 +205,7 @@ function renderer(options) {
|
|
|
205
205
|
width: 'auto',
|
|
206
206
|
minWidth: '200px',
|
|
207
207
|
maxWidth: '90vw',
|
|
208
|
-
marginTop: '
|
|
208
|
+
marginTop: '10px',
|
|
209
209
|
flexShrink: '0'
|
|
210
210
|
});
|
|
211
211
|
if (!environment.viewer) {
|
|
@@ -214,7 +214,7 @@ function renderer(options) {
|
|
|
214
214
|
|
|
215
215
|
currentStatusTextElement = document.createElement('p');
|
|
216
216
|
Object.assign(currentStatusTextElement.style, {
|
|
217
|
-
fontSize: isMobile ? '0.
|
|
217
|
+
fontSize: isMobile ? '0.8rem' : '1.1rem', // Responsive font size
|
|
218
218
|
fontWeight: '600',
|
|
219
219
|
margin: '0 0 5px 0'
|
|
220
220
|
});
|
|
@@ -222,7 +222,7 @@ function renderer(options) {
|
|
|
222
222
|
|
|
223
223
|
currentWinnerTextElement = document.createElement('p');
|
|
224
224
|
Object.assign(currentWinnerTextElement.style, {
|
|
225
|
-
fontSize: isMobile ? '
|
|
225
|
+
fontSize: isMobile ? '0.9rem' : '1.1rem', // Responsive font size
|
|
226
226
|
fontWeight: '700',
|
|
227
227
|
margin: '5px 0 0 0'
|
|
228
228
|
});
|
|
@@ -297,7 +297,10 @@ function renderer(options) {
|
|
|
297
297
|
// Calculate and apply board size
|
|
298
298
|
const containerWidth = currentBoardContainer?.clientWidth ?? width;
|
|
299
299
|
const containerHeight = currentBoardContainer?.clientHeight ?? height;
|
|
300
|
-
|
|
300
|
+
let smallestContainerEdge = Math.min(containerWidth, containerHeight);
|
|
301
|
+
// This is greedily trying to take as much space as possible, which can cause some conflict with flex box calculations for other elements
|
|
302
|
+
// we are going to take 24px off (arbitrary) to give the flex box renderer a bit of space to work with. Without it we will get some clipping.
|
|
303
|
+
smallestContainerEdge = smallestContainerEdge > 200 ? smallestContainerEdge - 24 : smallestContainerEdge;
|
|
301
304
|
const newSquareSize = Math.floor(smallestContainerEdge / displayCols);
|
|
302
305
|
|
|
303
306
|
if (newSquareSize !== squareSize) {
|
|
@@ -340,7 +343,7 @@ function renderer(options) {
|
|
|
340
343
|
const squareElement = currentBoardElement.querySelector(`#cell-${r_data}-${c_data}`);
|
|
341
344
|
if (squareElement && piece) {
|
|
342
345
|
const pieceImg = document.createElement('img');
|
|
343
|
-
pieceImg.src =
|
|
346
|
+
pieceImg.src = PIECE_IMAGES_SRC[piece];
|
|
344
347
|
pieceImg.style.width = `${pieceSize}px`;
|
|
345
348
|
pieceImg.style.height = `${pieceSize}px`;
|
|
346
349
|
squareElement.appendChild(pieceImg);
|