open-optcg-model 0.0.0-stage → 0.1.1-next.1

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.
package/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 daavoo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ This license covers the engine, tooling, and site code in this repository.
24
+ It does not cover One Piece Card Game trademarks, card text, or card artwork,
25
+ which remain the property of their respective owners (Bandai / Eiichiro Oda
26
+ / Shueisha / Toei Animation). See README.md for how card art is handled.
package/README.md CHANGED
@@ -1,3 +1,76 @@
1
- # Temporary Holding Version
1
+ # open-optcg-model
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The learned One Piece Card Game opponent from
4
+ [Open OPTCG](https://github.com/daavoo/open-optcg), for the
5
+ [`open-optcg`](https://www.npmjs.com/package/open-optcg) engine. MIT licensed.
6
+
7
+ It is a small neural scorer: given what its seat can see, it ranks the actions
8
+ the engine offers and plays the best one. It sees only that seat's view, so it
9
+ plays fair.
10
+
11
+ ```sh
12
+ npm install open-optcg open-optcg-model
13
+ ```
14
+
15
+ ## Play a game
16
+
17
+ ```ts
18
+ import { GameService } from "open-optcg/engine";
19
+ import { deckManifests, resolveDeck, STARTER_DECK_IDS } from "open-optcg/decks";
20
+ import { fetchLatestModel } from "open-optcg-model";
21
+
22
+ const model = await fetchLatestModel();
23
+ const [deck1, deck2] = STARTER_DECK_IDS;
24
+ const seats = {
25
+ p1: model.agentFor(deckManifests[deck1]!),
26
+ p2: model.agentFor(deckManifests[deck2]!),
27
+ };
28
+
29
+ const service = new GameService();
30
+ let view = service.create({
31
+ p1: resolveDeck(deck1),
32
+ p2: resolveDeck(deck2),
33
+ seed: "readme",
34
+ });
35
+ for (let step = 0; view.phase !== "finished" && step < 5000; step++) {
36
+ const action = await seats[view.activePlayer].chooseAction(view);
37
+ view = service.act(view.gameId, view.revision, action.actionId);
38
+ }
39
+ console.log(`${model.release}: ${view.winner} wins on turn ${view.turn}`);
40
+ ```
41
+
42
+ `agentFor(deck)` returns an `Agent`, the same contract any player implements
43
+ (`open-optcg/protocol`). A human seat and a model seat are interchangeable in
44
+ that loop.
45
+
46
+ ## Weights
47
+
48
+ The package holds the code. The weights are a separate download of about 12 MB,
49
+ published as `model-*` releases on GitHub and retrained more often than the
50
+ engine changes:
51
+
52
+ - `scorer-model.json`: the base model.
53
+ - `scorer-adapter-*.json`: optional deck adapters, each trained piloting one
54
+ Leader's list. `agentFor` uses the matching one when it was loaded.
55
+
56
+ `fetchLatestModel()` downloads the newest release from GitHub, so it suits Node
57
+ and other servers. GitHub serves release assets without CORS headers, so a
58
+ browser app should host the files itself and load them by URL:
59
+
60
+ ```ts
61
+ import { loadModel } from "open-optcg-model";
62
+
63
+ const model = await loadModel("/models/scorer-model.json", {
64
+ adapters: ["/models/scorer-adapter-st31.json"],
65
+ });
66
+ ```
67
+
68
+ `loadModel` also accepts the parsed JSON objects. It refuses a pack trained on
69
+ an input encoding this version of the code can't read, with an error naming the
70
+ mismatch, instead of playing badly.
71
+
72
+ ## Versions
73
+
74
+ `open-optcg-model` is a peer of `open-optcg` at a matching minor version.
75
+ Install both, and your app runs one engine that both the game and the model
76
+ use.
package/dist/index.js ADDED
@@ -0,0 +1,222 @@
1
+ var CpuMatmul=class{matmul(a,m,_key,w,bias){const[k2,n]=w.shape;const b=w.data;const out=new Float32Array(m*n);for(let i=0;i<m;i+=1){const row=i*n;if(bias)out.set(bias,row);const ao=i*k2;for(let p=0;p<k2;p+=1){const av=a[ao+p];if(av===0)continue;const bo=p*n;for(let j=0;j<n;j+=1)out[row+j]+=av*b[bo+j]}}return out}};function byteToUnicode(){const bytes=[];for(let b=33;b<=126;b+=1)bytes.push(b);for(let b=161;b<=172;b+=1)bytes.push(b);for(let b=174;b<=255;b+=1)bytes.push(b);const chars=bytes.slice();let extra=0;for(let b=0;b<256;b+=1){if(!bytes.includes(b)){bytes.push(b);chars.push(256+extra);extra+=1}}const map=new Array(256);bytes.forEach((b,i)=>{map[b]=String.fromCharCode(chars[i])});return map}var BYTE_CHAR=byteToUnicode();var ENCODER=new TextEncoder;import{deckManifests}from"open-optcg/decks";import{PAYOFF_KINDS,TIME_LIMIT_ADDITIONAL_TURNS}from"open-optcg/protocol";var LEADER_PRIORS={"EB01-001":[2,"EB01-002:4 EB01-013:4 EB02-006:1.5 EB03-016:4 EB04-012:3 OP01-055:3 OP06-038:1 OP07-022:4 OP12-023:4 OP12-034:2 OP13-031:1.5 OP16-003:2 OP17-003:3 OP17-007:3.5 OP17-015:1 OP17-022:2.5 ST16-004:0.5 ST32-001:1.5 ST32-002:4"],"EB01-021":[1,"EB01-028:2 EB01-061:4 OP08-069:2 OP13-042:4 OP13-054:2 OP13-076:2 OP16-042:6 OP16-044:3 OP16-045:2 OP16-048:3 OP16-054:4 OP16-055:4 OP16-056:4 OP17-049:2 OP17-074:4 ST10-010:2"],"EB02-010":[113,"EB02-017:4 EB02-019:1.84 EB02-035:4 EB02-036:1.24 EB02-037:1.53 EB02-041:3.41 EB02-061:2.59 OP03-072:1.19 OP05-061:0.35 OP05-070:0.44 OP05-076:3.28 OP05-077:0.69 OP07-064:3.96 OP07-066:0.85 OP08-036:1.89 OP09-078:3.96 OP10-041:0.27 OP12-037:0.38 OP13-040:0.55 OP14-031:0.4 OP15-032:0.31 OP15-078:0.65 PRB02-005:0.34 PRB02-012:0.81 ST18-001:4 ST18-004:4 ST18-005:1.76"],"EB03-001":[4,"EB03-006:1.75 EB03-011:1 EB03-024:2 EB03-025:1 EB03-026:1 EB03-027:1 EB04-007:0.5 EB04-023:0.75 EB04-024:1 OP05-118:2 OP06-007:3.25 OP06-009:0.75 OP06-058:0.25 OP07-003:0.5 OP08-047:1.5 OP08-118:2 OP09-008:0.5 OP09-009:3.5 OP10-005:1 OP10-011:4 OP10-045:1 OP11-012:1.5 OP11-054:3 OP13-007:1 OP13-011:1 OP13-012:2 OP13-042:0.5 OP13-043:1.75 OP13-054:1.75 OP13-058:0.5 OP16-056:2 OP17-046:1.5 OP17-050:0.5 PRB02-008:1.75 ST23-001:1"],"EB04-001":[9,"EB01-059:0.67 EB03-053:3 EB04-002:3.56 EB04-007:3.56 EB04-008:0.89 EB04-052:1.67 EB04-053:2.22 EB04-054:0.67 EB04-056:0.67 EB04-058:3.56 EB04-061:3 OP06-115:0.44 OP07-099:1.33 OP07-104:0.78 OP07-107:1.11 OP07-111:0.44 OP07-114:1.33 OP08-105:0.33 OP09-009:0.67 OP09-118:0.89 OP11-106:1.67 OP12-112:3.56 OP12-119:0.44 OP13-108:1.67 OP13-113:0.44 OP15-119:0.33 OP16-104:0.78 OP16-108:0.44 OP16-119:1.11 OP17-102:0.44 OP17-106:0.33 OP17-107:0.33 OP17-109:0.44 OP17-112:0.44 OP17-113:0.33 OP17-114:0.44 ST21-003:1.44 ST29-004:0.44 ST29-010:0.44 ST29-015:3.11"],"FIX-LEADER-B":[1,"FIX-001:20 FIX-002:30"],"FIX-LEADER-R":[1,"FIX-001:30 FIX-002:20"],"OP05-060":[49,"EB01-061:2.22 OP05-070:1.04 OP05-074:3.06 OP05-077:0.37 OP06-076:2.84 OP07-064:3.65 OP08-069:2.37 OP09-065:2.57 OP09-069:3.73 OP09-071:0.63 OP09-078:3.92 OP09-119:2.14 OP11-067:0.57 ST10-010:0.53 ST18-001:3.8 ST18-002:2.35 ST18-003:3.76 ST18-004:3.8 ST18-005:3.71"],"OP05-098":[40,"EB01-056:1.1 EB01-059:2.95 EB02-052:1.53 EB03-053:0.38 EB04-053:0.63 EB04-054:0.25 EB04-058:3.85 EB04-059:0.35 OP06-101:0.3 OP06-104:0.55 OP06-115:3.38 OP06-117:0.38 OP11-106:3.8 OP12-119:0.53 OP14-102:1.05 OP14-104:1.73 OP14-110:0.75 OP14-111:1 OP15-113:2.85 OP15-117:0.6 OP16-108:0.4 OP16-119:3.05 OP17-102:2.7 OP17-106:1.75 OP17-107:2.7 OP17-109:2.7 OP17-110:0.25 OP17-112:2.68 OP17-113:0.98 OP17-114:2.55"],"OP06-022":[2,"EB03-057:2 EB04-058:2 OP05-037:1 OP06-104:2 OP06-106:2 OP06-107:1 OP06-109:2 OP06-110:2 OP06-115:1 OP06-118:1 OP08-023:2 OP10-109:2 OP13-031:1 OP13-117:1 OP15-113:2 OP16-119:1.5 OP17-022:2 OP17-102:1 OP17-106:1 OP17-107:2 OP17-109:2 OP17-112:1.5 OP17-113:1.5 OP17-114:1.5 PRB02-016:2 ST28-001:2 ST28-002:2 ST28-003:2 ST28-004:2 ST28-005:2"],"OP06-042":[11,"OP05-077:0.45 OP06-060:4 OP06-061:4 OP06-062:1.18 OP06-063:3.45 OP06-064:0.73 OP06-065:0.55 OP06-068:4 OP06-069:4 OP06-072:1.18 OP06-077:0.27 OP06-079:3.91 OP07-066:3.91 OP07-072:4 OP08-074:3.91 OP09-077:0.73 OP10-063:4 OP10-075:1 OP11-043:0.55 OP11-045:0.27 OP11-047:3.27"],"OP07-059":[12,"EB01-061:3.75 EB04-033:1.75 EB04-036:3.17 EB04-037:3.67 EB04-038:0.33 OP05-074:0.33 OP07-060:4 OP07-064:0.25 OP07-065:3.5 OP07-066:3.83 OP07-069:0.75 OP07-071:4 OP07-072:3.75 OP07-074:0.75 OP07-076:1.75 OP07-078:1.92 OP08-074:0.67 OP09-065:0.67 OP09-119:0.25 OP10-075:3.5 OP11-067:0.33 OP13-076:1.75 OP15-069:0.92 OP17-062:0.5 OP17-063:0.42 OP17-074:0.25 ST18-001:1.58 ST18-002:1.08"],"OP07-097":[13,"EB02-054:1.15 EB02-056:2.77 EB03-058:0.62 EB04-058:0.62 OP07-098:3.92 OP07-100:3.69 OP07-101:4 OP07-105:3.54 OP07-107:3.31 OP07-109:3.15 OP07-110:0.62 OP07-111:4 OP07-113:0.38 OP07-114:1.38 OP07-115:3.62 OP07-117:1.23 OP08-105:0.54 OP08-106:3.69 OP08-112:1 OP09-117:2.62 OP10-112:1.38 OP13-108:0.38 OP13-113:0.31 OP16-119:0.31 ST13-011:0.54 ST20-005:0.31"],"OP08-021":[2,"EB02-013:4 EB03-013:2 EB04-013:3.5 EB04-017:1 EB04-019:3.5 OP07-026:0.5 OP07-031:3.5 OP08-023:4 OP08-030:4 OP08-032:3.5 OP08-034:4 OP08-036:1 OP08-039:4 OP10-032:2 OP12-037:1.5 OP12-118:0.5 OP13-031:1.5 OP14-033:1 OP15-035:2 ST24-004:3"],"OP08-058":[34,"EB03-051:0.35 OP07-077:2 OP08-062:3.82 OP08-077:0.32 OP11-067:1.79 OP11-070:0.5 OP11-106:2.88 OP13-076:0.62 OP15-078:3.18 OP17-074:3.18 OP17-102:0.29 OP17-103:3.97 OP17-104:2.82 OP17-107:3.15 OP17-109:4 OP17-112:4 OP17-113:3.76 OP17-114:3.85 PRB02-010:0.29 ST34-002:2.68 ST34-003:1.59"],"OP08-098":[26,"EB01-059:0.81 EB03-053:2.85 EB04-058:3.96 OP05-106:0.69 OP05-114:0.46 OP05-117:3.54 OP08-099:2 OP08-109:3.65 OP08-110:3.15 OP08-115:2.73 OP11-106:1.62 OP12-099:4 OP12-114:3.5 OP15-101:4 OP15-108:3.62 OP15-111:0.42 OP15-113:0.65 OP15-114:4 OP15-119:1.62 OP17-114:1"],"OP09-001":[8,"EB04-007:0.25 OP06-007:1.88 OP09-002:3.38 OP09-004:3.25 OP09-006:0.5 OP09-009:3.38 OP09-010:0.5 OP09-011:4 OP09-012:0.5 OP09-013:0.5 OP09-014:0.88 OP09-015:3.5 OP09-016:0.5 OP09-020:2.75 OP10-019:0.25 OP12-008:2.63 OP13-007:2.13 OP16-012:3.5 OP16-018:1.75 P-097:0.25 PRB02-001:1 PRB02-002:2.38 PRB02-003:2.5 ST23-001:2.63 ST23-002:3.75 ST23-003:0.25 ST23-004:0.5 ST23-005:0.5"],"OP09-022":[16,"EB01-015:1.75 EB03-037:0.81 OP05-077:0.25 OP06-118:1.63 OP07-064:0.38 OP07-076:0.25 OP09-023:0.75 OP09-026:2.25 OP09-027:3.88 OP09-031:3.94 OP09-032:0.5 OP09-033:0.5 OP09-035:2.44 OP09-036:0.31 OP09-037:4 OP09-041:0.38 OP09-069:0.5 OP09-077:0.44 OP10-024:3.06 OP10-025:3.69 OP10-029:3.25 OP10-033:2.63 OP10-037:3.75 OP12-037:0.25 OP13-028:0.38 OP13-030:0.31 OP13-040:0.44 OP14-023:0.5 OP15-069:0.25 OP16-030:0.25 P-078:1.13 ST16-004:0.63 ST18-001:2.69"],"OP09-042":[2,"OP06-058:1 OP07-045:2 OP07-046:2 OP07-047:1 OP07-051:1 OP07-057:1 OP08-047:2 OP09-045:2 OP09-046:2 OP09-048:2 OP09-051:2 OP09-056:2 OP09-057:2 OP14-042:2 OP14-049:2 OP15-052:1 OP16-054:2 OP16-055:2 OP16-056:1 P-082:1 PRB02-009:2 ST17-002:2 ST17-003:2 ST17-004:1 ST25-001:2 ST25-002:2 ST25-003:2 ST25-004:2 ST25-005:2"],"OP09-061":[18,"EB02-061:2.06 OP03-072:0.56 OP05-074:0.44 OP05-076:2.17 OP05-077:0.44 OP07-064:0.89 OP08-067:0.67 OP08-074:1.89 OP09-065:2.39 OP09-069:3.11 OP09-072:2.83 OP09-076:3.44 OP09-077:1.33 OP09-078:3.78 OP09-119:1.17 OP11-067:0.89 OP15-078:0.78 OP15-085:0.5 OP15-086:0.28 OP15-088:0.56 OP17-074:0.44 OP17-087:0.56 OP17-095:0.56 OP17-119:0.28 PRB02-012:0.56 ST18-001:2.89 ST18-002:0.39 ST26-002:0.83 ST26-003:3.89 ST26-005:3.61"],"OP09-062":[86,"EB04-058:1.83 OP08-076:0.33 OP09-078:3.58 OP09-106:0.38 OP12-112:2.2 OP16-119:3.24 OP17-074:3.64 OP17-102:3.78 OP17-106:3.65 OP17-107:3.76 OP17-109:3.86 OP17-110:1.97 OP17-111:0.71 OP17-112:3.81 OP17-113:3.52 OP17-114:3.79 ST34-003:2.55 ST36-005:0.27"],"OP09-081":[99,"EB01-048:0.89 EB01-051:2.1 OP05-096:0.38 OP07-096:1.3 OP09-083:3.43 OP09-084:0.37 OP09-086:3.68 OP09-088:1.71 OP09-089:3.99 OP09-090:3.3 OP09-091:1.07 OP09-093:3.51 OP09-095:3.41 OP09-096:3.55 OP09-097:0.56 OP09-098:2.24 OP09-099:3.86 OP10-082:3.63 OP10-086:1.64 OP10-098:0.76 OP11-083:1.78 PRB02-015:0.3 ST27-003:0.85 ST27-005:0.96"],"OP10-001":[6,"EB02-017:0.67 EB03-018:1 EB04-002:0.67 EB04-003:2.17 EB04-007:0.5 OP01-016:0.67 OP01-055:1.33 OP06-033:3.33 OP06-035:1 OP06-038:2.33 OP08-036:2 OP09-009:0.33 OP09-032:0.33 OP10-004:0.67 OP10-005:0.67 OP10-011:0.67 OP10-019:0.33 OP10-030:3.33 OP10-032:0.67 OP11-004:0.5 OP11-017:0.5 OP12-015:0.67 OP12-023:3.33 OP12-027:0.33 OP12-030:0.5 OP12-034:2.67 OP12-037:2 OP12-118:0.5 OP13-031:0.67 OP17-022:3.17 OP17-029:1.67 PRB02-001:0.5 ST21-014:0.67 ST21-017:0.67 ST31-001:0.67 ST32-001:1.67 ST32-002:2.5 ST32-003:1.5 ST32-005:2.5"],"OP10-042":[1,"OP04-077:4 OP04-083:4 OP04-096:4 OP10-043:2 OP10-046:4 OP10-049:2 OP10-057:4 OP10-058:4 OP10-083:2 OP10-090:3 OP10-095:4 OP10-118:3 OP15-040:4 OP15-047:4 OP15-052:2"],"OP10-099":[9,"EB04-058:2 EB04-059:0.56 OP06-104:1.33 OP06-115:0.44 OP07-119:0.44 OP10-101:0.44 OP10-103:0.67 OP10-106:0.33 OP10-108:0.89 OP10-109:3.11 OP10-111:3.11 OP10-112:3.56 OP10-114:2.22 OP10-119:2.11 OP11-101:1.22 OP11-106:1.78 OP11-114:0.78 OP12-113:0.44 OP12-119:1.56 OP13-113:0.44 OP13-116:1.33 OP14-102:0.89 OP14-110:0.89 OP14-111:0.89 OP15-105:0.78 OP15-119:0.33 OP16-119:1.89 OP17-109:0.44 OP17-114:0.56 P-085:0.44 P-088:2.89 ST36-001:1.44 ST36-002:2.44 ST36-003:2.44 ST36-004:1.67 ST36-005:2.22"],"OP11-001":[7,"EB03-008:4 EB03-041:3.43 EB04-003:1.43 EB04-044:4 EB04-047:4 OP11-004:4 OP11-008:4 OP11-013:0.29 OP11-018:0.71 OP11-082:3.57 OP11-092:3.43 OP11-096:3.57 OP11-097:1.57 OP11-099:4 OP13-007:3 OP13-020:0.29 OP13-090:0.57 OP14-096:0.57 PRB02-001:3.14"],"OP11-021":[3,"EB04-015:2.67 EB04-018:2.33 EB04-020:2.67 OP06-025:4 OP06-033:2 OP06-035:2 OP07-026:1 OP10-030:1.33 OP10-032:1.33 OP11-024:1.33 OP11-029:0.67 OP11-030:3.67 OP11-031:4 OP11-032:1.33 OP11-033:1.33 OP11-035:1.67 OP11-038:1.33 OP11-039:4 OP13-040:0.67 OP14-031:1.33 OP15-023:1.33 OP15-024:0.67 OP15-032:2.67 OP17-022:2 OP17-029:2.67"],"OP11-022":[12,"EB01-056:0.5 EB03-052:3.58 EB03-053:0.33 EB04-011:3.67 EB04-016:3.67 EB04-018:3.33 OP06-025:0.25 OP06-035:3.17 OP08-036:0.75 OP10-030:0.33 OP11-026:0.33 OP11-030:4 OP11-031:0.33 OP11-036:3.92 OP11-037:1.67 OP11-100:3.5 OP11-103:0.33 OP11-106:0.33 OP11-107:4 OP11-108:0.33 OP11-112:0.33 OP11-115:3 OP12-102:3.67 OP13-040:1.67 OP14-036:0.58 OP14-038:0.25 ST16-004:1.5"],"OP11-040":[8,"EB01-061:4 EB03-034:0.63 OP05-067:0.25 OP05-070:1.25 OP06-058:0.5 OP06-119:4 OP07-051:0.5 OP07-064:1.13 OP08-076:2.75 OP09-078:4 OP10-079:0.25 OP11-054:3.88 OP11-080:4 OP13-043:2.38 OP14-077:0.38 OP15-078:0.38 OP16-056:3.13 OP17-046:1.88 OP17-049:0.38 OP17-054:1.13 OP17-065:1.5 OP17-074:3.5 P-107:4 ST18-001:4"],"OP11-041":[175,"EB03-053:3.97 EB03-055:3.89 EB03-060:0.7 EB04-058:3.94 OP06-058:0.78 OP06-104:2.46 OP06-106:0.8 OP11-106:3.59 OP12-112:2.02 OP13-042:3.06 OP14-102:3.66 OP14-104:3.65 OP14-108:0.61 OP14-110:3.42 OP14-111:3.5 OP15-047:0.5 OP15-113:1.18 OP15-114:0.33 OP15-119:0.7 OP16-056:2.12 OP16-119:0.79 P-096:1.87 PRB02-008:1.1"],"OP11-062":[11,"EB02-035:0.73 EB03-032:0.36 EB03-035:1.82 OP07-076:0.73 OP07-077:2.64 OP08-062:2.18 OP08-067:1.91 OP08-069:0.55 OP08-077:1.36 OP11-065:0.73 OP11-066:0.55 OP11-067:3.64 OP11-068:3.18 OP11-070:3.64 OP11-071:1.36 OP11-074:0.91 OP11-079:1.27 OP11-081:0.36 OP13-076:0.73 OP15-078:2.55 OP17-062:0.27 P-090:0.55 PRB02-010:3.64 ST18-001:1.91 ST34-001:3.09 ST34-002:2.36 ST34-003:3.09 ST34-004:2.91 ST34-005:0.73"],"OP12-001":[1,"OP12-003:4 OP12-004:4 OP12-006:4 OP12-007:4 OP12-009:4 OP12-012:2 OP12-013:4 OP12-014:4 OP12-015:4 OP12-016:4 OP12-017:4 OP12-018:4 OP12-019:4"],"OP12-020":[87,"EB01-012:3.29 EB01-015:2.3 OP05-037:0.59 OP06-118:0.29 OP08-036:0.29 OP10-032:1.59 OP12-023:1.08 OP12-026:3.13 OP12-027:3.64 OP12-028:3.9 OP12-029:1.32 OP12-030:0.4 OP12-031:4 OP12-034:3.93 OP12-037:1.68 OP12-039:3.9 OP13-028:0.34 OP13-031:3.47 OP13-037:3.46 PRB02-006:0.52 ST12-003:3.47 ST24-005:0.61"],"OP12-040":[5,"EB04-022:2.2 EB04-026:2 EB04-028:2.4 OP06-043:1.4 OP06-044:0.6 OP06-050:3.2 OP06-051:1.6 OP06-058:1 OP08-047:0.8 OP09-051:0.4 OP12-043:2.8 OP12-044:2.4 OP12-045:0.8 OP12-046:1.4 OP12-047:3.6 OP12-050:0.8 OP12-051:1.8 OP12-052:0.8 OP12-053:0.8 OP12-056:1.6 OP12-057:3.6 OP16-056:1.4 ST33-001:3.2 ST33-002:0.8 ST33-003:2.8 ST33-004:2.8 ST33-005:2.8"],"OP12-041":[2,"EB01-039:0.5 EB03-031:1.5 EB04-029:2 EB04-041:2 OP06-058:1 OP07-064:2 OP09-077:1 OP09-078:4 OP11-061:1 OP12-059:3 OP12-060:4 OP12-062:2 OP12-063:2 OP12-065:1 OP12-066:2 OP12-070:4 OP12-071:2 OP12-072:1 OP12-074:2 OP12-078:2 OP12-079:4 OP13-043:1.5 OP13-076:0.5 OP15-078:1.5 OP16-056:1 OP17-049:1.5"],"OP12-081":[11,"EB03-042:1.27 EB03-056:0.36 EB04-058:1.73 OP06-115:0.45 OP07-085:0.27 OP08-106:0.36 OP09-097:0.27 OP09-100:0.27 OP09-108:1.45 OP12-083:0.36 OP12-084:0.27 OP12-085:1.18 OP12-086:2.55 OP12-087:2.09 OP12-089:2.55 OP12-090:0.64 OP12-093:2.09 OP12-094:2.45 OP12-095:0.36 OP12-097:1.82 OP12-098:1.91 OP12-100:0.55 OP12-112:1.55 OP12-119:3.09 OP14-108:0.64 OP15-088:0.64 OP16-119:0.64 OP17-080:1.36 OP17-081:1.36 OP17-082:1 OP17-083:0.55 OP17-084:1.18 OP17-086:0.91 OP17-087:1 OP17-089:1.45 OP17-090:0.27 OP17-091:0.27 OP17-093:1.45 OP17-095:1.36 OP17-098:0.55 OP17-102:0.27 OP17-106:0.36 OP17-109:0.36 OP17-112:0.36 OP17-114:0.36 OP17-119:1.64 PRB02-014:0.55 ST35-003:0.27"],"OP13-001":[51,"EB02-017:2.1 EB04-002:2.53 EB04-007:3.61 OP01-016:2.35 OP05-038:3.12 OP08-036:3.02 OP12-037:0.55 OP13-016:2.1 OP13-027:4 OP13-028:0.51 OP13-037:0.69 OP13-040:1.39 OP13-118:3.37 OP14-022:3.92 OP14-031:3.86 OP15-032:2.25 OP15-035:3.24 OP17-023:0.33 ST21-003:1.86 ST31-004:1.29 ST31-005:2.04"],"OP13-002":[45,"EB04-007:3.29 EB04-008:0.58 OP01-016:0.27 OP04-016:0.36 OP07-051:1.98 OP08-040:1.24 OP08-043:0.51 OP08-044:0.64 OP08-047:3.62 OP09-118:1.36 OP10-045:0.58 OP11-060:0.27 OP12-015:0.27 OP13-016:3.73 OP13-042:3.78 OP13-043:3.73 OP13-054:3.71 OP16-004:0.4 OP16-021:0.69 OP16-056:0.62 PRB02-008:3.71 ST15-004:0.67 ST21-014:0.27 ST22-002:3.42 ST22-015:3.67 ST23-001:2.69 ST31-001:0.27 ST31-005:0.27"],"OP13-003":[7,"EB01-061:2.29 EB04-007:1 OP06-007:0.86 OP09-004:3.14 OP09-009:0.43 OP09-014:0.57 OP09-118:1.29 OP10-018:0.57 OP12-010:0.86 OP13-007:0.86 OP13-060:0.29 OP13-061:1.14 OP13-062:0.57 OP13-063:1.57 OP13-064:0.57 OP13-065:3.86 OP13-066:2.71 OP13-067:3.14 OP13-068:4 OP13-071:1.57 OP13-072:4 OP13-075:4 OP13-076:2.86 P-107:3.14 ST18-001:1.71 ST21-003:0.57 ST23-001:2.29"],"OP13-004":[155,"EB04-007:0.74 OP01-016:1.31 OP04-016:1.77 OP04-083:0.35 OP05-015:0.47 OP05-021:0.46 OP07-015:0.27 OP07-085:0.39 OP12-086:0.38 OP12-089:0.33 OP12-093:0.44 OP12-094:0.39 OP12-098:0.45 OP13-007:0.37 OP13-016:0.58 OP13-120:0.55 OP14-096:0.66 OP15-088:2.95 OP17-080:3.41 OP17-081:0.86 OP17-082:2.99 OP17-083:1.47 OP17-084:2.1 OP17-086:2.25 OP17-087:3.05 OP17-089:3.37 OP17-093:3.01 OP17-095:3.4 OP17-096:1.01 OP17-098:0.54 OP17-119:3.1 PRB02-014:0.43 ST01-011:2.66"],"OP13-079":[10,"OP05-086:1 OP05-097:0.8 OP11-097:0.6 OP13-080:4 OP13-082:4 OP13-083:4 OP13-084:4 OP13-086:4 OP13-087:1.1 OP13-089:4 OP13-091:4 OP13-092:3.4 OP13-095:0.4 OP13-096:4 OP13-097:2.1 OP13-098:4 OP13-099:1 OP14-096:2 PRB02-014:1.4"],"OP13-100":[10,"EB03-053:1 EB04-058:1.2 OP06-101:1.7 OP06-104:2.2 OP06-115:1.8 OP07-099:2.6 OP07-107:0.9 OP07-113:0.6 OP08-105:0.3 OP08-106:0.6 OP09-107:2.2 OP10-109:0.6 OP10-112:0.6 OP12-119:0.4 OP13-108:3.4 OP13-110:0.4 OP13-113:3 OP14-102:1.1 OP14-104:0.7 OP14-110:1 OP14-111:1.1 OP16-104:0.4 OP16-108:0.4 OP16-119:1.6 OP17-102:1.8 OP17-106:1.9 OP17-107:2.3 OP17-109:2.4 OP17-110:0.7 OP17-111:0.3 OP17-112:1.9 OP17-113:1.6 OP17-114:2.3 PRB02-016:1.8 PRB02-017:0.9 ST29-004:0.4"],"OP14-001":[1,"OP14-002:4 OP14-003:4 OP14-004:2 OP14-005:4 OP14-007:4 OP14-009:4 OP14-010:4 OP14-011:4 OP14-013:4 OP14-014:4 OP14-015:4 OP14-016:4 OP14-018:4"],"OP14-020":[195,"OP01-055:2.99 OP06-033:2.83 OP06-035:0.29 OP06-038:3.1 OP07-022:3.52 OP07-026:0.82 OP08-036:1.77 OP12-023:3.24 OP12-034:4 OP12-037:0.65 OP13-031:3.16 OP13-040:0.72 OP14-023:0.84 OP14-027:0.34 OP14-033:1.15 OP14-037:0.38 OP14-038:0.76 OP14-039:2.25 OP17-022:2.91 OP17-031:2.78 ST24-004:1.1 ST32-001:3.76 ST32-002:3.78 ST32-003:0.98 ST32-005:0.28"],"OP14-040":[11,"EB01-023:2.18 OP06-058:0.73 OP07-040:2.64 OP07-045:3.27 OP07-046:2.91 OP07-047:1 OP07-051:2.09 OP07-057:1.82 OP08-047:3.18 OP13-043:0.27 OP14-042:3.82 OP14-043:0.36 OP14-045:0.36 OP14-046:3.09 OP14-047:0.55 OP14-049:4 OP14-050:3.27 OP14-051:0.36 OP14-054:1.27 OP14-055:0.36 OP14-056:1.55 OP14-058:0.36 OP16-054:0.36 OP16-055:0.36 OP16-056:0.55 OP17-046:0.55 OP17-049:0.73 P-048:0.36 ST17-002:3.27 ST17-003:2.09 ST17-004:0.91"],"OP14-041":[54,"EB03-053:0.39 EB04-058:3.87 OP06-058:0.72 OP06-106:1.43 OP06-115:1.19 OP07-056:0.31 OP07-057:1.04 OP08-050:0.26 OP11-054:1.5 OP12-119:0.94 OP14-103:0.87 OP14-104:3.5 OP14-105:4 OP14-107:4 OP14-108:1.65 OP14-112:3.76 OP14-113:0.28 OP14-114:3.98 OP14-118:1.09 OP15-113:3.54 OP16-113:3.61 OP16-119:1.33 OP17-046:0.28 OP17-107:0.33 OP17-109:2.89 OP17-114:0.41 ST17-004:1.41"],"OP14-060":[26,"EB04-038:0.31 OP07-076:3.69 OP10-062:0.58 OP10-065:3.85 OP10-071:4 OP10-072:2.5 OP10-078:1.23 OP11-067:0.27 OP12-076:0.96 OP13-076:3.5 OP14-061:3.92 OP14-063:3.38 OP14-067:4 OP14-068:2.81 OP14-069:2.96 OP14-071:0.54 OP14-072:2.81 OP14-074:3.92 OP14-078:1.08 ST18-001:2.54"],"OP14-079":[33,"OP05-094:2.61 OP14-083:3.97 OP14-084:4 OP14-085:2.42 OP14-086:1.91 OP14-087:4 OP14-088:3.85 OP14-090:3.88 OP14-091:4 OP14-093:2.15 OP14-094:2.7 OP14-096:4 OP14-097:0.36 OP14-099:3.88 OP14-120:4 OP15-092:1.82"],"OP14-080":[19,"EB04-058:0.89 OP06-090:1.47 OP06-091:3.79 OP06-104:1.11 OP13-113:2.63 OP14-089:1.16 OP14-096:0.53 OP14-097:2.11 OP14-100:3.79 OP14-102:4 OP14-104:4 OP14-109:1.11 OP14-110:3.89 OP14-111:4 OP14-112:3.84 OP14-117:0.79 OP15-084:2.11 OP15-113:1.37 OP17-109:1.26 PRB02-013:4"],"OP15-001":[12,"OP08-036:0.92 OP11-028:0.75 OP13-040:1.83 OP14-027:0.33 OP15-003:2.67 OP15-007:4 OP15-008:4 OP15-009:3.33 OP15-010:0.25 OP15-011:2.08 OP15-012:0.25 OP15-015:1.25 OP15-017:3.92 OP15-023:4 OP15-025:3.92 OP15-026:4 OP15-027:1 OP15-028:3.42 OP15-037:3.5 OP15-038:2.17 OP17-023:1.25"],"OP15-002":[60,"OP05-015:1.23 OP05-019:2.15 OP08-047:0.33 OP09-118:0.93 OP10-045:3.17 OP10-049:0.7 OP10-059:1.5 OP10-060:3.92 OP13-042:0.33 OP15-014:1.25 OP15-019:0.6 OP15-020:3.87 OP15-021:3.52 OP15-040:4 OP15-046:4 OP15-052:3.85 OP15-053:3.93 OP15-054:3.77 OP15-055:1.45 OP15-056:1.9 OP15-057:2.05"],"OP15-022":[6,"EB02-017:3.83 EB04-049:0.33 OP08-036:0.83 OP11-097:1 OP12-037:1.17 OP13-031:0.5 OP13-040:0.33 OP13-089:0.67 OP13-118:1.17 OP14-031:1.17 OP15-024:0.67 OP15-032:2.5 OP15-035:1.33 OP15-081:4 OP15-082:0.67 OP15-083:2.67 OP15-085:4 OP15-086:4 OP15-087:0.67 OP15-088:3.83 OP15-089:0.67 OP15-092:4 OP15-094:1.83 OP15-095:0.83 OP15-096:2 OP16-086:0.5 OP16-092:0.5 OP17-022:0.33 OP17-082:0.67 OP17-084:0.67 PRB02-006:1.17 ST30-012:0.5 ST32-001:0.67"],"OP15-039":[4,"OP06-056:0.25 OP06-058:0.5 OP07-051:1.5 OP08-046:0.5 OP08-047:0.5 OP10-045:4 OP10-046:1 OP10-049:0.5 OP10-054:3 OP10-059:2.75 OP10-060:1.5 OP14-049:2 OP15-040:4 OP15-041:1 OP15-042:4 OP15-046:1 OP15-047:4 OP15-051:3.5 OP15-052:4 OP15-053:4 OP16-054:1 OP16-056:1 OP17-046:1 OP17-049:1 OP17-054:0.75 OP17-118:1 PRB02-009:0.75"],"OP15-058":[297,"OP05-077:2.65 OP07-064:0.78 OP09-072:1.51 OP09-077:0.44 OP10-067:1.78 OP12-063:2.27 OP12-071:3.65 OP13-076:1.25 OP15-060:0.5 OP15-061:4 OP15-066:1.74 OP15-067:3.63 OP15-071:2.07 OP15-074:2.48 OP15-075:3.97 OP15-076:4 OP15-077:3.95 OP15-078:4 OP15-118:3.55 ST10-010:1.22"],"OP15-098":[19,"EB02-052:2 EB03-056:1.84 EB04-058:3.79 EB04-061:0.26 OP05-102:1.42 OP05-106:3.79 OP05-117:0.26 OP11-106:3.53 OP12-099:2.74 OP15-102:1.11 OP15-107:0.53 OP15-108:4 OP15-109:4 OP15-113:4 OP15-114:4 OP15-116:4 OP15-119:4 ST29-004:0.26 ST29-009:3.11"],"OP16-001":[58,"EB01-002:0.34 EB04-007:0.33 OP04-016:0.55 OP08-118:0.26 OP10-005:0.83 OP13-007:0.34 OP13-016:3.95 OP14-018:0.26 OP16-003:3.59 OP16-004:3.98 OP16-005:1.38 OP16-010:0.93 OP16-011:3.1 OP16-014:3.84 OP16-015:3.76 OP16-017:3.9 OP16-020:1.09 OP16-021:3.93 OP16-118:2.55 OP17-005:3.05 OP17-006:2.95 OP17-016:0.47 OP17-017:1.53 ST23-001:1.4"],"OP16-022":[131,"OP02-068:0.79 OP06-058:1.4 OP08-036:0.36 OP11-061:0.36 OP12-037:0.73 OP13-040:0.64 OP15-032:1.18 OP16-026:2.27 OP16-027:0.58 OP16-032:2.71 OP16-034:4 OP16-037:0.26 OP16-038:2.2 OP16-042:8.41 OP16-045:3.63 OP16-048:4 OP16-054:4 OP16-055:4 OP16-056:3.99 ST12-010:1.16 ST30-012:0.29 ST30-014:2.68"],"OP16-041":[5,"EB01-028:0.8 EB03-021:0.4 OP06-058:1.4 OP07-056:2.4 OP08-047:1.2 OP09-046:0.4 OP09-051:0.4 OP09-056:0.6 OP13-042:0.8 OP15-047:0.4 OP16-042:18.6 OP16-044:1.8 OP16-045:3.8 OP16-048:3.6 OP16-050:1.4 OP16-054:4 OP16-055:4 OP16-056:4"],"OP16-060":[8,"EB01-061:0.38 EB03-036:0.75 OP07-064:1 OP07-076:1.25 OP09-077:1.63 OP12-071:0.25 OP15-078:2.88 OP16-062:1 OP16-063:4 OP16-064:4 OP16-065:4 OP16-066:4 OP16-067:3.63 OP16-070:2.88 OP16-073:4 OP16-075:2.63 OP16-076:3.5 OP16-077:4 OP16-078:2.63 OP17-065:0.25 OP17-075:0.25 ST10-010:0.38 ST18-001:0.5"],"OP16-079":[36,"OP05-091:0.39 OP06-093:0.28 OP11-097:0.33 OP13-093:0.44 OP14-096:1.92 OP15-092:0.33 OP16-081:2.22 OP16-082:3.94 OP16-084:4 OP16-085:3.92 OP16-086:0.42 OP16-087:3.97 OP16-088:1.89 OP16-091:3.89 OP16-092:3.64 OP16-094:0.67 OP16-095:0.86 OP16-096:3.86 OP16-097:3.94 OP16-098:4 OP16-099:3.58 OP17-082:0.78"],"OP16-080":[76,"EB04-058:3.71 EB04-059:0.57 OP09-086:2.93 OP09-093:3.62 OP09-095:1.42 OP09-096:2.99 OP09-099:3.63 OP12-112:1.59 OP13-113:0.25 OP14-108:0.58 OP14-112:0.66 OP15-114:0.7 OP16-102:0.75 OP16-103:1.96 OP16-104:3.99 OP16-106:2.14 OP16-108:3.97 OP16-109:3.24 OP16-110:3.87 OP16-115:0.29 OP16-116:2.22 OP16-119:3.92"],"OP17-001":[14,"EB04-007:0.57 OP06-017:0.43 OP09-015:0.29 OP09-118:1.64 OP11-018:0.43 OP12-002:1.57 OP13-007:0.86 OP16-002:0.29 OP16-003:0.29 OP16-021:4 OP16-118:3.5 OP17-002:2.29 OP17-003:4 OP17-004:2 OP17-005:4 OP17-006:1 OP17-007:0.29 OP17-008:4 OP17-009:1.86 OP17-015:1 OP17-016:0.29 OP17-017:3.64 OP17-019:4 PRB02-003:1.29 ST23-001:1.14 ST30-004:2.29 ST30-005:1.86"],"OP17-020":[26,"OP01-055:0.69 OP06-033:0.96 OP06-038:0.42 OP07-022:0.46 OP08-036:0.54 OP10-030:0.62 OP12-023:1 OP12-034:1.92 OP12-037:0.46 OP13-031:2.38 OP13-040:0.27 OP15-035:0.54 OP17-021:2.77 OP17-022:3.69 OP17-026:1 OP17-027:3.73 OP17-028:1 OP17-029:2.73 OP17-031:3.96 OP17-032:3.46 OP17-033:3.08 OP17-036:2.42 OP17-037:2.23 OP17-038:0.85 ST16-004:1.92 ST32-001:1.92 ST32-002:3"],"OP17-039":[158,"EB02-030:0.6 OP08-051:2.96 OP17-040:3.99 OP17-041:1.17 OP17-042:1.58 OP17-043:0.39 OP17-044:2.72 OP17-045:3.92 OP17-046:3.98 OP17-048:3.99 OP17-049:3.96 OP17-050:2.33 OP17-052:2.06 OP17-054:3.96 OP17-055:4 OP17-056:3.99 OP17-118:4"],"OP17-058":[116,"EB04-030:1.26 EB04-031:4 EB04-032:4 EB04-040:0.59 OP07-076:2.66 OP07-077:3.91 OP08-069:0.29 OP08-074:1.71 OP09-077:1.2 OP13-076:0.68 OP15-078:3.6 OP17-061:2.74 OP17-062:3.72 OP17-063:2.19 OP17-065:1.66 OP17-073:3.91 OP17-074:3.96 OP17-075:1.19 OP17-076:0.8 OP17-077:0.44 ST10-010:0.29 ST34-004:3.13"],"OP17-079":[97,"OP07-096:0.3 OP14-096:0.46 OP15-088:3.47 OP17-080:3.99 OP17-081:3.99 OP17-082:3.4 OP17-084:1.73 OP17-086:3.18 OP17-087:3.55 OP17-089:3.55 OP17-090:0.75 OP17-091:1.65 OP17-093:3.73 OP17-094:3.86 OP17-095:3.86 OP17-096:0.31 OP17-098:1.19 OP17-119:3.99 ST14-017:2"],"OP17-099":[15,"EB04-058:1.6 EB04-059:1 OP11-106:2.27 OP13-113:0.27 OP13-117:0.33 OP14-104:2.33 OP15-113:1.73 OP16-119:2.13 OP17-102:3.87 OP17-103:4 OP17-104:3.8 OP17-106:1.8 OP17-107:4 OP17-108:0.4 OP17-109:4 OP17-110:1.73 OP17-111:0.27 OP17-112:4 OP17-113:4 OP17-114:3.93 OP17-115:0.4 OP17-117:0.8 ST20-003:0.27 ST36-001:0.27"],"ST10-002":[4,"EB01-061:3.25 EB04-007:3.75 OP01-016:4 OP04-016:0.75 OP05-067:3.75 OP05-119:2 OP09-004:0.75 OP09-078:4 OP12-018:0.75 OP13-015:2.5 OP13-016:4 OP15-078:2.75 OP17-063:0.75 OP17-067:1.25 OP17-074:4 P-107:3 ST18-001:3 ST21-003:1 ST31-004:0.75 ST31-005:4"],"ST12-001":[2,"EB02-017:2 EB02-026:2 EB03-024:2 EB04-018:2 OP10-030:3 OP10-032:2 OP10-045:3 OP12-034:4 OP12-037:2 OP13-027:1.5 OP13-031:1.5 OP13-037:2 OP13-040:2 OP13-118:1 OP14-026:2 OP15-023:2 OP16-054:2 OP16-055:2 OP16-056:2 ST12-003:4 ST32-003:2 ST32-005:2 ST33-003:2"],"ST13-001":[3,"EB04-058:1.33 OP06-104:3.33 OP07-107:1.33 OP09-118:0.67 OP11-106:1.33 OP12-112:2.67 OP13-016:2.67 OP13-104:2.67 OP13-113:2 OP14-100:1.33 OP14-102:4 OP14-104:4 OP14-108:2.33 OP14-109:1.67 OP14-110:4 OP14-111:4 OP15-113:4 OP15-114:1 OP15-119:1.67 OP16-104:0.67 OP16-116:0.67 OP16-119:2.67"],"ST13-003":[8,"EB01-056:4 EB01-059:0.25 OP04-083:2.75 OP06-093:0.75 OP06-106:3.63 OP07-109:2.5 OP10-115:0.5 OP10-117:0.5 OP11-106:3 OP12-087:0.25 OP12-100:2.38 OP12-117:0.75 PRB02-018:2.5 ST13-007:3.88 ST13-010:4 ST13-011:2.63 ST13-012:0.38 ST13-013:4 ST13-014:3.75 ST13-015:3.63 ST13-018:0.38 ST13-019:3 ST29-016:0.25"],"ST21-001":[13,"EB04-002:2.85 EB04-008:0.62 OP01-016:3.62 OP04-016:2.46 OP06-018:0.38 OP10-005:1.54 OP10-011:1 OP10-020:0.31 OP11-003:1 OP11-009:1 OP11-012:1.62 OP12-015:2.08 OP12-018:0.38 OP13-021:0.77 OP14-015:0.46 OP14-019:0.85 OP15-012:0.69 P-101:0.85 ST21-003:2.62 ST21-009:0.69 ST21-010:0.31 ST21-014:3 ST21-015:2.23 ST21-017:1.31 ST23-004:0.31 ST30-016:0.62 ST31-001:3.85 ST31-002:2.46 ST31-003:1.23 ST31-004:3.85 ST31-005:3.77"],"ST22-001":[3,"OP08-040:2.67 OP08-043:0.67 OP08-044:2.67 OP08-047:1.33 OP08-052:2.67 OP08-053:1.33 OP10-055:1 OP11-118:1.33 OP12-058:1.67 PRB02-008:1.33 ST22-002:3.33 ST22-003:3.33 ST22-004:1.33 ST22-005:1.33 ST22-006:1.33 ST22-007:2.67 ST22-008:1.33 ST22-009:0.67 ST22-010:4 ST22-011:4 ST22-012:2.67 ST22-013:1.33 ST22-014:1.33 ST22-015:0.67 ST22-016:3.33 ST22-017:0.67"],"ST29-001":[20,"EB03-053:0.9 EB03-055:0.75 EB04-058:0.35 OP06-104:3.8 OP06-115:2 OP07-099:3.4 OP08-106:1.95 OP09-107:2.9 OP10-112:2.85 OP12-119:2.45 OP13-108:3.4 OP13-113:2.8 OP13-114:0.25 OP14-100:0.3 OP14-102:0.4 OP14-104:0.4 OP14-110:0.5 OP14-111:0.4 OP15-119:0.25 ST13-017:1.95 ST29-004:3.45 ST29-005:3.9 ST29-009:3.65 ST29-016:2.5"],"ST30-001":[120,"EB02-017:3.69 EB04-002:3.63 OP01-016:3.7 OP01-055:0.41 OP04-016:3.44 OP06-017:0.41 OP06-018:0.59 OP12-015:3.73 OP12-018:1.17 OP12-037:2.06 OP13-040:2.31 OP14-019:1.52 OP15-032:0.38 OP17-017:2.66 OP17-023:0.31 ST21-014:3.83 ST21-017:0.57 ST30-002:0.25 ST30-004:0.25 ST30-005:0.26 ST30-012:3.64 ST30-016:0.25 ST31-001:3.73 ST31-005:3.71"]};function turnStart(events,player){for(let index=events.length-1;index>=0;index-=1){const event=events[index];if(event.type==="turn-ended"&&event.nextPlayerId===player)return index+1}return 0}function historyFacts(observation){const events=observation.publicEvents;const opponent=observation.viewer==="p1"?"p2":"p1";const played=events.slice(turnStart(events,opponent)).filter(event=>event.type==="card-played"&&event.playerId===opponent).map(event=>event.cardNumber);const used=events.slice(turnStart(events,observation.viewer)).filter(event=>(event.type==="counter-used"||event.type==="event-activated")&&event.playerId===opponent).map(event=>event.cardNumber);const known=new Map;const removeNumber=number=>{for(const[id,held]of known)if(held===number){known.delete(id);return}};for(const event of events){if(event.type==="card-moved"&&event.playerId===opponent){if(event.to==="hand"&&event.cardNumber&&event.cardInstanceId&&!event.identityVisibleTo)known.set(event.cardInstanceId,event.cardNumber);else if(event.from==="hand"&&event.cardInstanceId)known.delete(event.cardInstanceId)}else if(event.type==="card-played"&&event.playerId===opponent){known.delete(event.cardInstanceId)}else if((event.type==="counter-used"||event.type==="event-activated")&&event.playerId===opponent){removeNumber(event.cardNumber)}}return{played,used,known:[...known.values()]}}var COMPACT_VERSION=5;var ZONES=["my-leader","my-hand","my-board","my-stage","op-leader","op-board","op-stage","my-trash","op-trash","my-life-up","op-life-up","my-deck-top","op-deck-top","op-played","op-used","op-hand-known","my-unseen","op-unseen","op-attacker","my-ko","my-removed","op-activated","op-don-target","op-blocker","my-played","my-activated","my-attacker","my-don-returned"];var V1_ZONES=16;var KEYWORDS=["Blocker","Rush","Rush: Character","Banish","Unblockable","Double Attack"];var PHASES=["mulligan","main","block","counter","trigger"];var TYPES=["mulligan-decision","play-card","attach-don","attack","block","counter","activate-counter-event","activate-main","resolve-effect","resolve-trigger","pass"];var KINDS=["none","skip","confirm","cost","position","resource","don-return-count","mode","card","don-return","order","other"];var CARD_ZONES=["hand","board","trash","deck","life","stage","leader","don"];var V4_CARD_NUMBERS=14+KEYWORDS.length;var CARD_CONDITIONAL_COLUMN=V4_CARD_NUMBERS;var CARD_COUNTER_COLUMN=CARD_CONDITIONAL_COLUMN+KEYWORDS.length;var CARD_NUMBERS=CARD_COUNTER_COLUMN+1;var STAGE_COLUMNS=[3,9];var OPTION_ROW_COLUMN=22;var POOLED_ZONES=[16,17];var V3_OPTION_NUMBERS=28;var OPTION_COST_COLUMN=V3_OPTION_NUMBERS;var OPTION_PAYOFF_COLUMN=OPTION_COST_COLUMN+3;var OPTION_NUMBERS=OPTION_PAYOFF_COLUMN+PAYOFF_KINDS.length;var V2_GLOBAL_NUMBERS=26+PHASES.length;var V4_GLOBAL_NUMBERS=30+PHASES.length;var GLOBAL_NUMBERS=V4_GLOBAL_NUMBERS+10;var priors=new Map;function leaderPrior(leader){let prior=priors.get(leader);if(!prior){prior=new Map;const known=LEADER_PRIORS[leader];if(known){for(const pair of known[1].split(" ")){const[number,copies]=pair.split(":");if(number&&copies)prior.set(number,Number(copies))}}else{const lists=Object.values(deckManifests).filter(manifest=>manifest.leader===leader);for(const list of lists)for(const[number,count]of Object.entries(list.cards))prior.set(number,(prior.get(number)??0)+count/lists.length)}priors.set(leader,prior)}return prior}var k=n=>(n??0)/1e3;function modifierTotal(entry){return[...entry.staticPowerModifiers,...entry.powerModifiers].reduce((sum,m)=>sum+m.amount,0)}function inPlay(entry,instanceId,turn,battle,mine){const board="summoningSick"in entry?entry:void 0;const lasting=entry.powerModifiers.some(m=>m.expiresAtTurn>turn);return[k(entry.currentPower),entry.attachedDon,k(modifierTotal(entry)),entry.rested?1:0,board?.summoningSick?1:0,board&&board.cannotAttackUntilTurn!==void 0&&board.cannotAttackUntilTurn>=turn?1:0,board?.negated?1:0,board?board.currentCost-board.cost:0,1,"dormantAbility"in entry?1:0,battle?.attackerInstanceId===instanceId?1:0,battle?.targetInstanceId===instanceId?1:0,mine?1:0,lasting?1:0,...KEYWORDS.map(keyword=>entry.effectiveKeywords.includes(keyword)?1:0),...KEYWORDS.map(keyword=>entry.conditionalKeywords?.includes(keyword)?1:0),0]}function stageRow(stage,mine){const row=plain(1,mine);row[STAGE_COLUMNS[0]]=stage.rested?1:0;row[STAGE_COLUMNS[1]]="dormantAbility"in stage?1:0;return row}function plain(count,mine,costDelta=0){const row=new Array(CARD_NUMBERS).fill(0);row[7]=costDelta;row[8]=count;row[12]=mine?1:0;return row}function resources(side,hand){return[side.lifeCount,side.deckCount/10,hand,side.activeDon,side.restedDon,side.donDeckCount,side.trash.length/10]}function sideFlags(side){return[side.donHeldFromRefresh??0,side.extraTurns??0,side.onPlayNegated?1:0]}function timeLimit(observation){const limit=observation.timeLimit;if(!limit)return[0,0,0,0];return[1,limit.called?1:0,limit.called?(limit.turnsLeft??0)/TIME_LIMIT_ADDITIONAL_TURNS:0,limit.called?0:Math.max(0,limit.calledOnTurn-observation.turn)/10]}function leaderSpent(leader){return[k(leader.basePower??leader.power),leader.leaderReactionSpent?1:0]}function bucket(text,size){let hash=7;for(let index=0;index<text.length;index+=1)hash=hash*31+text.charCodeAt(index)>>>0;return hash%size}function unseen(observation,context,known){const{self,opponent}=observation;const out=[];const less=(list,seen,zone,mine,floor)=>{const left=new Map(list);for(const number of seen)if(number&&left.has(number))left.set(number,left.get(number)-1);for(const number of[...left.keys()].sort()){const copies=left.get(number);if(copies>=floor)out.push([number,{zone,copies,mine}])}};if(context.ownDeck)less(new Map(Object.entries(context.ownDeck)),[...self.hand.map(e=>e.cardNumber),...self.board.map(e=>e.cardNumber),self.stage?.cardNumber,...self.trash.map(e=>e.cardNumber),...self.life.filter(e=>e.faceUp).map(e=>e.cardNumber)],"my-unseen",true,1);less(leaderPrior(opponent.leader.cardNumber),[...opponent.board.map(e=>e.cardNumber),opponent.stage?.cardNumber,...opponent.trash.map(e=>e.cardNumber),...opponent.life.filter(e=>e.faceUp).map(e=>e.cardNumber),...known],"op-unseen",false,.25);return out}function lastTurn(observation){const viewer=observation.viewer;const opponent=viewer==="p1"?"p2":"p1";const events=observation.publicEvents;const numberOf=new Map([[`${viewer}-leader`,observation.self.leader.cardNumber],[`${opponent}-leader`,observation.opponent.leader.cardNumber]]);for(const event of events)if("cardInstanceId"in event&&event.cardInstanceId&&"cardNumber"in event&&event.cardNumber)numberOf.set(event.cardInstanceId,event.cardNumber);for(const entry of[...observation.self.board,...observation.opponent.board])numberOf.set(entry.instanceId,entry.cardNumber);const out=new Map;const tally=(zone,number,count=1,power=0,atLeader=0)=>{if(!number)return;const key=`${zone}|${number}`;const entry=out.get(key)??{count:0,power:0,atLeader:0};entry.count+=count;entry.power=Math.max(entry.power,power);entry.atLeader+=atLeader;out.set(key,entry)};for(const event of events.slice(turnStart(events,opponent))){switch(event.type){case"attack-declared":if(event.playerId===opponent)tally("op-attacker",numberOf.get(event.attackerInstanceId),1,event.power,event.targetInstanceId===`${viewer}-leader`?1:0);break;case"character-ko":if(event.playerId===viewer)tally("my-ko",event.cardNumber);break;case"card-moved":if(event.playerId===viewer&&event.from==="board"&&event.to!=="trash"&&event.cardNumber)tally("my-removed",event.cardNumber);break;case"ability-triggered":if(event.playerId===opponent&&!event.noEffect)tally("op-activated",event.sourceCardNumber);break;case"don-attached":if(event.playerId===opponent)tally("op-don-target",numberOf.get(event.targetInstanceId),event.count);break;case"attack-blocked":if(event.playerId===opponent)tally("op-blocker",numberOf.get(event.blockerInstanceId));break;default:break}}if(observation.turnPlayer!==viewer)return out;for(const event of events.slice(turnStart(events,viewer))){if(!("playerId"in event)||event.playerId!==viewer)continue;switch(event.type){case"card-played":case"stage-played":case"event-activated":tally("my-played",event.cardNumber);break;case"ability-triggered":case"ability-activated":tally("my-activated",event.sourceCardNumber);break;case"attack-declared":tally("my-attacker",numberOf.get(event.attackerInstanceId),1,event.power,event.targetInstanceId===`${opponent}-leader`?1:0);break;case"don-returned":tally("my-don-returned",event.sourceCardNumber,event.count);break;default:break}}return out}function compactDecision(observation,offered,rows,context={}){const row=number=>number?rows.get(number)??-1:-1;const{self,opponent}=observation;const battle=observation.activeBattle;const cards=[];const index=new Map;const add=(instanceId,number,zone,numbers)=>{if(instanceId)index.set(instanceId,cards.length);cards.push([row(number),ZONES.indexOf(zone),...numbers])};const turn=observation.turn;const myLeader=`${observation.viewer}-leader`;const opLeader=`${observation.viewer==="p1"?"p2":"p1"}-leader`;add(myLeader,self.leader.cardNumber,"my-leader",inPlay(self.leader,myLeader,turn,battle,true));for(const entry of self.hand){const numbers=plain(1,true,entry.currentCost-entry.cost);numbers[CARD_COUNTER_COLUMN]=k(entry.currentCounter-entry.counter);add(entry.instanceId,entry.cardNumber,"my-hand",numbers)}for(const entry of self.board)add(entry.instanceId,entry.cardNumber,"my-board",inPlay(entry,entry.instanceId,turn,battle,true));if(self.stage)add(self.stage.instanceId,self.stage.cardNumber,"my-stage",stageRow(self.stage,true));add(opLeader,opponent.leader.cardNumber,"op-leader",inPlay(opponent.leader,opLeader,turn,battle,false));for(const entry of opponent.board)add(entry.instanceId,entry.cardNumber,"op-board",inPlay(entry,entry.instanceId,turn,battle,false));if(opponent.stage)add(opponent.stage.instanceId,opponent.stage.cardNumber,"op-stage",stageRow(opponent.stage,false));const counted=(list,zone,mine)=>{const counts=new Map;for(const entry of list)counts.set(entry.cardNumber,(counts.get(entry.cardNumber)??0)+1);for(const[number,count]of counts)add(void 0,number,zone,plain(count,mine))};counted(self.trash,"my-trash",true);counted(opponent.trash,"op-trash",false);counted(self.life.filter(e=>e.faceUp&&e.cardNumber).map(e=>({cardNumber:e.cardNumber})),"my-life-up",true);counted(opponent.life.filter(e=>e.faceUp&&e.cardNumber).map(e=>({cardNumber:e.cardNumber})),"op-life-up",false);if(self.knowledge.ownDeckTop)add(void 0,self.knowledge.ownDeckTop.cardNumber,"my-deck-top",plain(1,true));if(self.knowledge.opponentDeckTop)add(void 0,self.knowledge.opponentDeckTop.cardNumber,"op-deck-top",plain(1,false));const history=historyFacts(observation);counted(history.played.map(cardNumber=>({cardNumber})),"op-played",false);counted(history.used.map(cardNumber=>({cardNumber})),"op-used",false);counted(history.known.map(cardNumber=>({cardNumber})),"op-hand-known",false);for(const[number,count]of unseen(observation,context,history.known))add(void 0,number,count.zone,plain(count.copies,count.mine));for(const[key,entry]of lastTurn(observation)){const[zone,number]=key.split("|");const numbers=plain(entry.count,zone.startsWith("my-"));numbers[0]=k(entry.power);numbers[11]=entry.atLeader;add(void 0,number,zone,numbers)}const global=[turn/10,observation.turnPlayer===observation.viewer?1:0,...PHASES.map(phase=>observation.phase===phase?1:0),...resources(self,self.hand.length),...resources(opponent,opponent.handCount),battle?1:0,k(battle?.attackPower),k(battle?.targetPower),k(battle?.counterPower),k(battle?.counterShortfall),battle?.lifeCost??0,offered.length/10,self.board.length,opponent.board.length,history.known.length,...leaderSpent(self.leader),...leaderSpent(opponent.leader),...timeLimit(observation),...sideFlags(self),...sideFlags(opponent)];const ptr=instanceId=>instanceId?index.get(instanceId)??-1:-1;const options=offered.map(action=>{const n=new Array(OPTION_NUMBERS).fill(0);n[OPTION_ROW_COLUMN]=-1;let kind="none";let p1=-1;let p2=-1;let cardRow=-1;const shared=a=>{if(a.ramps)n[10]=a.ramps.active+a.ramps.rested;if(a.donGift)n[11]=a.donGift.count;if(a.grants)n[12]=1+KEYWORDS.indexOf(a.grants.keyword);if(a.cost){n[13]=1;n[OPTION_COST_COLUMN]=a.cost.returnsDon??0;n[OPTION_COST_COLUMN+1]=a.cost.trashesFromHand??0;n[OPTION_COST_COLUMN+2]=a.cost.restsDon??0}for(const kind2 of a.payoff??[])n[OPTION_PAYOFF_COLUMN+PAYOFF_KINDS.indexOf(kind2)]=1};switch(action.type){case"mulligan-decision":n[0]=action.decision==="keep"?1:0;break;case"play-card":p1=ptr(action.cardInstanceId);break;case"attach-don":p1=ptr(action.targetInstanceId);break;case"attack":p1=ptr(action.attackerInstanceId);p2=ptr(action.targetInstanceId);n[1]=k(action.attackPower);n[2]=k(action.targetPower);n[3]=k(action.counterShortfall);n[4]=action.donToReach??0;n[5]=action.donToWinBattle??0;n[6]=action.unspentActiveDon??0;n[7]=action.attackerIsBlocker?1:0;n[8]=action.attackerIsLeader?1:0;n[9]=k(action.effectiveTargetPower);break;case"block":p1=ptr(action.blockerInstanceId);break;case"counter":p1=ptr(action.cardInstanceId);n[1]=k(action.counterPower);break;case"activate-counter-event":p1=ptr(action.eventInstanceId);cardRow=row(action.sourceCardNumber);n[1]=k(action.counterPower);break;case"activate-main":p1=ptr(action.sourceInstanceId);p2=ptr(action.targetInstanceId);cardRow=row(action.sourceCardNumber);n[1]=action.amount??0;n[14]=action.position==="top"?1:action.position==="bottom"?-1:0;n[15]=action.selectsNothing?1:0;shared(action);break;case"resolve-trigger":p2=ptr(action.targetInstanceId);cardRow=row(action.sourceCardNumber);n[15]=action.selectsNothing?1:0;n[16]=action.selfPowerForTheTurn?1:0;shared(action);break;case"resolve-effect":{const option=action.option;kind=KINDS.includes(option.kind)?option.kind:"other";cardRow=row(action.sourceCardNumber);shared(action);if(action.modifiesPower){n[17]=k(action.modifiesPower.amount);n[18]=action.modifiesPower.expiresThisTurn?1:0}if(action.destination){n[19]=1+CARD_ZONES.indexOf(action.destination.zone);n[20]=action.destination.player==="self"?1:action.destination.player==="opponent"?-1:0}switch(option.kind){case"confirm":n[21]=["apply","done","activate"].indexOf(option.choice)+1;break;case"cost":n[21]=option.cost;break;case"position":n[21]=option.position==="top"?1:-1;break;case"resource":n[21]=option.resource==="leader"?1:-1;break;case"don-return-count":n[21]=option.count;break;case"mode":n[21]=1+bucket(option.mode,32);break;case"card":{const own=ptr(option.card.instanceId);if(own>=0)p1=own;else n[22]=row(option.card.cardNumber);n[21]=option.owner==="self"?1:-1;n[23]=1+CARD_ZONES.indexOf(option.zone);n[24]=option.amount??0;n[25]=k(option.currentPower);n[26]=option.rested?1:0;if(option.via)p2=ptr(option.via.instanceId);break}case"don-return":n[21]=["active","rested","attached"].indexOf(option.pool)+1;if(option.card)p1=ptr(option.card.instanceId);n[24]=option.available;n[25]=option.remaining;break;case"order":n[21]=option.cards.length;break;default:break}break}case"pass":n[1]=action.unspentActiveDon??0;n[2]=action.availablePlayCount??0;n[3]=action.availableActivationCount??0;n[4]=action.eligibleAttackCount??0;break;default:break}n[27]=1;return[TYPES.indexOf(action.type),KINDS.indexOf(kind),p1,p2,cardRow,...n]});return{global,cards,options}}var CARD_INDEX=2;var OPTION_INDEX=5;var OPTION_ROW_COLUMN2=22;var LAYER_NORM_EPS=1e-5;var STAGE_ZONES=[ZONES.indexOf("my-stage"),ZONES.indexOf("op-stage")];function erf(x){const sign=x<0?-1:1;const t=1/(1+.3275911*Math.abs(x));const y=1-((((1.061405429*t-1.453152027)*t+1.421413741)*t-.284496736)*t+.254829592)*t*Math.exp(-x*x);return sign*y}function geluInPlace(x){for(let i=0;i<x.length;i+=1){const v=x[i];x[i]=.5*v*(1+erf(v/Math.SQRT2))}return x}var halfBuffer=new DataView(new ArrayBuffer(4));function roundHalf(x){if(!Number.isFinite(x)||x===0)return x;halfBuffer.setFloat32(0,x);const bits=halfBuffer.getUint32(0);const sign=bits>>>31;const exponent=(bits>>>23&255)-127+15;let mantissa=bits&8388607;if(exponent>=31)return sign?-65504:65504;if(exponent<=0){const step=2**-24;return Math.round(x/step)*step}const dropped=mantissa&8191;mantissa>>>=13;if(dropped>4096||dropped===4096&&mantissa&1)mantissa+=1;const value=(1+mantissa/1024)*2**(exponent-15);return sign?-value:value}var mlpOps=name=>[{linear:`${name}.layers.0`},"gelu",{linear:`${name}.layers.2`}];var CompactModel=class{config;#tensors;#rows;#cpu=new CpuMatmul;#zones;#v1;#globals;#options;#cardNumbers;constructor(config,tensors,cards,zones){this.config=config;this.#tensors=tensors;this.#rows=new Map(cards.map((number,row)=>[number,row]));this.#zones=zones??this.#t("zone.weight").shape[0];this.#v1=zones!==void 0;this.#globals=this.#t("glob.layers.0.w").shape[0];this.#options=this.#t("option_num.layers.0.w").shape[0];this.#cardNumbers=this.#t("card_num.layers.0.w").shape[0]}get rows(){return this.#rows}tensor(name){return this.#t(name)}#t(name){const tensor=this.#tensors.get(name);if(!tensor)throw new Error(`COMPACT_TENSOR_MISSING: ${name}`);return tensor}#linear(name,x,rows){return this.#cpu.matmul(x,rows,name,this.#t(`${name}.w`),this.#t(`${name}.b`).data)}#layerNorm(name,x,rows){const d=this.config.d;const gain=this.#t(`${name}.weight`).data;const bias=this.#t(`${name}.bias`).data;const out=new Float32Array(x.length);for(let r=0;r<rows;r+=1){const o=r*d;let mean=0;for(let i=0;i<d;i+=1)mean+=x[o+i];mean/=d;let variance=0;for(let i=0;i<d;i+=1)variance+=(x[o+i]-mean)**2;const scale=1/Math.sqrt(variance/d+LAYER_NORM_EPS);for(let i=0;i<d;i+=1)out[o+i]=(x[o+i]-mean)*scale*gain[i]+bias[i]}return out}#embed(name,row,into,offset){const{data,shape}=this.#t(`${name}.weight`);const d=shape[1];for(let i=0;i<d;i+=1)into[offset+i]+=data[row*d+i]}#cardRow(row){return row<0?this.config.cards:row}score(input){const pass=this.#forward([input]);let step=pass.next();while(!step.done)step=pass.next(this.runOnCpu(step.value));return step.value[0]}async scoreMany(inputs,run){const pass=this.#forward(inputs);let step=pass.next();while(!step.done)step=pass.next(await run(step.value));return step.value}runOnCpu(stage){const d=this.config.d;return stage.map(request=>{if("blocks"in request){const{x:x2,spans}=request;const out=new Float32Array(x2.length);for(let t=0;t<spans.length/2;){const[start,end]=[spans[t*2],spans[t*2+1]];out.set(this.blocks(x2.subarray(start*d,end*d),end-start),start*d);t=end}return out}let x=request.x;for(const op of request.ops)x=op==="gelu"?geluInPlace(new Float32Array(x)):this.#linear(op.linear,x,request.rows);return x})}#known(input){if(this.#cardNumbers<CARD_NUMBERS){const width=CARD_INDEX+this.#cardNumbers;input={...input,cards:input.cards.map(row=>STAGE_ZONES.includes(row[1])?row.slice(0,width).map((v,i)=>STAGE_COLUMNS.includes(i-CARD_INDEX)?0:v):row.slice(0,width))}}if(input.global.length>this.#globals)input={...input,global:input.global.slice(0,this.#globals)};const optionRow=OPTION_INDEX+this.#options;if(input.options.some(row=>row.length>optionRow))input={...input,options:input.options.map(row=>row.slice(0,optionRow))};const zones=this.#zones;if(input.cards.some(row=>row[1]>=zones))input={...input,cards:input.cards.filter(row=>row[1]<zones)};if(!this.#v1)return input;return{...input,options:input.options.map(row=>row[OPTION_INDEX+OPTION_ROW_COLUMN2]<0?row.map((v,i)=>i===OPTION_INDEX+OPTION_ROW_COLUMN2?0:v):row)}}*#forward(inputs){const d=this.config.d;const decisions=inputs.map(input=>this.#known(input));const B=decisions.length;const cardStart=[];const optionStart=[];let C=0;let K=0;for(const decision of decisions){cardStart.push(C);optionStart.push(K);C+=decision.cards.length;K+=decision.options.length}const sample=decisions.find(decision=>decision.cards.length>0);const cardWidth=(sample?.cards[0]?.length??CARD_INDEX)-CARD_INDEX;const optionWidth=decisions[0].options[0].length-OPTION_INDEX;const cardNumbers=new Float32Array(C*cardWidth);const optionNumbers=new Float32Array(K*optionWidth);const globalWidth=decisions[0].global.length;const globals=new Float32Array(B*globalWidth);decisions.forEach((decision,b)=>{decision.cards.forEach((row,c)=>{const o=(cardStart[b]+c)*cardWidth;for(let i=0;i<cardWidth;i+=1)cardNumbers[o+i]=roundHalf(row[CARD_INDEX+i])});decision.options.forEach((row,k2)=>{const o=(optionStart[b]+k2)*optionWidth;for(let i=0;i<optionWidth;i+=1)optionNumbers[o+i]=i===OPTION_ROW_COLUMN2?0:roundHalf(row[OPTION_INDEX+i])});decision.global.forEach((v,i)=>{globals[b*globalWidth+i]=roundHalf(v)})});const first=[{x:optionNumbers,rows:K,ops:mlpOps("option_num")},{x:globals,rows:B,ops:mlpOps("glob")}];if(C>0)first.push({x:cardNumbers,rows:C,ops:mlpOps("card_num")});const[options,glob,projected]=yield first;const cards=projected??new Float32Array(0);decisions.forEach((decision,b)=>{decision.cards.forEach((row,c)=>{const o=(cardStart[b]+c)*d;this.#embed("card",this.#cardRow(row[0]),cards,o);this.#embed("zone",Math.max(row[1],0),cards,o)})});const pointed=column=>{const gathered=new Float32Array(K*d);decisions.forEach((decision,b)=>{decision.options.forEach((row,k2)=>{const ptr=row[column];if(ptr<0)return;const from=(cardStart[b]+ptr)*d;gathered.set(cards.subarray(from,from+d),(optionStart[b]+k2)*d)})});return gathered};const named=rowOf=>{const table=new Float32Array(K*d);decisions.forEach((decision,b)=>{decision.options.forEach((row,k2)=>{this.#embed("card",this.#cardRow(rowOf(row)),table,(optionStart[b]+k2)*d)})});return table};const pooled=this.#tensors.has("pool.w");const pools=[];const sums=[];decisions.forEach((decision,b)=>{for(const zone of pooled?POOLED_ZONES:[]){const sum=new Float32Array(d);let members=0;decision.cards.forEach((row,c)=>{if(row[1]!==zone)return;members+=1;const o=(cardStart[b]+c)*d;for(let i=0;i<d;i+=1)sum[i]+=cards[o+i]});if(members===0)continue;pools.push({decision:b,row:pools.length});sums.push(...sum)}});const second=[{x:pointed(2),rows:K,ops:[{linear:"ptr1"}]},{x:pointed(3),rows:K,ops:[{linear:"ptr2"}]},{x:named(row=>row[4]),rows:K,ops:[{linear:"option_card"}]},{x:named(row=>row[OPTION_INDEX+OPTION_ROW_COLUMN2]),rows:K,ops:[{linear:"searched_card"}]}];if(pools.length>0)second.push({x:Float32Array.from(sums),rows:pools.length,ops:[{linear:"pool"}]});const[p1,p2,optionCard,searched,poolOut]=yield second;const length=[];const before=[];decisions.forEach((decision,b)=>{const kept=pooled?decision.cards.filter(row=>!POOLED_ZONES.includes(row[1])).length:decision.cards.length;const own=pools.filter(pool=>pool.decision===b).length;before.push(1+kept+own);length.push(1+kept+own+decision.options.length)});const total=length.reduce((a,b)=>a+b,0);const x=new Float32Array(total*d);const spans=new Int32Array(total*2);const tokenStart=[];let at=0;decisions.forEach((decision,b)=>{tokenStart.push(at);for(let t2=0;t2<length[b];t2+=1){spans[(at+t2)*2]=at;spans[(at+t2)*2+1]=at+length[b]}let t=at;x.set(glob.subarray(b*d,b*d+d),t*d);t+=1;decision.cards.forEach((row,c)=>{if(pooled&&POOLED_ZONES.includes(row[1]))return;const o=(cardStart[b]+c)*d;x.set(cards.subarray(o,o+d),t*d);t+=1});for(const pool of pools)if(pool.decision===b){x.set(poolOut.subarray(pool.row*d,pool.row*d+d),t*d);t+=1}decision.options.forEach((row,k2)=>{const from=(optionStart[b]+k2)*d;const to=t*d;this.#embed("type",Math.max(row[0],0),x,to);this.#embed("kind",Math.max(row[1],0),x,to);const hasCard=row[4]>=0;const hasSearched=row[OPTION_INDEX+OPTION_ROW_COLUMN2]>=0;for(let i=0;i<d;i+=1)x[to+i]+=options[from+i]+p1[from+i]+p2[from+i]+(hasCard?optionCard[from+i]:0)+(hasSearched?searched[from+i]:0);t+=1});at+=length[b]});const[encoded]=yield[{blocks:true,x,spans}];const optionRows=new Float32Array(K*d);const globalRows=new Float32Array(B*d);decisions.forEach((decision,b)=>{const s=tokenStart[b];globalRows.set(encoded.subarray(s*d,s*d+d),b*d);const o=(s+before[b])*d;optionRows.set(encoded.subarray(o,o+decision.options.length*d),optionStart[b]*d)});const[logits,values]=yield[{x:optionRows,rows:K,ops:[...mlpOps("policy"),{linear:"policy_out"}]},{x:globalRows,rows:B,ops:[...mlpOps("value"),{linear:"value_out"}]}];return decisions.map((decision,b)=>{const own=logits.subarray(optionStart[b],optionStart[b]+decision.options.length);let max=-Infinity;for(const v of own)if(v>max)max=v;const exps=Array.from(own,v=>Math.exp(v-max));const sum=exps.reduce((a,c)=>a+c,0);return{probs:exps.map(e=>e/sum),value:Math.tanh(values[b])}})}blocks(input,T){const{d,heads,layers}=this.config;const x=new Float32Array(input);const hd=d/heads;const scale=1/Math.sqrt(hd);for(let l=0;l<layers;l+=1){const p=`blocks.${l}`;const qkv=this.#linear(`${p}.qkv`,this.#layerNorm(`${p}.norm1`,x,T),T);const attended=new Float32Array(T*d);const weights=new Float32Array(T);for(let h=0;h<heads;h+=1){const qo=h*hd;const ko=d+h*hd;const vo=2*d+h*hd;for(let i=0;i<T;i+=1){let max=-Infinity;for(let j=0;j<T;j+=1){let dot=0;for(let e=0;e<hd;e+=1)dot+=qkv[i*3*d+qo+e]*qkv[j*3*d+ko+e];weights[j]=dot*scale;if(weights[j]>max)max=weights[j]}let sum=0;for(let j=0;j<T;j+=1){weights[j]=Math.exp(weights[j]-max);sum+=weights[j]}for(let j=0;j<T;j+=1){const w=weights[j]/sum;for(let e=0;e<hd;e+=1)attended[i*d+h*hd+e]+=w*qkv[j*3*d+vo+e]}}}const out=this.#linear(`${p}.out`,attended,T);for(let i=0;i<x.length;i+=1)x[i]+=out[i];const ff=this.#linear(`${p}.ff.layers.2`,geluInPlace(this.#linear(`${p}.ff.layers.0`,this.#layerNorm(`${p}.norm2`,x,T),T)),T);for(let i=0;i<x.length;i+=1)x[i]+=ff[i]}return this.#layerNorm("norm",x,T)}};function float16(bits){const sign=bits&32768?-1:1;const exponent=bits>>10&31;const fraction=bits&1023;if(exponent===0)return sign*2**-14*(fraction/1024);if(exponent===31)return fraction?NaN:sign*Infinity;return sign*2**(exponent-15)*(1+fraction/1024)}function decodeBase64(text){const binary=atob(text);const bytes=new Uint8Array(binary.length);for(let i=0;i<binary.length;i+=1)bytes[i]=binary.charCodeAt(i);return bytes}function decodeTensors(data,entries){const bytes=decodeBase64(data);const halves=new Uint16Array(bytes.buffer,bytes.byteOffset,bytes.byteLength/2);const table=new Float32Array(65536);for(let bits=0;bits<65536;bits+=1)table[bits]=float16(bits);const tensors=new Map;for(const[name,{offset,shape}]of Object.entries(entries)){const size=shape.reduce((product,n)=>product*n,1);const values=new Float32Array(size);for(let i=0;i<size;i+=1)values[i]=table[halves[offset+i]];tensors.set(name,{data:values,shape})}return tensors}function sharedCards(left,right){let shared=0;for(const[card,count]of Object.entries(left))shared+=Math.min(count,right[card]??0);return shared}function listSize(cards){let size=0;for(const count of Object.values(cards))size+=count;return size}function adapterFor(adapters,deck){let best;let bestShared=-1;for(const adapter of adapters){if(adapter.deck.leader!==deck.leader)continue;const shared=sharedCards(adapter.deck.cards,deck.cards);if(adapter.scope==="list"&&(shared!==listSize(adapter.deck.cards)||shared!==listSize(deck.cards)))continue;if(shared>bestShared)[best,bestShared]=[adapter,shared]}return best}function applyAdapter(tensors,adapter,cards){const factors=decodeTensors(adapter.data,adapter.tensors);if(adapter.cardRows?.length){const rows=factors.get("card.rows");const table=tensors.get("card.weight");if(!rows||!table)throw new Error("ADAPTER_TENSOR_MISSING: card.rows");const d=table.shape[1];if(rows.shape[0]!==adapter.cardRows.length||rows.shape[1]!==d)throw new Error("ADAPTER_SHAPE_MISMATCH: card.rows");const out=new Float32Array(table.data);adapter.cardRows.forEach((card,i)=>{const at=cards.indexOf(card);if(at<0)throw new Error(`ADAPTER_UNKNOWN_CARD: ${card}`);out.set(rows.data.subarray(i*d,(i+1)*d),at*d)});tensors.set("card.weight",{data:out,shape:table.shape})}for(const[name,a]of factors){if(!name.endsWith(".a"))continue;const layer=name.slice(0,-2);const b=factors.get(`${layer}.b`);const w=tensors.get(`${layer}.w`);if(!b||!w)throw new Error(`ADAPTER_TENSOR_MISSING: ${layer}`);const[inputs,rank]=a.shape;const outputs=b.shape[1];if(w.shape[0]!==inputs||w.shape[1]!==outputs||b.shape[0]!==rank)throw new Error(`ADAPTER_SHAPE_MISMATCH: ${layer}`);const out=new Float32Array(w.data);for(let i=0;i<inputs;i+=1)for(let k2=0;k2<rank;k2+=1){const scale=a.data[i*rank+k2];const row=i*outputs;const from=k2*outputs;for(let j=0;j<outputs;j+=1)out[row+j]+=scale*b.data[from+j]}tensors.set(`${layer}.w`,{data:out,shape:w.shape})}}function loadCompact(pack,adapter){if(pack.version!==1)throw new Error("COMPACT_PACK_VERSION");if(!Number.isInteger(pack.compactVersion)||pack.compactVersion<1||pack.compactVersion>COMPACT_VERSION)throw new Error(`COMPACT_ENCODING_MISMATCH: the model reads compact version ${pack.compactVersion}, this build writes ${COMPACT_VERSION}`);const tensors=decodeTensors(pack.data,pack.tensors);if(adapter){if(adapter.version!==1)throw new Error("COMPACT_ADAPTER_VERSION");if(adapter.base!==pack.source)throw new Error(`ADAPTER_BASE_MISMATCH: the ${adapter.deck.id} adapter was trained over ${adapter.base}, not ${pack.source}`);applyAdapter(tensors,adapter,pack.cards)}return new CompactModel(pack.config,tensors,pack.cards,pack.compactVersion===1?V1_ZONES:void 0)}function isAsideCounter(action){return action.type==="counter"&&action.targetInstanceId!==void 0}import{donCountReaders}from"open-optcg/engine";function isDoomedAttack(action,observation){if(action.type!=="attack"||action.attackerIsLeader)return false;if(action.counterShortfall!==0)return false;const attacker=observation.self.board.find(card=>card.instanceId===action.attackerInstanceId);if(!attacker)return false;const clauses=observation.cardFacts[attacker.cardNumber]?.rules.clauses;if(!clauses)return false;if(clauses.some(clause=>clause.includes("Your Turn")))return false;return action.whenAttackingInert===true||!clauses.some(clause=>clause.includes("When Attacking"))}function isWastedCounter(action,observation){if(action.type!=="counter")return false;const battle=observation.activeBattle;if(!battle||battle.counterShortfall===0)return false;let reachable=0;for(const offered of observation.legalActions){if(offered.type==="pass"||isAsideCounter(offered))continue;if(offered.type!=="counter"||offered.counterPower===void 0){return false}reachable+=offered.counterPower}return reachable<battle.counterShortfall}var LEADER_BASE_POWER_PAYMENTS={"ST26-005":7e3};var readers;function isSpentLeaderOverride(action,observation){if(action.type!=="resolve-effect")return false;if(action.option.kind!=="confirm"||action.option.choice!=="apply"){return false}const value=LEADER_BASE_POWER_PAYMENTS[action.sourceCardNumber];const returned=action.cost?.returnsDon;if(value===void 0||!returned)return false;const{self,opponent,viewer,publicEvents}=observation;if(observation.turnPlayer!==viewer)return false;const leaderId=`${viewer}-leader`;const alreadyPaid=publicEvents.slice(turnStart(publicEvents,viewer)).some(event=>event.type==="base-power-set"&&event.targetInstanceId===leaderId&&event.sourceCardNumber===action.sourceCardNumber&&event.basePower===value);if(!alreadyPaid)return false;if(self.activeDon+self.restedDon<returned)return false;readers??=donCountReaders();const{reactions,ceilings,ramps,opponent:watching}=readers;if(reactions.has(self.leader.cardNumber)&&!self.leader.leaderReactionSpent){return false}const inPlay2=[...self.board.map(card=>card.cardNumber),...self.stage?[self.stage.cardNumber]:[]];if(inPlay2.some(card=>reactions.has(card)))return false;const held=[self.leader.cardNumber,...inPlay2,...self.hand.map(card=>card.cardNumber)];if(held.some(card=>ceilings.has(card)))return false;const rampDemand=held.reduce((sum,card)=>sum+(ramps.get(card)??0),0);if(rampDemand>self.donDeckCount)return false;const theirs=[opponent.leader.cardNumber,...opponent.board.map(card=>card.cardNumber),...opponent.stage?[opponent.stage.cardNumber]:[]];return!theirs.some(card=>watching.has(card))}var DEFENDING_LEADER_DEBUFFS={"OP17-058":2e3};function isDebuffForADecidedBattle(action,observation){if(action.type!=="resolve-effect")return false;if(action.option.kind!=="confirm"||action.option.choice!=="apply"){return false}const amount=DEFENDING_LEADER_DEBUFFS[action.sourceCardNumber];const battle=observation.activeBattle;if(amount===void 0||!battle)return false;const{self,turnPlayer,viewer,cardFacts}=observation;if(turnPlayer===viewer)return false;if(battle.attackerInstanceId===`${turnPlayer}-leader`)return true;if(self.board.some(card=>!card.rested&&card.effectiveKeywords.includes("Blocker"))){return false}if(self.hand.some(card=>cardFacts[card.cardNumber]?.category==="event")){return false}const counters=self.hand.reduce((sum,card)=>sum+card.currentCounter,0);return battle.attackPower-amount>=battle.targetPower+battle.counterPower+counters}function isNoOpActivation(action){return action.type==="activate-main"&&action.changesNothing===true&&action.cost===void 0}function prunedActions(observation){const pruned=new Set;observation.legalActions.forEach((action,index)=>{if(isAsideCounter(action)||isDoomedAttack(action,observation)||isWastedCounter(action,observation)||isSpentLeaderOverride(action,observation)||isDebuffForADecidedBattle(action,observation)||isNoOpActivation(action)){pruned.add(index)}});return pruned.size===observation.legalActions.length?new Set:pruned}var CompactScorerAgent=class _CompactScorerAgent{#model;#batcher;lastValue;lastProbabilities;#context;constructor(pack,context={}){if("onGpu"in pack){this.#batcher=pack;this.#model=pack.model}else this.#model="score"in pack?pack:loadCompact(pack);this.#context=context}withDeck(ownDeck){return new _CompactScorerAgent(this.#batcher??this.#model,{ownDeck})}#score(decision){return this.#batcher?this.#batcher.score(decision):this.#model.score(decision)}async chooseAction(observation){if(observation.legalActions.length===1)return observation.legalActions[0];const pruned=prunedActions(observation);const offered=observation.legalActions.filter((_,i)=>!pruned.has(i));if(offered.length===1)return offered[0];const{probs,value}=await this.#score(compactDecision(observation,offered,this.#model.rows,this.#context));this.lastValue=value;this.lastProbabilities=probs;let best=0;for(let k2=1;k2<probs.length;k2+=1)if(probs[k2]>probs[best])best=k2;return offered[best]}};var HEADER=`
2
+ struct Dims { m: u32, n: u32, k: u32, a: u32, b: u32, c: u32, f0: f32, f1: f32 };
3
+ @group(0) @binding(0) var<uniform> dims: Dims;
4
+ `;var MATMUL=`${HEADER}
5
+ @group(0) @binding(1) var<storage, read> a: array<f32>;
6
+ @group(0) @binding(2) var<storage, read> w: array<f32>;
7
+ @group(0) @binding(3) var<storage, read_write> out: array<f32>;
8
+ var<workgroup> tileA: array<f32, 256>;
9
+ var<workgroup> tileB: array<f32, 256>;
10
+ @compute @workgroup_size(16, 16)
11
+ fn main(@builtin(global_invocation_id) g: vec3<u32>, @builtin(local_invocation_id) l: vec3<u32>) {
12
+ let row = g.y; let col = g.x;
13
+ var sum = 0.0;
14
+ let tiles = (dims.k + 15u) / 16u;
15
+ for (var t = 0u; t < tiles; t++) {
16
+ let ak = t * 16u + l.x;
17
+ let bk = t * 16u + l.y;
18
+ tileA[l.y * 16u + l.x] = select(0.0, a[row * dims.k + ak], row < dims.m && ak < dims.k);
19
+ tileB[l.y * 16u + l.x] = select(0.0, w[bk * dims.n + col], bk < dims.k && col < dims.n);
20
+ workgroupBarrier();
21
+ for (var i = 0u; i < 16u; i++) { sum += tileA[l.y * 16u + i] * tileB[i * 16u + l.x]; }
22
+ workgroupBarrier();
23
+ }
24
+ if (row < dims.m && col < dims.n) { out[row * dims.n + col] = sum; }
25
+ }`;var LAYERNORM=`${HEADER}
26
+ @group(0) @binding(1) var<storage, read> x: array<f32>;
27
+ @group(0) @binding(2) var<storage, read> weight: array<f32>;
28
+ @group(0) @binding(3) var<storage, read_write> out: array<f32>;
29
+ var<workgroup> partial: array<f32, 64>;
30
+ @compute @workgroup_size(64)
31
+ fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) l: vec3<u32>) {
32
+ let row = wg.x; let width = dims.n; let base = row * width;
33
+ var s = 0.0;
34
+ for (var j = l.x; j < width; j += 64u) { s += x[base + j]; }
35
+ partial[l.x] = s; workgroupBarrier();
36
+ for (var stride = 32u; stride > 0u; stride >>= 1u) {
37
+ if (l.x < stride) { partial[l.x] += partial[l.x + stride]; }
38
+ workgroupBarrier();
39
+ }
40
+ let mean = partial[0] / f32(width); workgroupBarrier();
41
+ var v = 0.0;
42
+ for (var j = l.x; j < width; j += 64u) { let d = x[base + j] - mean; v += d * d; }
43
+ partial[l.x] = v; workgroupBarrier();
44
+ for (var stride = 32u; stride > 0u; stride >>= 1u) {
45
+ if (l.x < stride) { partial[l.x] += partial[l.x + stride]; }
46
+ workgroupBarrier();
47
+ }
48
+ let inv = 1.0 / sqrt(partial[0] / f32(width) + dims.f0);
49
+ for (var j = l.x; j < width; j += 64u) { out[base + j] = (x[base + j] - mean) * inv * weight[j]; }
50
+ }`;var ROPE=`${HEADER}
51
+ @group(0) @binding(1) var<storage, read> positions: array<i32>;
52
+ @group(0) @binding(2) var<storage, read_write> qkv: array<f32>;
53
+ @compute @workgroup_size(64)
54
+ fn main(@builtin(global_invocation_id) g: vec3<u32>) {
55
+ let half = dims.b / 2u;
56
+ let per_token = 2u * dims.a * half;
57
+ let index = g.x;
58
+ if (index >= dims.m * per_token) { return; }
59
+ let t = index / per_token;
60
+ let rest = index % per_token;
61
+ let part = rest / (dims.a * half);
62
+ let head = (rest / half) % dims.a;
63
+ let i = rest % half;
64
+ let angle = f32(positions[t]) / pow(dims.f0, f32(2u * i) / f32(dims.b));
65
+ let o = t * 3u * dims.n + part * dims.n + head * dims.b;
66
+ let x1 = qkv[o + i]; let x2 = qkv[o + i + half];
67
+ qkv[o + i] = x1 * cos(angle) - x2 * sin(angle);
68
+ qkv[o + i + half] = x2 * cos(angle) + x1 * sin(angle);
69
+ }`;var ATTENTION=`${HEADER}
70
+ @group(0) @binding(1) var<storage, read> qkv: array<f32>;
71
+ @group(0) @binding(2) var<storage, read> positions: array<i32>;
72
+ @group(0) @binding(3) var<storage, read> segments: array<i32>;
73
+ @group(0) @binding(4) var<storage, read_write> out: array<f32>;
74
+ @compute @workgroup_size(64)
75
+ fn main(@builtin(global_invocation_id) g: vec3<u32>) {
76
+ let index = g.x;
77
+ if (index >= dims.m * dims.a) { return; }
78
+ let t = index / dims.a; let head = index % dims.a;
79
+ let D = dims.b; let H = dims.n;
80
+ let qo = t * 3u * H + head * D;
81
+ let seg = segments[t]; let pos = positions[t];
82
+ var acc: array<f32, 64>;
83
+ for (var d = 0u; d < 64u; d++) { acc[d] = 0.0; }
84
+ var maxv = -1e30; var total = 0.0;
85
+ for (var j = 0u; j < dims.m; j++) {
86
+ let sj = segments[j];
87
+ if (!(sj == seg || (sj == 0 && seg > 0))) { continue; }
88
+ if (dims.c == 0u && f32(abs(positions[j] - pos)) > dims.f0) { continue; }
89
+ let ko = j * 3u * H + H + head * D;
90
+ var dot = 0.0;
91
+ for (var d = 0u; d < D; d++) { dot += qkv[qo + d] * qkv[ko + d]; }
92
+ dot *= dims.f1;
93
+ let newmax = max(maxv, dot);
94
+ let correction = exp(maxv - newmax);
95
+ let e = exp(dot - newmax);
96
+ total = total * correction + e;
97
+ let vo = j * 3u * H + 2u * H + head * D;
98
+ for (var d = 0u; d < D; d++) { acc[d] = acc[d] * correction + e * qkv[vo + d]; }
99
+ maxv = newmax;
100
+ }
101
+ let o = t * H + head * D;
102
+ for (var d = 0u; d < D; d++) { out[o + d] = acc[d] / total; }
103
+ }`;var ADD=`${HEADER}
104
+ @group(0) @binding(1) var<storage, read> y: array<f32>;
105
+ @group(0) @binding(2) var<storage, read_write> x: array<f32>;
106
+ @compute @workgroup_size(64)
107
+ fn main(@builtin(global_invocation_id) g: vec3<u32>) {
108
+ if (g.x < dims.m) { x[g.x] += y[g.x]; }
109
+ }`;var GATE=`${HEADER}
110
+ @group(0) @binding(1) var<storage, read> wi: array<f32>;
111
+ @group(0) @binding(2) var<storage, read_write> out: array<f32>;
112
+ fn erf(x: f32) -> f32 {
113
+ let s = sign(x); let t = 1.0 / (1.0 + 0.3275911 * abs(x));
114
+ let y = 1.0 - ((((1.061405429 * t - 1.453152027) * t + 1.421413741) * t - 0.284496736) * t + 0.254829592) * t * exp(-x * x);
115
+ return s * y;
116
+ }
117
+ @compute @workgroup_size(64)
118
+ fn main(@builtin(global_invocation_id) g: vec3<u32>) {
119
+ if (g.x >= dims.m * dims.n) { return; }
120
+ let t = g.x / dims.n; let j = g.x % dims.n;
121
+ let v = wi[t * 2u * dims.n + j];
122
+ out[g.x] = 0.5 * v * (1.0 + erf(v / 1.4142135)) * wi[t * 2u * dims.n + dims.n + j];
123
+ }`;var HEADER2=`
124
+ struct Dims { m: u32, n: u32, k: u32, a: u32, b: u32, c: u32, f0: f32, f1: f32 };
125
+ @group(0) @binding(0) var<uniform> dims: Dims;
126
+ `;var MATMUL2=`${HEADER2}
127
+ @group(0) @binding(1) var<storage, read> a: array<f32>;
128
+ @group(0) @binding(2) var<storage, read> w: array<f32>;
129
+ @group(0) @binding(3) var<storage, read> bias: array<f32>;
130
+ @group(0) @binding(4) var<storage, read_write> out: array<f32>;
131
+ var<workgroup> tileA: array<f32, 256>;
132
+ var<workgroup> tileB: array<f32, 256>;
133
+ @compute @workgroup_size(16, 16)
134
+ fn main(@builtin(global_invocation_id) g: vec3<u32>, @builtin(local_invocation_id) l: vec3<u32>) {
135
+ let row = g.y; let col = g.x;
136
+ var sum = 0.0;
137
+ let tiles = (dims.k + 15u) / 16u;
138
+ for (var t = 0u; t < tiles; t++) {
139
+ let ak = t * 16u + l.x;
140
+ let bk = t * 16u + l.y;
141
+ tileA[l.y * 16u + l.x] = select(0.0, a[row * dims.k + ak], row < dims.m && ak < dims.k);
142
+ tileB[l.y * 16u + l.x] = select(0.0, w[bk * dims.n + col], bk < dims.k && col < dims.n);
143
+ workgroupBarrier();
144
+ for (var i = 0u; i < 16u; i++) { sum += tileA[l.y * 16u + i] * tileB[i * 16u + l.x]; }
145
+ workgroupBarrier();
146
+ }
147
+ if (row < dims.m && col < dims.n) {
148
+ var v = sum + bias[col];
149
+ if (dims.a == 1u) { v += out[row * dims.n + col]; }
150
+ out[row * dims.n + col] = v;
151
+ }
152
+ }`;var LAYERNORM2=`${HEADER2}
153
+ @group(0) @binding(1) var<storage, read> x: array<f32>;
154
+ @group(0) @binding(2) var<storage, read> gain: array<f32>;
155
+ @group(0) @binding(3) var<storage, read> beta: array<f32>;
156
+ @group(0) @binding(4) var<storage, read_write> out: array<f32>;
157
+ var<workgroup> partial: array<f32, 64>;
158
+ @compute @workgroup_size(64)
159
+ fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) l: vec3<u32>) {
160
+ let n = dims.n;
161
+ let base = wg.x * n;
162
+ var s = 0.0;
163
+ for (var i = l.x; i < n; i += 64u) { s += x[base + i]; }
164
+ partial[l.x] = s;
165
+ workgroupBarrier();
166
+ for (var stride = 32u; stride > 0u; stride >>= 1u) {
167
+ if (l.x < stride) { partial[l.x] += partial[l.x + stride]; }
168
+ workgroupBarrier();
169
+ }
170
+ let mean = partial[0] / f32(n);
171
+ workgroupBarrier();
172
+ var v = 0.0;
173
+ for (var i = l.x; i < n; i += 64u) { let e = x[base + i] - mean; v += e * e; }
174
+ partial[l.x] = v;
175
+ workgroupBarrier();
176
+ for (var stride = 32u; stride > 0u; stride >>= 1u) {
177
+ if (l.x < stride) { partial[l.x] += partial[l.x + stride]; }
178
+ workgroupBarrier();
179
+ }
180
+ let scale = inverseSqrt(partial[0] / f32(n) + dims.f0);
181
+ for (var i = l.x; i < n; i += 64u) { out[base + i] = (x[base + i] - mean) * scale * gain[i] + beta[i]; }
182
+ }`;var GELU=`${HEADER2}
183
+ @group(0) @binding(1) var<storage, read_write> x: array<f32>;
184
+ @compute @workgroup_size(64)
185
+ fn main(@builtin(global_invocation_id) g: vec3<u32>) {
186
+ let i = g.x + g.y * 65535u * 64u;
187
+ if (i >= dims.m) { return; }
188
+ let v = x[i];
189
+ let z = v / 1.4142135;
190
+ let t = 1.0 / (1.0 + 0.3275911 * abs(z));
191
+ let y = 1.0 - ((((1.061405429 * t - 1.453152027) * t + 1.421413741) * t - 0.284496736) * t + 0.254829592) * t * exp(-z * z);
192
+ x[i] = 0.5 * v * (1.0 + sign(z) * y);
193
+ }`;var ATTENTION2=`${HEADER2}
194
+ @group(0) @binding(1) var<storage, read> qkv: array<f32>;
195
+ @group(0) @binding(2) var<storage, read> spans: array<i32>;
196
+ @group(0) @binding(3) var<storage, read_write> out: array<f32>;
197
+ @compute @workgroup_size(64)
198
+ fn main(@builtin(global_invocation_id) g: vec3<u32>) {
199
+ let heads = dims.a; let hd = dims.b; let d = dims.n;
200
+ let idx = g.x + g.y * 65535u * 64u;
201
+ if (idx >= dims.m * heads) { return; }
202
+ let t = idx / heads; let h = idx % heads;
203
+ let start = u32(spans[t * 2u]); let end = u32(spans[t * 2u + 1u]);
204
+ var acc: array<f32, 64>;
205
+ for (var e = 0u; e < hd; e++) { acc[e] = 0.0; }
206
+ var top = -3.0e38; var sum = 0.0;
207
+ let qo = t * 3u * d + h * hd;
208
+ for (var j = start; j < end; j++) {
209
+ let ko = j * 3u * d + d + h * hd;
210
+ var dot = 0.0;
211
+ for (var e = 0u; e < hd; e++) { dot += qkv[qo + e] * qkv[ko + e]; }
212
+ dot *= dims.f0;
213
+ let next = max(top, dot);
214
+ let carry = exp(top - next);
215
+ let p = exp(dot - next);
216
+ sum = sum * carry + p;
217
+ let vo = j * 3u * d + 2u * d + h * hd;
218
+ for (var e = 0u; e < hd; e++) { acc[e] = acc[e] * carry + p * qkv[vo + e]; }
219
+ top = next;
220
+ }
221
+ for (var e = 0u; e < hd; e++) { out[t * d + h * hd + e] = acc[e] / sum; }
222
+ }`;async function loadModel(pack,options={}){const base=await read(pack);const adapters=await Promise.all((options.adapters??[]).map(read));const models=new Map;const modelFor=adapter=>{let model=models.get(adapter);if(!model){model=new CompactScorerAgent(loadCompact(base,adapter));models.set(adapter,model)}return model};modelFor(void 0);return{source:base.source,agentFor(deck){return modelFor(adapterFor(adapters,deck)).withDeck(deck.cards)}}}async function fetchLatestModel(repository="daavoo/open-optcg"){const releases=await read(`https://api.github.com/repos/${repository}/releases?per_page=100`);const release=releases.find(candidate=>!candidate.draft&&candidate.tag_name.startsWith("model-"));if(!release)throw new Error(`MODEL_NOT_FOUND: no model-* release`);const pack=release.assets.find(asset=>asset.name==="scorer-model.json");if(!pack)throw new Error(`MODEL_NOT_FOUND: ${release.tag_name}`);const adapters=release.assets.filter(asset=>/^scorer-adapter-.+\.json$/.test(asset.name)).map(asset=>asset.browser_download_url);const model=await loadModel(pack.browser_download_url,{adapters});return{...model,release:release.tag_name}}async function read(source){if(typeof source!=="string"&&!(source instanceof URL))return source;const response=await fetch(source);if(!response.ok){throw new Error(`MODEL_FETCH_FAILED: ${response.status} ${String(source)}`)}return await response.json()}export{fetchLatestModel,loadModel};
@@ -0,0 +1,17 @@
1
+ import { type CompactAdapter, type CompactPack } from "./scorer/index.js";
2
+ import type { Agent } from "open-optcg/protocol";
3
+ export type { CompactAdapter, CompactPack };
4
+ export interface LearnedModel {
5
+ readonly source: string;
6
+ agentFor(deck: {
7
+ leader: string;
8
+ cards: Record<string, number>;
9
+ }): Agent;
10
+ }
11
+ type Source<T> = T | string | URL;
12
+ export declare function loadModel(pack: Source<CompactPack>, options?: {
13
+ adapters?: readonly Source<CompactAdapter>[];
14
+ }): Promise<LearnedModel>;
15
+ export declare function fetchLatestModel(repository?: string): Promise<LearnedModel & {
16
+ readonly release: string;
17
+ }>;
@@ -0,0 +1,27 @@
1
+ import type { GameObservation, LegalAction } from "open-optcg/protocol";
2
+ import type { Agent } from "open-optcg/protocol";
3
+ import type { PackedDecision, ScorerModel } from "./model.js";
4
+ import { type ScorerPack } from "./pack.js";
5
+ import type { Tokenizer } from "./tokenizer.js";
6
+ export declare class ScorerAgent implements Agent {
7
+ #private;
8
+ lastValue: number | undefined;
9
+ lastProbabilities: number[] | undefined;
10
+ constructor(pack: ScorerPack | {
11
+ model: ScorerModel;
12
+ tokenizer: Tokenizer;
13
+ });
14
+ pack(observation: GameObservation): {
15
+ decision: PackedDecision;
16
+ offered: LegalAction[];
17
+ };
18
+ packRendered(rendered: {
19
+ state: string;
20
+ actions: string[];
21
+ }): PackedDecision;
22
+ score(decision: PackedDecision): Promise<{
23
+ probs: number[];
24
+ value: number;
25
+ }>;
26
+ chooseAction(observation: GameObservation): Promise<LegalAction>;
27
+ }
@@ -0,0 +1,14 @@
1
+ import type { GameObservation, LegalAction } from "open-optcg/protocol";
2
+ import type { Agent } from "open-optcg/protocol";
3
+ import { type CompactContext } from "./compact.js";
4
+ import type { CompactBatcher } from "./compact-batch.js";
5
+ import type { CompactModel } from "./compact-model.js";
6
+ import { type CompactPack } from "./pack.js";
7
+ export declare class CompactScorerAgent implements Agent {
8
+ #private;
9
+ lastValue: number | undefined;
10
+ lastProbabilities: number[] | undefined;
11
+ constructor(pack: CompactPack | CompactModel | CompactBatcher, context?: CompactContext);
12
+ withDeck(ownDeck: Record<string, number>): CompactScorerAgent;
13
+ chooseAction(observation: GameObservation): Promise<LegalAction>;
14
+ }
@@ -0,0 +1,10 @@
1
+ import type { CompactDecision } from "./compact.js";
2
+ import type { CompactGpu } from "./compact-gpu.js";
3
+ import type { CompactModel, CompactScore } from "./compact-model.js";
4
+ export declare class CompactBatcher {
5
+ #private;
6
+ readonly model: CompactModel;
7
+ constructor(model: CompactModel, gpu: CompactGpu | null);
8
+ get onGpu(): boolean;
9
+ score(decision: CompactDecision): Promise<CompactScore>;
10
+ }
@@ -0,0 +1,7 @@
1
+ import type { CompactModel, CompactRequest } from "./compact-model.js";
2
+ import { type GpuDevice } from "./gpu.js";
3
+ export declare class CompactGpu {
4
+ #private;
5
+ constructor(device: GpuDevice, model: CompactModel);
6
+ run(stage: CompactRequest[]): Promise<Float32Array[]>;
7
+ }
@@ -0,0 +1,37 @@
1
+ import { type CompactDecision } from "./compact.js";
2
+ import { type Tensor } from "./model.js";
3
+ export interface CompactConfig {
4
+ d: number;
5
+ layers: number;
6
+ heads: number;
7
+ cards: number;
8
+ }
9
+ export declare function roundHalf(x: number): number;
10
+ export interface CompactScore {
11
+ probs: number[];
12
+ value: number;
13
+ }
14
+ export type CompactOp = {
15
+ linear: string;
16
+ } | "gelu";
17
+ export type CompactRequest = {
18
+ x: Float32Array;
19
+ rows: number;
20
+ ops: CompactOp[];
21
+ } | {
22
+ blocks: true;
23
+ x: Float32Array;
24
+ spans: Int32Array;
25
+ };
26
+ export type CompactRunner = (stage: CompactRequest[]) => Float32Array[] | Promise<Float32Array[]>;
27
+ export declare class CompactModel {
28
+ #private;
29
+ readonly config: CompactConfig;
30
+ constructor(config: CompactConfig, tensors: Map<string, Tensor>, cards: string[], zones?: number);
31
+ get rows(): Map<string, number>;
32
+ tensor(name: string): Tensor;
33
+ score(input: CompactDecision): CompactScore;
34
+ scoreMany(inputs: readonly CompactDecision[], run: CompactRunner): Promise<CompactScore[]>;
35
+ runOnCpu(stage: CompactRequest[]): Float32Array[];
36
+ blocks(input: Float32Array, T: number): Float32Array;
37
+ }
@@ -0,0 +1,31 @@
1
+ import { type GameObservation, type LegalAction } from "open-optcg/protocol";
2
+ export declare const COMPACT_VERSION = 5;
3
+ export declare const ZONES: readonly ["my-leader", "my-hand", "my-board", "my-stage", "op-leader", "op-board", "op-stage", "my-trash", "op-trash", "my-life-up", "op-life-up", "my-deck-top", "op-deck-top", "op-played", "op-used", "op-hand-known", "my-unseen", "op-unseen", "op-attacker", "my-ko", "my-removed", "op-activated", "op-don-target", "op-blocker", "my-played", "my-activated", "my-attacker", "my-don-returned"];
4
+ export declare const V1_ZONES = 16;
5
+ export declare const V2_ZONES = 24;
6
+ export declare const TYPES: string[];
7
+ export declare const KINDS: string[];
8
+ export declare const V4_CARD_NUMBERS: number;
9
+ export declare const CARD_CONDITIONAL_COLUMN: number;
10
+ export declare const CARD_COUNTER_COLUMN: number;
11
+ export declare const CARD_NUMBERS: number;
12
+ export declare const STAGE_COLUMNS: readonly number[];
13
+ export declare const OPTION_ROW_COLUMN = 22;
14
+ export declare const POOLED_ZONES: readonly number[];
15
+ export declare const V3_OPTION_NUMBERS = 28;
16
+ export declare const OPTION_COST_COLUMN = 28;
17
+ export declare const OPTION_PAYOFF_COLUMN: number;
18
+ export declare const OPTION_NUMBERS: number;
19
+ export declare const V2_GLOBAL_NUMBERS: number;
20
+ export declare const V4_GLOBAL_NUMBERS: number;
21
+ export declare const GLOBAL_NUMBERS: number;
22
+ export interface CompactContext {
23
+ ownDeck?: Record<string, number>;
24
+ }
25
+ export declare function leaderPrior(leader: string): Map<string, number>;
26
+ export interface CompactDecision {
27
+ global: number[];
28
+ cards: number[][];
29
+ options: number[][];
30
+ }
31
+ export declare function compactDecision(observation: GameObservation, offered: LegalAction[], rows: Map<string, number>, context?: CompactContext): CompactDecision;
@@ -0,0 +1,7 @@
1
+ import type { GameObservation, LegalAction } from "open-optcg/protocol";
2
+ export declare function isDoomedAttack(action: LegalAction, observation: GameObservation): boolean;
3
+ export declare function isWastedCounter(action: LegalAction, observation: GameObservation): boolean;
4
+ export declare function isSpentLeaderOverride(action: LegalAction, observation: GameObservation): boolean;
5
+ export declare function isDebuffForADecidedBattle(action: LegalAction, observation: GameObservation): boolean;
6
+ export declare function isNoOpActivation(action: LegalAction): boolean;
7
+ export declare function prunedActions(observation: GameObservation): Set<number>;
@@ -0,0 +1,76 @@
1
+ import type { PackedDecision, ScorerModel } from "./model.js";
2
+ export interface GpuBuffer {
3
+ destroy(): void;
4
+ mapAsync(mode: number): Promise<void>;
5
+ getMappedRange(): ArrayBuffer;
6
+ unmap(): void;
7
+ }
8
+ export interface GpuPipeline {
9
+ getBindGroupLayout(index: number): unknown;
10
+ }
11
+ export interface GpuPass {
12
+ setPipeline(pipeline: GpuPipeline): void;
13
+ setBindGroup(index: number, group: unknown): void;
14
+ dispatchWorkgroups(x: number, y?: number): void;
15
+ end(): void;
16
+ }
17
+ export interface GpuCommandEncoder {
18
+ beginComputePass(): GpuPass;
19
+ copyBufferToBuffer(source: GpuBuffer, sourceOffset: number, target: GpuBuffer, targetOffset: number, size: number): void;
20
+ finish(): unknown;
21
+ }
22
+ export interface GpuDevice {
23
+ createBuffer(descriptor: {
24
+ size: number;
25
+ usage: number;
26
+ mappedAtCreation?: boolean;
27
+ }): GpuBuffer;
28
+ createShaderModule(descriptor: {
29
+ code: string;
30
+ }): unknown;
31
+ createComputePipeline(descriptor: {
32
+ layout: "auto";
33
+ compute: {
34
+ module: unknown;
35
+ entryPoint: string;
36
+ };
37
+ }): GpuPipeline;
38
+ createBindGroup(descriptor: {
39
+ layout: unknown;
40
+ entries: Array<{
41
+ binding: number;
42
+ resource: {
43
+ buffer: GpuBuffer;
44
+ };
45
+ }>;
46
+ }): unknown;
47
+ createCommandEncoder(): GpuCommandEncoder;
48
+ queue: {
49
+ writeBuffer(buffer: GpuBuffer, offset: number, data: ArrayBufferView): void;
50
+ submit(commands: unknown[]): void;
51
+ };
52
+ }
53
+ interface Scratch {
54
+ x: GpuBuffer;
55
+ h: GpuBuffer;
56
+ qkv: GpuBuffer;
57
+ att: GpuBuffer;
58
+ proj: GpuBuffer;
59
+ wi: GpuBuffer;
60
+ gated: GpuBuffer;
61
+ positions: GpuBuffer;
62
+ segments: GpuBuffer;
63
+ read: GpuBuffer;
64
+ }
65
+ export declare function mapRead(device: GpuDevice, buffer: GpuBuffer): Promise<void>;
66
+ export declare function requestGpuDevice(): Promise<GpuDevice | null>;
67
+ export declare class GpuEncoder {
68
+ #private;
69
+ constructor(device: GpuDevice, model: ScorerModel);
70
+ encode(decision: PackedDecision, embedded: Float32Array, debug?: {
71
+ layers: number;
72
+ buffer: keyof Scratch;
73
+ width: number;
74
+ }): Promise<Float32Array<ArrayBuffer>>;
75
+ }
76
+ export {};
@@ -0,0 +1,9 @@
1
+ export { ScorerAgent } from "./agent.js";
2
+ export { adapterFor, isCompactPack, loadCompact, loadScorer, type CompactAdapter, type CompactPack, type ScorerPack, } from "./pack.js";
3
+ export { CompactScorerAgent } from "./compact-agent.js";
4
+ export { CompactBatcher } from "./compact-batch.js";
5
+ export { CompactGpu } from "./compact-gpu.js";
6
+ export { CompactModel } from "./compact-model.js";
7
+ export { compactDecision, type CompactDecision } from "./compact.js";
8
+ export { GpuEncoder, requestGpuDevice } from "./gpu.js";
9
+ export { isDebuffForADecidedBattle, isDoomedAttack, isNoOpActivation, isSpentLeaderOverride, isWastedCounter, prunedActions, } from "./dominance.js";
@@ -0,0 +1 @@
1
+ export declare const LEADER_PRIORS: Readonly<Record<string, readonly [number, string]>>;
@@ -0,0 +1,46 @@
1
+ export interface ScorerConfig {
2
+ hidden: number;
3
+ layers: number;
4
+ heads: number;
5
+ intermediate: number;
6
+ globalEvery: number;
7
+ window: number;
8
+ ropeTheta: number;
9
+ normEps: number;
10
+ cls: number;
11
+ sep: number;
12
+ cardSlot: number;
13
+ render?: number;
14
+ }
15
+ export interface Tensor {
16
+ data: Float32Array;
17
+ shape: number[];
18
+ }
19
+ export interface MatmulBackend {
20
+ matmul(a: Float32Array, m: number, key: string, w: Tensor, bias?: Float32Array): Float32Array | Promise<Float32Array>;
21
+ }
22
+ export declare class CpuMatmul implements MatmulBackend {
23
+ matmul(a: Float32Array, m: number, _key: string, w: Tensor, bias?: Float32Array): Float32Array;
24
+ }
25
+ export interface PackedDecision {
26
+ ids: Int32Array;
27
+ positions: Int32Array;
28
+ segments: Int32Array;
29
+ cardRows: Int32Array;
30
+ stateLength: number;
31
+ actions: Array<[number, number]>;
32
+ }
33
+ export declare class ScorerModel {
34
+ #private;
35
+ readonly config: ScorerConfig;
36
+ constructor(config: ScorerConfig, tensors: Map<string, Tensor>, cards: readonly string[], backend?: MatmulBackend);
37
+ useGpu(encoder: {
38
+ encode(d: PackedDecision, x: Float32Array): Promise<Float32Array>;
39
+ }): void;
40
+ cardRow(cardNumber: string): number;
41
+ tensor(name: string): Tensor;
42
+ score(decision: PackedDecision): Promise<{
43
+ probs: number[];
44
+ value: number;
45
+ }>;
46
+ }
@@ -0,0 +1,60 @@
1
+ import { ScorerModel, type MatmulBackend, type ScorerConfig } from "./model.js";
2
+ import { Tokenizer, type TokenizerTables } from "./tokenizer.js";
3
+ import { CompactModel, type CompactConfig } from "./compact-model.js";
4
+ export interface ScorerPack {
5
+ version: number;
6
+ source: string;
7
+ trainedOnPrintedText?: boolean;
8
+ config: ScorerConfig;
9
+ cards: string[];
10
+ tokenizer: TokenizerTables;
11
+ tensors: Record<string, {
12
+ offset: number;
13
+ shape: number[];
14
+ }>;
15
+ data: string;
16
+ }
17
+ export interface CompactPack {
18
+ kind: "compact";
19
+ version: number;
20
+ compactVersion: number;
21
+ source: string;
22
+ trainedOnPrintedText?: boolean;
23
+ config: CompactConfig;
24
+ cards: string[];
25
+ tensors: Record<string, {
26
+ offset: number;
27
+ shape: number[];
28
+ }>;
29
+ data: string;
30
+ }
31
+ export declare function isCompactPack(pack: ScorerPack | CompactPack): pack is CompactPack;
32
+ export interface CompactAdapter {
33
+ kind: "compact-adapter";
34
+ version: number;
35
+ deck: {
36
+ id: string;
37
+ leader: string;
38
+ cards: Record<string, number>;
39
+ };
40
+ scope?: "list";
41
+ base: string;
42
+ source: string;
43
+ trainedOnPrintedText?: boolean;
44
+ rank: number;
45
+ cardRows?: string[];
46
+ tensors: Record<string, {
47
+ offset: number;
48
+ shape: number[];
49
+ }>;
50
+ data: string;
51
+ }
52
+ export declare function adapterFor(adapters: readonly CompactAdapter[], deck: {
53
+ leader: string;
54
+ cards: Record<string, number>;
55
+ }): CompactAdapter | undefined;
56
+ export declare function loadCompact(pack: CompactPack, adapter?: CompactAdapter): CompactModel;
57
+ export declare function loadScorer(pack: ScorerPack, backend?: MatmulBackend): {
58
+ model: ScorerModel;
59
+ tokenizer: Tokenizer;
60
+ };
@@ -0,0 +1,18 @@
1
+ import type { GameObservation, LegalAction } from "open-optcg/protocol";
2
+ export interface RenderedDecision {
3
+ state: string;
4
+ actions: string[];
5
+ }
6
+ export declare function card(cardNumber: string): string;
7
+ export declare function renderState(observation: GameObservation): string;
8
+ export declare function turnStart(events: GameObservation["publicEvents"], player: string): number;
9
+ export declare function historyFacts(observation: GameObservation): {
10
+ played: string[];
11
+ used: string[];
12
+ known: string[];
13
+ };
14
+ export declare function renderHistory(observation: GameObservation): string[];
15
+ export declare function renderAction(action: LegalAction, observation: GameObservation, labels: Map<string, string>): string;
16
+ export declare const RENDER_VERSION = 4;
17
+ export declare const SUPPORTED_RENDER_VERSIONS: readonly number[];
18
+ export declare function renderDecision(observation: GameObservation, version?: number): RenderedDecision;
@@ -0,0 +1,15 @@
1
+ export interface TokenizerTables {
2
+ words: Record<string, number[]>;
3
+ bytes: Record<string, number>;
4
+ cardSlot: number;
5
+ }
6
+ export interface Tokenized {
7
+ ids: number[];
8
+ cards: string[];
9
+ }
10
+ export declare class Tokenizer {
11
+ #private;
12
+ unseenWords: number;
13
+ constructor(tables: TokenizerTables);
14
+ encode(text: string): Tokenized;
15
+ }
package/package.json CHANGED
@@ -1,6 +1,37 @@
1
1
  {
2
2
  "name": "open-optcg-model",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.1-next.1",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/daavoo/open-optcg.git"
9
+ },
10
+ "homepage": "https://github.com/daavoo/open-optcg",
11
+ "engines": {
12
+ "node": ">=22"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "description": "The learned One Piece Card Game opponent for the open-optcg engine.",
19
+ "keywords": [
20
+ "one-piece",
21
+ "card-game",
22
+ "tcg",
23
+ "ai"
24
+ ],
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/types/index.d.ts",
28
+ "default": "./dist/index.js"
29
+ }
30
+ },
31
+ "peerDependencies": {
32
+ "open-optcg": "0.1.1-next.1"
33
+ },
34
+ "devDependencies": {
35
+ "open-optcg": "0.1.1-next.1"
36
+ }
6
37
  }