@piltoverarchive/riftbound-deck-codes 1.0.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,75 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2025
9
+
10
+ ### Added
11
+
12
+ - Initial release of RiftboundDeckCodes library
13
+ - Support for encoding Riftbound TCG decks to shareable codes
14
+ - Support for decoding deck codes back to deck lists
15
+ - Version 1 format: Main deck only
16
+ - Version 2 format: Main deck + sideboard support
17
+ - Support for 4 sets: OGN, OGS, SFD, ARC
18
+ - Support for 4 variants: base, a, s, b
19
+ - Support for card counts 1-12 (for runes and standard cards)
20
+ - Comprehensive error handling for invalid inputs
21
+ - TypeScript type definitions
22
+ - Base32 encoding for compact, shareable codes
23
+ - Variable-length integer encoding for space efficiency
24
+ - Full documentation and usage examples
25
+ - Compatibility with LoR deck code format structure
26
+
27
+ ### Format Specification
28
+
29
+ - Format byte: `1`
30
+ - Version 1: Main deck encoding only
31
+ - Version 2: Main deck + sideboard (exactly 0 or 8 cards)
32
+ - Card code format: `SET-NUMBERvariant` (e.g., `OGN-007a`)
33
+ - Set encoding: 1 byte per set (0-255 supported)
34
+ - Variant encoding: 1 byte per variant (0-255 supported)
35
+ - Count groups: 12 down to 1 for backward compatibility
36
+
37
+ ---
38
+
39
+ ## Version History Summary
40
+
41
+ | Version | Date | Key Changes |
42
+ | ------- | ---- | ---------------------------------------------------- |
43
+ | 1.0.0 | 2025 | Initial release with Version 1 and Version 2 support |
44
+
45
+ ---
46
+
47
+ ## Migration Guides
48
+
49
+ ### From Version 1 to Version 2
50
+
51
+ If you're upgrading from using Version 1 codes (main deck only) to Version 2 (with sideboard):
52
+
53
+ **Before (Version 1):**
54
+
55
+ ```typescript
56
+ const deckCode = getCodeFromDeck(mainDeck);
57
+ const decoded = getDeckFromCode(deckCode); // Returns Deck
58
+ ```
59
+
60
+ **After (Version 2):**
61
+
62
+ ```typescript
63
+ const deckCode = getCodeFromDeck(mainDeck, sideboard);
64
+ const decoded = getDeckFromCode(deckCode); // Returns DeckWithSideboard
65
+ ```
66
+
67
+ **Backward Compatibility:**
68
+
69
+ - Version 1 codes still work and return empty sideboard array
70
+ - Version 2 encoder automatically detects if sideboard is provided
71
+ - No breaking changes to existing code
72
+
73
+ ---
74
+
75
+ For a full list of changes, see the [commit history](https://github.com/Piltover-Archive/RiftboundDeckCodes/commits/main).
package/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright 2025 PiltoverArchive
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,203 @@
1
+ <div align="center">
2
+ <img src="https://cdn.piltoverarchive.com/PiltoverArchive.webp" alt="Piltover Archive" width="200"/>
3
+
4
+ # RiftboundDeckCodes
5
+
6
+ **By [Piltover Archive](https://piltoverarchive.com)**
7
+ </div>
8
+
9
+ The RiftboundDeckCodes library can be used to encode/decode Riftbound TCG decks to/from simple strings. Below is an example code for a Kai'Sa deck with sideboard.
10
+
11
+ ```
12
+ CIAAAAAAAAAQCAAAA4AACAIAABMQAAILAAAAICIMDMOVOX3AM5UHIAIDAAACO6XYAEAQKAAABX3QDGACUABKIAQAAEBQAAAWDBOQCAQAABMHE
13
+ ```
14
+
15
+ These strings can be used to share decks across Riftbound TCG applications and the PiltoverArchive companion app.
16
+
17
+ ## Cards & Decks
18
+
19
+ Every Riftbound TCG card has a corresponding card code. Card codes are comprised of a three-character set identifier, a three-digit card number, and an optional single-character variant suffix.
20
+
21
+ ```
22
+ OGN-007a
23
+ │ │ └ variant - a
24
+ │ └ card number - 007
25
+ └ set - OGN
26
+ ```
27
+
28
+ The deck code library accepts a Riftbound deck as a list of `Card` objects. This is simply the card code and an associated integer for the number of occurrences of the card in the deck.
29
+
30
+ ## Process
31
+
32
+ Decks are encoded via arranging VarInts (big endian) into an array and then base32 encoding into a string.
33
+
34
+ All encodings begin with 4 bits for format and 4 bits for version.
35
+
36
+ | Format | Version | Date | About |
37
+ | ------ | ------- | ----------------- | ------------------------------------------------------------------------- |
38
+ | 1 | 1 | April 1, 2025 | Initial release. Supports main deck encoding. |
39
+ | 1 | 2 | November 20, 2025 | Adds sideboard support. All new codes encode as V2 (backward compatible). |
40
+
41
+ The list of cards are then encoded according to the following scheme:
42
+
43
+ 1. Cards are grouped together based on how many copies of the card are in the deck (e.g., cards with twelve copies, cards with three copies, cards with two copies, and cards with a single copy are grouped together).
44
+ 2. Within those groups, lists of cards are created which share the same set AND variant.
45
+ 3. The set/variant lists are ordered by increasing length. The contents of the set/variant lists are ordered alphanumerically by card number.
46
+ 4. Variable length integer ([varints](https://en.wikipedia.org/wiki/Variable-length_quantity)) (big endian) bytes for each ordered group of cards are written into the byte array according to the following convention:
47
+ - [how many lists of set/variant combination have twelve copies of a card]
48
+ - [how many cards within this set/variant combination follow]
49
+ - [set]
50
+ - [variant]
51
+ - [card number]
52
+ - [card number]
53
+ - ...
54
+ - [how many cards in this next set/variant combination follow]
55
+ - [set]
56
+ - [variant]
57
+ - [card number]
58
+ - [card number]
59
+ - ...
60
+ - [repeat for the groups of eleven copies of a card]
61
+ - [repeat down to groups of a single copy of a card]
62
+ 5. For Version 2 decks, the sideboard is encoded using the same scheme after the main deck, but **only processes counts 3, 2, and 1** (since sideboards cannot contain runes which may have higher counts). This optimization reduces sideboard encoding size by ~12%.
63
+ 6. The resulting byte array is base32 encoded into a string.
64
+
65
+ ### Set Identifiers
66
+
67
+ Sets are mapped as follows:
68
+
69
+ | Version | Integer Identifier | Set Code | Set Name |
70
+ | ------- | ------------------ | -------- | --------------- |
71
+ | 1 | 0 | OGN | Origins |
72
+ | 1 | 1 | OGS | Proving Grounds |
73
+ | 2 | 2 | ARC | Arcane Box Set |
74
+ | 2 | 3 | SFD | Spiritforged |
75
+
76
+ ### Variant Identifiers
77
+
78
+ Variants are mapped as follows:
79
+
80
+ | Version | Integer Identifier | Variant Code | Variant Name |
81
+ | ------- | ------------------ | ------------ | --------------- |
82
+ | 1 | 0 | (none) | Base variant |
83
+ | 1 | 1 | a | Alternate art A |
84
+ | 1 | 2 | s | Signed |
85
+ | 2 | 3 | b | Alternate art B |
86
+
87
+ ## Installation
88
+
89
+ ```bash
90
+ npm install @piltoverarchive/riftbound-deck-codes
91
+ ```
92
+
93
+ Or using other package managers:
94
+
95
+ ```bash
96
+ yarn add @piltoverarchive/riftbound-deck-codes
97
+ pnpm add @piltoverarchive/riftbound-deck-codes
98
+ ```
99
+
100
+ ## Usage
101
+
102
+ ### Encoding a Deck
103
+
104
+ ```typescript
105
+ import { getCodeFromDeck } from "@piltoverarchive/riftbound-deck-codes";
106
+ import type { Deck } from "@piltoverarchive/riftbound-deck-codes";
107
+
108
+ // Kai'Sa deck
109
+ const mainDeck: Deck = [
110
+ { cardCode: "OGN-007", count: 7 },
111
+ { cardCode: "OGN-089", count: 5 },
112
+ { cardCode: "OGN-004", count: 3 },
113
+ { cardCode: "OGN-009", count: 3 },
114
+ { cardCode: "OGN-012", count: 3 },
115
+ { cardCode: "OGN-027", count: 3 },
116
+ { cardCode: "OGN-029", count: 3 },
117
+ { cardCode: "OGN-087", count: 3 },
118
+ { cardCode: "OGN-095", count: 3 },
119
+ { cardCode: "OGN-096", count: 3 },
120
+ { cardCode: "OGN-103", count: 3 },
121
+ { cardCode: "OGN-104", count: 3 },
122
+ { cardCode: "OGN-116", count: 3 },
123
+ { cardCode: "OGN-039", count: 2 },
124
+ { cardCode: "OGN-122", count: 2 },
125
+ { cardCode: "OGN-248", count: 2 },
126
+ { cardCode: "OGN-013", count: 1 },
127
+ { cardCode: "OGN-247", count: 1 },
128
+ { cardCode: "OGN-280", count: 1 },
129
+ { cardCode: "OGN-288", count: 1 },
130
+ { cardCode: "OGN-292", count: 1 },
131
+ ];
132
+
133
+ const sideboard: Deck = [
134
+ { cardCode: "OGN-022", count: 2 },
135
+ { cardCode: "OGN-024", count: 2 },
136
+ { cardCode: "OGN-093", count: 2 },
137
+ { cardCode: "OGN-088", count: 1 },
138
+ { cardCode: "OGN-114", count: 1 },
139
+ ];
140
+
141
+ // Encode with sideboard
142
+ const deckCode = getCodeFromDeck(mainDeck, sideboard);
143
+ console.log(deckCode);
144
+ // Output: CIAAAAAAAAAQCAAAA4AACAIAABMQAAILAAAAICIMDMOVOX3AM5UHIAIDAAACO6XYAEAQKAAABX3QDGACUABKIAQAAEBQAAAWDBOQCAQAABMHE
145
+
146
+ // Encode without sideboard (empty sideboard encoded as V2)
147
+ const deckCodeNoSideboard = getCodeFromDeck(mainDeck);
148
+ console.log(deckCodeNoSideboard);
149
+ // Output: CIAAAAAAAAAQCAAAA4AACAIAABMQAAILAAAAICIMDMOVOX3AM5UHIAIDAAACO6XYAEAQKAAABX3QDGACUABKIAQAAAAA
150
+ // Note: ~5 character overhead for empty sideboard encoding
151
+ ```
152
+
153
+ ### Decoding a Deck
154
+
155
+ ```typescript
156
+ import { getDeckFromCode } from "@piltoverarchive/riftbound-deck-codes";
157
+ import type { DeckWithSideboard } from "@piltoverarchive/riftbound-deck-codes";
158
+
159
+ const code =
160
+ "CIAAAAAAAAAQCAAAA4AACAIAABMQAAILAAAAICIMDMOVOX3AM5UHIAIDAAACO6XYAEAQKAAABX3QDGACUABKIAQAAEBQAAAWDBOQCAQAABMHE";
161
+
162
+ const decoded: DeckWithSideboard = getDeckFromCode(code);
163
+
164
+ console.log("Main Deck:", decoded.mainDeck);
165
+ // 21 cards including 7x OGN-007 (runes), 5x OGN-089 (runes), and various 1-3 copy cards
166
+
167
+ console.log("Sideboard:", decoded.sideboard);
168
+ // 8 cards: 2x OGN-022, 2x OGN-024, 2x OGN-093, 1x OGN-088, 1x OGN-114
169
+ ```
170
+
171
+ ### Important Notes
172
+
173
+ - **No Game Rule Validation**: This library only encodes/decodes deck data. It does not validate Riftbound game rules (card limits, sideboard size, etc.). Validation should be done in your application.
174
+ - **Card Counts**: Main deck supports counts 1-12 (for runes and standard cards). Sideboard only supports counts 1-3 (optimized for regular cards only).
175
+ - **Always Version 2**: All new deck codes encode as Version 2, even without a sideboard. Empty sideboards add ~5 characters overhead.
176
+ - **Backward Compatibility**: Can decode Version 1 codes (without sideboard section) which return an empty sideboard array.
177
+
178
+ ## Implementations
179
+
180
+ The TypeScript implementation in this repository is the reference implementation. Community implementations in other languages are welcome!
181
+
182
+ ### Current Version
183
+
184
+ | Name | Language | Version\* | Maintainer |
185
+ | ------------------ | ---------- | --------- | --------------- |
186
+ | RiftboundDeckCodes | TypeScript | 2 | PiltoverArchive |
187
+
188
+ \*Version refers to the MAX_KNOWN_VERSION supported by the implementation.
189
+
190
+ ## Links
191
+
192
+ - **PiltoverArchive**: [https://piltoverarchive.com](https://piltoverarchive.com) - Comprehensive Riftbound TCG companion app
193
+ - **Riftbound TCG**: [https://riftbound.leagueoflegends.com/](https://riftbound.leagueoflegends.com/) Official game information and rules
194
+
195
+ ## Credits
196
+
197
+ This library is adapted from [Riot Games' LoRDeckCodes](https://github.com/RiotGames/LoRDeckCodes) for use with Riftbound TCG.
198
+
199
+ This project is not affiliated with or endorsed by Riot Games.
200
+
201
+ ## License
202
+
203
+ Apache 2.0 (see [LICENSE](LICENSE) for details)
@@ -0,0 +1,40 @@
1
+ /**
2
+ * VarintTranslator handles encoding and decoding of variable-length integers
3
+ * used in the Riftbound deck code format.
4
+ *
5
+ * Variable-length integers use the most significant bit (MSB) as a continuation flag.
6
+ * If MSB is 1, more bytes follow. If MSB is 0, this is the final byte.
7
+ */
8
+ export default class VarintTranslator {
9
+ private static readonly AllButMSB;
10
+ private static readonly JustMSB;
11
+ private bytes;
12
+ constructor(_bytes: ArrayBuffer | Uint8Array);
13
+ get length(): number;
14
+ /**
15
+ * Reads and removes a varint from the beginning of the byte array
16
+ * @returns The decoded integer value
17
+ * @throws Error if no bytes available or invalid varint format
18
+ */
19
+ PopVarint(): number;
20
+ /**
21
+ * Slices the internal byte array
22
+ * @param begin - Start index
23
+ * @param end - Optional end index
24
+ */
25
+ sliceAndSet(begin: number, end?: number): void;
26
+ /**
27
+ * Gets a byte at the specified index
28
+ * @param index - Index to retrieve
29
+ * @returns The byte value at the index
30
+ * @throws Error if index is out of bounds
31
+ */
32
+ get(index: number): number;
33
+ /**
34
+ * Converts a number into its varint byte representation
35
+ * @param value - The number to encode
36
+ * @returns Uint8Array containing the varint encoding
37
+ */
38
+ static GetVarint(value: number): Uint8Array;
39
+ }
40
+ //# sourceMappingURL=VarintTranslator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VarintTranslator.d.ts","sourceRoot":"","sources":["../src/VarintTranslator.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACpC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAQ;IACzC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAEvC,OAAO,CAAC,KAAK,CAAa;gBAEd,MAAM,EAAE,WAAW,GAAG,UAAU;IAI5C,IAAW,MAAM,IAAI,MAAM,CAE1B;IAED;;;;OAIG;IACI,SAAS,IAAI,MAAM;IA6B1B;;;;OAIG;IACI,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI;IAIrD;;;;;OAKG;IACI,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAOjC;;;;OAIG;WACW,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU;CAqBlD"}
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ // THIS CODE IS ADAPTED FROM https://github.com/RiotGames/LoRDeckCodes/blob/main/LoRDeckCodes/VarintTranslator.cs
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ /**
5
+ * VarintTranslator handles encoding and decoding of variable-length integers
6
+ * used in the Riftbound deck code format.
7
+ *
8
+ * Variable-length integers use the most significant bit (MSB) as a continuation flag.
9
+ * If MSB is 1, more bytes follow. If MSB is 0, this is the final byte.
10
+ */
11
+ class VarintTranslator {
12
+ constructor(_bytes) {
13
+ this.bytes = new Uint8Array(_bytes);
14
+ }
15
+ get length() {
16
+ return this.bytes.length;
17
+ }
18
+ /**
19
+ * Reads and removes a varint from the beginning of the byte array
20
+ * @returns The decoded integer value
21
+ * @throws Error if no bytes available or invalid varint format
22
+ */
23
+ PopVarint() {
24
+ if (this.bytes.length === 0) {
25
+ throw new Error("No bytes available to read varint");
26
+ }
27
+ let result = 0;
28
+ let currentShift = 0;
29
+ let bytesPopped = 0;
30
+ for (let i = 0; i < this.bytes.length; i++) {
31
+ bytesPopped++;
32
+ const current = this.bytes[i] & VarintTranslator.AllButMSB;
33
+ result |= current << currentShift;
34
+ if ((this.bytes[i] & VarintTranslator.JustMSB) !==
35
+ VarintTranslator.JustMSB) {
36
+ this.bytes = this.bytes.slice(bytesPopped);
37
+ return result;
38
+ }
39
+ currentShift += 7;
40
+ }
41
+ throw new Error("Byte array did not contain valid varints.");
42
+ }
43
+ /**
44
+ * Slices the internal byte array
45
+ * @param begin - Start index
46
+ * @param end - Optional end index
47
+ */
48
+ sliceAndSet(begin, end) {
49
+ this.bytes = this.bytes.slice(begin, end);
50
+ }
51
+ /**
52
+ * Gets a byte at the specified index
53
+ * @param index - Index to retrieve
54
+ * @returns The byte value at the index
55
+ * @throws Error if index is out of bounds
56
+ */
57
+ get(index) {
58
+ if (index < 0 || index >= this.bytes.length) {
59
+ throw new Error(`Index out of bounds: ${index}`);
60
+ }
61
+ return this.bytes[index];
62
+ }
63
+ /**
64
+ * Converts a number into its varint byte representation
65
+ * @param value - The number to encode
66
+ * @returns Uint8Array containing the varint encoding
67
+ */
68
+ static GetVarint(value) {
69
+ const buff = new Uint8Array(10);
70
+ let currentIndex = 0;
71
+ if (value === 0)
72
+ return new Uint8Array([0]);
73
+ while (value !== 0) {
74
+ // Get lower 7 bits
75
+ let byteVal = value & this.AllButMSB;
76
+ // Shift right by 7 bits
77
+ value >>>= 7;
78
+ // If more bytes needed, set continuation bit
79
+ if (value !== 0)
80
+ byteVal |= this.JustMSB;
81
+ buff[currentIndex++] = byteVal;
82
+ }
83
+ return buff.slice(0, currentIndex);
84
+ }
85
+ }
86
+ VarintTranslator.AllButMSB = 0x7f;
87
+ VarintTranslator.JustMSB = 0x80;
88
+ exports.default = VarintTranslator;
89
+ //# sourceMappingURL=VarintTranslator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VarintTranslator.js","sourceRoot":"","sources":["../src/VarintTranslator.ts"],"names":[],"mappings":";AAAA,iHAAiH;;AAEjH;;;;;;GAMG;AACH,MAAqB,gBAAgB;IAMpC,YAAY,MAAgC;QAC3C,IAAI,CAAC,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IAED,IAAW,MAAM;QAChB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACI,SAAS;QACf,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACtD,CAAC;QAED,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,IAAI,WAAW,GAAG,CAAC,CAAC;QAEpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5C,WAAW,EAAE,CAAC;YAEd,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,GAAG,gBAAgB,CAAC,SAAS,CAAC;YAC5D,MAAM,IAAI,OAAO,IAAI,YAAY,CAAC;YAElC,IACC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,GAAG,gBAAgB,CAAC,OAAO,CAAC;gBAC3C,gBAAgB,CAAC,OAAO,EACvB,CAAC;gBACF,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;gBAC3C,OAAO,MAAM,CAAC;YACf,CAAC;YAED,YAAY,IAAI,CAAC,CAAC;QACnB,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC9D,CAAC;IAED;;;;OAIG;IACI,WAAW,CAAC,KAAa,EAAE,GAAY;QAC7C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC3C,CAAC;IAED;;;;;OAKG;IACI,GAAG,CAAC,KAAa;QACvB,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CAAC,wBAAwB,KAAK,EAAE,CAAC,CAAC;QAClD,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAE,CAAC;IAC3B,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,SAAS,CAAC,KAAa;QACpC,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,YAAY,GAAG,CAAC,CAAC;QAErB,IAAI,KAAK,KAAK,CAAC;YAAE,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAE5C,OAAO,KAAK,KAAK,CAAC,EAAE,CAAC;YACpB,mBAAmB;YACnB,IAAI,OAAO,GAAG,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;YAErC,wBAAwB;YACxB,KAAK,MAAM,CAAC,CAAC;YAEb,6CAA6C;YAC7C,IAAI,KAAK,KAAK,CAAC;gBAAE,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC;YAEzC,IAAI,CAAC,YAAY,EAAE,CAAC,GAAG,OAAO,CAAC;QAChC,CAAC;QAED,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;IACpC,CAAC;;AA9FuB,0BAAS,GAAG,IAAI,CAAC;AACjB,wBAAO,GAAG,IAAI,CAAC;kBAFnB,gBAAgB"}
@@ -0,0 +1,17 @@
1
+ import type { Deck, DeckWithSideboard } from "./types";
2
+ /**
3
+ * Encodes a Riftbound deck into a shareable deck code
4
+ * @param mainDeck - The main deck cards
5
+ * @param sideboard - Optional sideboard cards (defaults to empty array)
6
+ * @returns Base32-encoded deck code string
7
+ * @throws Error if deck format is invalid
8
+ */
9
+ export declare function getCodeFromDeck(mainDeck: Deck, sideboard?: Deck): string;
10
+ /**
11
+ * Decodes a Riftbound deck code into deck and sideboard
12
+ * @param code - Base32-encoded deck code string
13
+ * @returns Object containing mainDeck and sideboard arrays
14
+ * @throws Error if code is invalid or unsupported
15
+ */
16
+ export declare function getDeckFromCode(code: string): DeckWithSideboard;
17
+ //# sourceMappingURL=deckCode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deckCode.d.ts","sourceRoot":"","sources":["../src/deckCode.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAyB,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAsO9E;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,IAAI,EAAE,SAAS,GAAE,IAAS,GAAG,MAAM,CAc5E;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAqC/D"}
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getCodeFromDeck = getCodeFromDeck;
7
+ exports.getDeckFromCode = getDeckFromCode;
8
+ const mappings_1 = require("./mappings");
9
+ const VarintTranslator_1 = __importDefault(require("./VarintTranslator"));
10
+ const FORMAT = 1;
11
+ const VERSION = 2;
12
+ const BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
13
+ /**
14
+ * Encodes a byte array to base32 string
15
+ */
16
+ function base32Encode(bytes) {
17
+ let result = "";
18
+ let buffer = 0;
19
+ let bitsLeft = 0;
20
+ for (const byte of bytes) {
21
+ buffer = (buffer << 8) | byte;
22
+ bitsLeft += 8;
23
+ while (bitsLeft >= 5) {
24
+ bitsLeft -= 5;
25
+ result += BASE32_ALPHABET[(buffer >> bitsLeft) & 0x1f];
26
+ }
27
+ }
28
+ if (bitsLeft > 0) {
29
+ buffer <<= 5 - bitsLeft;
30
+ result += BASE32_ALPHABET[buffer & 0x1f];
31
+ }
32
+ return result;
33
+ }
34
+ /**
35
+ * Decodes a base32 string to byte array
36
+ */
37
+ function base32Decode(str) {
38
+ const bytes = [];
39
+ let buffer = 0;
40
+ let bitsLeft = 0;
41
+ for (const char of str) {
42
+ const value = BASE32_ALPHABET.indexOf(char.toUpperCase());
43
+ if (value === -1) {
44
+ throw new Error(`Invalid character in deck code: '${char}'`);
45
+ }
46
+ buffer = (buffer << 5) | value;
47
+ bitsLeft += 5;
48
+ while (bitsLeft >= 8) {
49
+ bitsLeft -= 8;
50
+ bytes.push((buffer >> bitsLeft) & 0xff);
51
+ }
52
+ }
53
+ return new Uint8Array(bytes);
54
+ }
55
+ /**
56
+ * Parses a Riftbound card code into its components
57
+ * @param cardCode - Card code in format "SET-NUMBERvariant" (e.g., "OGN-007a")
58
+ * @returns Object containing set, number, and variant
59
+ * @throws Error if card code format is invalid
60
+ */
61
+ function parseCardCode(cardCode) {
62
+ const parts = cardCode.split("-");
63
+ if (parts.length !== 2) {
64
+ throw new Error(`Invalid card code format: ${cardCode}. Expected format: SET-NUMBERvariant`);
65
+ }
66
+ const set = parts[0];
67
+ const rest = parts[1];
68
+ if (!set || !rest) {
69
+ throw new Error(`Invalid card code format: ${cardCode}. Missing set or card number.`);
70
+ }
71
+ const match = rest.match(/^(\d+)([a-z]?)$/);
72
+ if (!match) {
73
+ throw new Error(`Invalid card code format: ${cardCode}. Expected format: SET-NUMBERvariant`);
74
+ }
75
+ return {
76
+ set,
77
+ number: match[1] ?? "",
78
+ variant: match[2] ?? "",
79
+ };
80
+ }
81
+ /**
82
+ * Groups cards by set and variant for efficient encoding
83
+ */
84
+ function groupBySetAndVariant(cards) {
85
+ const groups = new Map();
86
+ for (const card of cards) {
87
+ const { set, number, variant } = parseCardCode(card.cardCode);
88
+ const key = `${set}-${variant}`;
89
+ if (!groups.has(key)) {
90
+ const setValue = mappings_1.SET_MAP[set];
91
+ if (setValue === undefined) {
92
+ throw new Error(`Unknown set: ${set}. Valid sets: ${Object.keys(mappings_1.SET_MAP).join(", ")}`);
93
+ }
94
+ const variantValue = mappings_1.VARIANT_MAP[variant];
95
+ if (variantValue === undefined) {
96
+ throw new Error(`Unknown variant: '${variant}'. Valid variants: ${Object.keys(mappings_1.VARIANT_MAP).join(", ")}`);
97
+ }
98
+ groups.set(key, {
99
+ set: setValue,
100
+ variant: variantValue,
101
+ cardNumbers: [],
102
+ });
103
+ }
104
+ groups.get(key).cardNumbers.push(number);
105
+ }
106
+ return Array.from(groups.values())
107
+ .sort((a, b) => {
108
+ if (a.set !== b.set)
109
+ return a.set - b.set;
110
+ if (a.variant !== b.variant)
111
+ return a.variant - b.variant;
112
+ return 0;
113
+ })
114
+ .map((group) => ({
115
+ ...group,
116
+ cardNumbers: group.cardNumbers.sort((a, b) => a.localeCompare(b, undefined, { numeric: true })),
117
+ }));
118
+ }
119
+ /**
120
+ * Encodes a deck section into bytes
121
+ * @param deck - The deck to encode
122
+ * @param maxCount - Maximum count to process (12 for main deck, 3 for sideboard)
123
+ */
124
+ function encodeDeckSection(deck, maxCount = 12) {
125
+ const bytes = [];
126
+ // Process counts from maxCount down to 1
127
+ for (let count = maxCount; count >= 1; count--) {
128
+ const cards = deck.filter((card) => card.count === count);
129
+ const setVariantGroups = groupBySetAndVariant(cards);
130
+ // Write number of set/variant groups
131
+ bytes.push(...VarintTranslator_1.default.GetVarint(setVariantGroups.length));
132
+ // Write each set/variant group
133
+ for (const group of setVariantGroups) {
134
+ bytes.push(...VarintTranslator_1.default.GetVarint(group.cardNumbers.length));
135
+ bytes.push(group.set);
136
+ bytes.push(group.variant);
137
+ // Write card numbers
138
+ for (const cardNumber of group.cardNumbers) {
139
+ bytes.push(...VarintTranslator_1.default.GetVarint(parseInt(cardNumber)));
140
+ }
141
+ }
142
+ }
143
+ return bytes;
144
+ }
145
+ /**
146
+ * Decodes a deck section from bytes
147
+ * @param translator - The varint translator
148
+ * @param maxCount - Maximum count to process (12 for main deck, 3 for sideboard)
149
+ */
150
+ function decodeDeckSection(translator, maxCount = 12) {
151
+ const deck = [];
152
+ // Process counts from maxCount down to 1
153
+ for (let count = maxCount; count >= 1; count--) {
154
+ const numGroups = translator.PopVarint();
155
+ for (let i = 0; i < numGroups; i++) {
156
+ const numCards = translator.PopVarint();
157
+ const set = translator.get(0);
158
+ const variant = translator.get(1);
159
+ translator.sliceAndSet(2);
160
+ const setCode = Object.entries(mappings_1.SET_MAP).find(([_, value]) => value === set)?.[0];
161
+ const variantCode = Object.entries(mappings_1.VARIANT_MAP).find(([_, value]) => value === variant)?.[0];
162
+ if (!setCode) {
163
+ throw new Error(`Unknown set code: ${set}`);
164
+ }
165
+ for (let j = 0; j < numCards; j++) {
166
+ const cardNumber = translator.PopVarint();
167
+ deck.push({
168
+ cardCode: `${setCode}-${cardNumber.toString().padStart(3, "0")}${variantCode || ""}`,
169
+ count,
170
+ });
171
+ }
172
+ }
173
+ }
174
+ return deck;
175
+ }
176
+ /**
177
+ * Encodes a Riftbound deck into a shareable deck code
178
+ * @param mainDeck - The main deck cards
179
+ * @param sideboard - Optional sideboard cards (defaults to empty array)
180
+ * @returns Base32-encoded deck code string
181
+ * @throws Error if deck format is invalid
182
+ */
183
+ function getCodeFromDeck(mainDeck, sideboard = []) {
184
+ const bytes = [];
185
+ // Write format and version (always version 2)
186
+ bytes.push((FORMAT << 4) | VERSION);
187
+ // Encode main deck (counts 1-12)
188
+ bytes.push(...encodeDeckSection(mainDeck, 12));
189
+ // Encode sideboard (counts 1-3 only, since sideboards can't have runes/battlefields)
190
+ // Empty sideboard will encode as three 0-byte varints (3 bytes total)
191
+ bytes.push(...encodeDeckSection(sideboard, 3));
192
+ return base32Encode(new Uint8Array(bytes));
193
+ }
194
+ /**
195
+ * Decodes a Riftbound deck code into deck and sideboard
196
+ * @param code - Base32-encoded deck code string
197
+ * @returns Object containing mainDeck and sideboard arrays
198
+ * @throws Error if code is invalid or unsupported
199
+ */
200
+ function getDeckFromCode(code) {
201
+ const bytes = base32Decode(code);
202
+ const translator = new VarintTranslator_1.default(bytes);
203
+ // Read format and version
204
+ const formatVersion = translator.get(0);
205
+ translator.sliceAndSet(1);
206
+ const format = (formatVersion >> 4) & 0x0f;
207
+ const version = formatVersion & 0x0f;
208
+ if (format !== FORMAT) {
209
+ throw new Error(`Unsupported format: ${format}. Expected format: ${FORMAT}`);
210
+ }
211
+ if (version > VERSION) {
212
+ throw new Error(`Unsupported version: ${version}. Maximum supported version: ${VERSION}`);
213
+ }
214
+ // Decode main deck (counts 1-12)
215
+ const mainDeck = decodeDeckSection(translator, 12);
216
+ // Decode sideboard (counts 1-3 only)
217
+ // Version 1 codes don't have sideboard section, version 2+ do
218
+ let sideboard = [];
219
+ if (version >= 2) {
220
+ sideboard = decodeDeckSection(translator, 3);
221
+ }
222
+ return {
223
+ mainDeck,
224
+ sideboard,
225
+ };
226
+ }
227
+ //# sourceMappingURL=deckCode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deckCode.js","sourceRoot":"","sources":["../src/deckCode.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { getCodeFromDeck, getDeckFromCode } from "./deckCode";
2
+ export { SET_MAP, VARIANT_MAP } from "./mappings";
3
+ export type { Card, Deck, DeckWithSideboard, SetVariantGroup, CountGroup } from "./types";
4
+ export { default as VarintTranslator } from "./VarintTranslator";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAClD,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,iBAAiB,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1F,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.VarintTranslator = exports.VARIANT_MAP = exports.SET_MAP = exports.getDeckFromCode = exports.getCodeFromDeck = void 0;
7
+ var deckCode_1 = require("./deckCode");
8
+ Object.defineProperty(exports, "getCodeFromDeck", { enumerable: true, get: function () { return deckCode_1.getCodeFromDeck; } });
9
+ Object.defineProperty(exports, "getDeckFromCode", { enumerable: true, get: function () { return deckCode_1.getDeckFromCode; } });
10
+ var mappings_1 = require("./mappings");
11
+ Object.defineProperty(exports, "SET_MAP", { enumerable: true, get: function () { return mappings_1.SET_MAP; } });
12
+ Object.defineProperty(exports, "VARIANT_MAP", { enumerable: true, get: function () { return mappings_1.VARIANT_MAP; } });
13
+ var VarintTranslator_1 = require("./VarintTranslator");
14
+ Object.defineProperty(exports, "VarintTranslator", { enumerable: true, get: function () { return __importDefault(VarintTranslator_1).default; } });
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAAA,uCAA8D;AAArD,2GAAA,eAAe,OAAA;AAAE,2GAAA,eAAe,OAAA;AACzC,uCAAkD;AAAzC,mGAAA,OAAO,OAAA;AAAE,uGAAA,WAAW,OAAA;AAE7B,uDAAiE;AAAxD,qIAAA,OAAO,OAAoB"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Mapping of set codes to their numeric identifiers
3
+ * Format: SET_CODE -> numeric_id
4
+ */
5
+ export declare const SET_MAP: Record<string, number>;
6
+ /**
7
+ * Mapping of variant codes to their numeric identifiers
8
+ * Format: variant_suffix -> numeric_id
9
+ * Empty string represents the base/default variant
10
+ */
11
+ export declare const VARIANT_MAP: Record<string, number>;
12
+ //# sourceMappingURL=mappings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mappings.d.ts","sourceRoot":"","sources":["../src/mappings.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK1C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK9C,CAAC"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VARIANT_MAP = exports.SET_MAP = void 0;
4
+ /**
5
+ * Mapping of set codes to their numeric identifiers
6
+ * Format: SET_CODE -> numeric_id
7
+ */
8
+ exports.SET_MAP = {
9
+ OGN: 0,
10
+ OGS: 1,
11
+ ARC: 2,
12
+ SFD: 3,
13
+ };
14
+ /**
15
+ * Mapping of variant codes to their numeric identifiers
16
+ * Format: variant_suffix -> numeric_id
17
+ * Empty string represents the base/default variant
18
+ */
19
+ exports.VARIANT_MAP = {
20
+ "": 0, // Base variant (no suffix)
21
+ a: 1,
22
+ s: 2,
23
+ b: 3,
24
+ };
25
+ //# sourceMappingURL=mappings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mappings.js","sourceRoot":"","sources":["../src/mappings.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACU,QAAA,OAAO,GAA2B;IAC7C,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;CACP,CAAC;AAEF;;;;GAIG;AACU,QAAA,WAAW,GAA2B;IACjD,EAAE,EAAE,CAAC,EAAE,2BAA2B;IAClC,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;CACL,CAAC"}
@@ -0,0 +1,18 @@
1
+ export interface Card {
2
+ cardCode: string;
3
+ count: number;
4
+ }
5
+ export type Deck = Card[];
6
+ export interface DeckWithSideboard {
7
+ mainDeck: Deck;
8
+ sideboard: Deck;
9
+ }
10
+ export interface SetVariantGroup {
11
+ set: number;
12
+ variant: number;
13
+ cardNumbers: string[];
14
+ }
15
+ export interface CountGroup {
16
+ setVariantGroups: SetVariantGroup[];
17
+ }
18
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,IAAI;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,IAAI,GAAG,IAAI,EAAE,CAAC;AAE1B,MAAM,WAAW,iBAAiB;IACjC,QAAQ,EAAE,IAAI,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IAC1B,gBAAgB,EAAE,eAAe,EAAE,CAAC;CACpC"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@piltoverarchive/riftbound-deck-codes",
3
+ "version": "1.0.0",
4
+ "description": "Encode and decode Riftbound TCG decks to/from shareable strings",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "README.md",
10
+ "LICENSE",
11
+ "CHANGELOG.md"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "test": "echo \"Error: no test specified\" && exit 1",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "keywords": [
19
+ "riftbound",
20
+ "tcg",
21
+ "deck",
22
+ "encoder",
23
+ "decoder",
24
+ "league-of-legends",
25
+ "trading-card-game"
26
+ ],
27
+ "author": "Piltover Archive",
28
+ "license": "Apache-2.0",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/Piltover-Archive/RiftboundDeckCodes.git"
32
+ },
33
+ "bugs": {
34
+ "url": "https://github.com/Piltover-Archive/RiftboundDeckCodes/issues"
35
+ },
36
+ "homepage": "https://piltoverarchive.com",
37
+ "devDependencies": {
38
+ "typescript": "^5.8.0"
39
+ },
40
+ "engines": {
41
+ "node": ">=16.0.0"
42
+ }
43
+ }