landlord-ts 1.0.2

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.
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+
3
+ // src/simulation-worker.ts
4
+ var import_node_fs = require("fs");
5
+ var import_node_worker_threads = require("worker_threads");
6
+
7
+ // src/card/types.ts
8
+ function isLandKind(kind) {
9
+ return kind === "BasicLand" /* BasicLand */ || kind === "ShockLand" /* ShockLand */ || kind === "CheckLand" /* CheckLand */ || kind === "TapLand" /* TapLand */ || kind === "FastLand" /* FastLand */ || kind === "SlowLand" /* SlowLand */ || kind === "BattleLand" /* BattleLand */ || kind === "TurnLand" /* TurnLand */ || kind === "SurveilLand" /* SurveilLand */ || kind === "BounceLand" /* BounceLand */ || kind === "TriomeLand" /* TriomeLand */ || kind === "CyclingLand" /* CyclingLand */ || kind === "PainLand" /* PainLand */ || kind === "FetchLand" /* FetchLand */ || kind === "CanopyLand" /* CanopyLand */ || kind === "PathwayLand" /* PathwayLand */ || kind === "OtherLand" /* OtherLand */ || kind === "ForcedLand" /* ForcedLand */;
10
+ }
11
+ function isLand(card) {
12
+ return isLandKind(card.kind);
13
+ }
14
+
15
+ // src/card/land-types.ts
16
+ var BasicLandType = {
17
+ Plains: 1 << 0,
18
+ Island: 1 << 1,
19
+ Swamp: 1 << 2,
20
+ Mountain: 1 << 3,
21
+ Forest: 1 << 4
22
+ };
23
+ var TYPE_NAME_TO_BIT = [
24
+ ["plains", BasicLandType.Plains],
25
+ ["island", BasicLandType.Island],
26
+ ["swamp", BasicLandType.Swamp],
27
+ ["mountain", BasicLandType.Mountain],
28
+ ["forest", BasicLandType.Forest]
29
+ ];
30
+
31
+ // src/hand.ts
32
+ function simCardFromCard(card) {
33
+ return {
34
+ hash: card.hash,
35
+ kind: card.kind,
36
+ manaCost: { ...card.manaCost },
37
+ basicLandTypes: card.basicLandTypes ?? 0,
38
+ checkTypes: card.checkTypes ?? 0,
39
+ manaPerTap: card.manaPerTap ?? 1
40
+ };
41
+ }
42
+ function handFromOpeningAndDraws(opening, draws, startingHandSize = 7) {
43
+ const cards = [];
44
+ for (const card of opening) cards.push(simCardFromCard(card));
45
+ for (const card of draws) cards.push(simCardFromCard(card));
46
+ const openingHandSize = opening.length;
47
+ return {
48
+ cards,
49
+ startingHandSize,
50
+ openingHandSize,
51
+ mulliganCount: startingHandSize - openingHandSize
52
+ };
53
+ }
54
+ function handFromMulligan(mulligan, rng, deck, draws) {
55
+ return mulligan.simulateHand(rng, deck, draws);
56
+ }
57
+
58
+ // src/mulligan/types.ts
59
+ function createMulberry32(seed) {
60
+ let t = seed >>> 0;
61
+ return {
62
+ genRange(min, max) {
63
+ t = t + 1831565813 >>> 0;
64
+ let r = Math.imul(t ^ t >>> 15, 1 | t);
65
+ r ^= r + Math.imul(r ^ r >>> 7, 61 | r);
66
+ const u = ((r ^ r >>> 14) >>> 0) / 4294967296;
67
+ return min + Math.floor(u * (max - min));
68
+ }
69
+ };
70
+ }
71
+ function partialShuffle(slice, rng, amount) {
72
+ const n = Math.min(amount, slice.length);
73
+ for (let i = 0; i < n; i++) {
74
+ const j = rng.genRange(i, slice.length);
75
+ const tmp = slice[i];
76
+ slice[i] = slice[j];
77
+ slice[j] = tmp;
78
+ }
79
+ return slice.slice(0, n);
80
+ }
81
+
82
+ // src/mulligan/london.ts
83
+ var STARTING_HAND_SIZE = 7;
84
+ function londonNever() {
85
+ return {
86
+ startingHandSize: STARTING_HAND_SIZE,
87
+ mulliganDownTo: STARTING_HAND_SIZE,
88
+ mulliganOnLands: /* @__PURE__ */ new Set(),
89
+ acceptableHandList: []
90
+ };
91
+ }
92
+ function londonSimulateHand(self, rng, deck, draws) {
93
+ const deckSize = deck.length;
94
+ const startingHandSize = Math.min(self.startingHandSize, deckSize);
95
+ const mulliganDownTo = Math.min(self.mulliganDownTo, startingHandSize);
96
+ const maxMulliganRounds = startingHandSize - mulliganDownTo + 1;
97
+ const cardsToDraw = Math.min(startingHandSize + draws + maxMulliganRounds, deckSize);
98
+ const indexRange = Array.from({ length: deckSize }, (_, i) => i);
99
+ const mustKeepCardIndices = [];
100
+ const seenCardHashes = /* @__PURE__ */ new Set();
101
+ for (let round = 0; round < maxMulliganRounds; round++) {
102
+ const shuffledIndices2 = partialShuffle(indexRange.slice(), rng, cardsToDraw);
103
+ const shuffledDeck2 = shuffledIndices2.map((i) => deck[i]);
104
+ const startingHand = shuffledDeck2.slice(0, startingHandSize);
105
+ const isLastRound = round === maxMulliganRounds - 1;
106
+ let landCount = 0;
107
+ for (const c of startingHand) {
108
+ if (isLand(c)) landCount += 1;
109
+ }
110
+ const sufficientLandCount = !self.mulliganOnLands.has(landCount);
111
+ if (!isLastRound && !sufficientLandCount) continue;
112
+ let foundAcceptableHand = false;
113
+ for (const acceptableHand of self.acceptableHandList) {
114
+ mustKeepCardIndices.length = 0;
115
+ seenCardHashes.clear();
116
+ for (let i = 0; i < startingHand.length; i++) {
117
+ const card = startingHand[i];
118
+ if (seenCardHashes.has(card.hash)) continue;
119
+ if (acceptableHand.has(card.hash)) mustKeepCardIndices.push(i);
120
+ seenCardHashes.add(card.hash);
121
+ }
122
+ foundAcceptableHand = mustKeepCardIndices.length === acceptableHand.size;
123
+ if (foundAcceptableHand) break;
124
+ }
125
+ const disregardFoundAcceptableHand = self.acceptableHandList.length === 0;
126
+ const keep = isLastRound || sufficientLandCount && (disregardFoundAcceptableHand || foundAcceptableHand);
127
+ if (!keep) continue;
128
+ const openingHandSize = startingHandSize - round;
129
+ let landsSaved = 0;
130
+ for (let i = 0; i < startingHand.length; i++) {
131
+ const card = startingHand[i];
132
+ if (!isLandKind(card.kind)) continue;
133
+ const needMoreLands = self.mulliganOnLands.has(landsSaved) && landsSaved < openingHandSize;
134
+ if (needMoreLands) {
135
+ mustKeepCardIndices.push(i);
136
+ landsSaved += 1;
137
+ } else {
138
+ break;
139
+ }
140
+ }
141
+ mustKeepCardIndices.sort((a, b) => a - b);
142
+ {
143
+ let w = 0;
144
+ for (let r = 0; r < mustKeepCardIndices.length; r++) {
145
+ if (r === 0 || mustKeepCardIndices[r] !== mustKeepCardIndices[r - 1]) {
146
+ mustKeepCardIndices[w++] = mustKeepCardIndices[r];
147
+ }
148
+ }
149
+ mustKeepCardIndices.length = w;
150
+ }
151
+ for (let i = 0; i < mustKeepCardIndices.length; i++) {
152
+ const mustKeepI = mustKeepCardIndices[i];
153
+ const tmp = shuffledDeck2[i];
154
+ shuffledDeck2[i] = shuffledDeck2[mustKeepI];
155
+ shuffledDeck2[mustKeepI] = tmp;
156
+ }
157
+ let discardCount = 0;
158
+ for (let i = openingHandSize; i < startingHandSize; i++) {
159
+ const j = cardsToDraw - 1 - discardCount;
160
+ const tmp = shuffledDeck2[i];
161
+ shuffledDeck2[i] = shuffledDeck2[j];
162
+ shuffledDeck2[j] = tmp;
163
+ discardCount += 1;
164
+ }
165
+ return handFromOpeningAndDraws(
166
+ shuffledDeck2.slice(0, openingHandSize),
167
+ shuffledDeck2.slice(openingHandSize),
168
+ startingHandSize
169
+ );
170
+ }
171
+ const shuffledIndices = partialShuffle(indexRange.slice(), rng, cardsToDraw);
172
+ const shuffledDeck = shuffledIndices.map((i) => deck[i]);
173
+ return handFromOpeningAndDraws(
174
+ shuffledDeck.slice(0, startingHandSize),
175
+ shuffledDeck.slice(startingHandSize),
176
+ startingHandSize
177
+ );
178
+ }
179
+ function asLondonMulligan(london) {
180
+ return {
181
+ simulateHand: (rng, deck, draws) => londonSimulateHand(london, rng, deck, draws)
182
+ };
183
+ }
184
+
185
+ // src/simulation-worker.ts
186
+ var payload = import_node_worker_threads.workerData;
187
+ try {
188
+ const london = londonNever();
189
+ london.startingHandSize = payload.startingHandSize;
190
+ london.mulliganDownTo = payload.mulliganDownTo;
191
+ london.mulliganOnLands = new Set(payload.mulliganOnLands);
192
+ london.acceptableHandList = payload.acceptableHandList.map((row) => new Set(row));
193
+ const mulligan = asLondonMulligan(london);
194
+ const rng = createMulberry32(payload.seed);
195
+ const deck = payload.deck;
196
+ const hands = [];
197
+ for (let i = 0; i < payload.runCount; i++) {
198
+ hands.push(handFromMulligan(mulligan, rng, deck, payload.drawCount));
199
+ }
200
+ (0, import_node_fs.writeFileSync)(payload.outFile, JSON.stringify(hands));
201
+ const statuses = new Int32Array(payload.sab);
202
+ Atomics.store(statuses, payload.statusIndex, 1);
203
+ Atomics.notify(statuses, payload.statusIndex);
204
+ } catch {
205
+ const statuses = new Int32Array(payload.sab);
206
+ Atomics.store(statuses, payload.statusIndex, -1);
207
+ Atomics.notify(statuses, payload.statusIndex);
208
+ }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,33 @@
1
+ import {
2
+ asLondonMulligan,
3
+ createMulberry32,
4
+ handFromMulligan,
5
+ londonNever
6
+ } from "./chunk-I6ETCO5F.js";
7
+
8
+ // src/simulation-worker.ts
9
+ import { writeFileSync } from "fs";
10
+ import { workerData } from "worker_threads";
11
+ var payload = workerData;
12
+ try {
13
+ const london = londonNever();
14
+ london.startingHandSize = payload.startingHandSize;
15
+ london.mulliganDownTo = payload.mulliganDownTo;
16
+ london.mulliganOnLands = new Set(payload.mulliganOnLands);
17
+ london.acceptableHandList = payload.acceptableHandList.map((row) => new Set(row));
18
+ const mulligan = asLondonMulligan(london);
19
+ const rng = createMulberry32(payload.seed);
20
+ const deck = payload.deck;
21
+ const hands = [];
22
+ for (let i = 0; i < payload.runCount; i++) {
23
+ hands.push(handFromMulligan(mulligan, rng, deck, payload.drawCount));
24
+ }
25
+ writeFileSync(payload.outFile, JSON.stringify(hands));
26
+ const statuses = new Int32Array(payload.sab);
27
+ Atomics.store(statuses, payload.statusIndex, 1);
28
+ Atomics.notify(statuses, payload.statusIndex);
29
+ } catch {
30
+ const statuses = new Int32Array(payload.sab);
31
+ Atomics.store(statuses, payload.statusIndex, -1);
32
+ Atomics.notify(statuses, payload.statusIndex);
33
+ }
package/package.json ADDED
@@ -0,0 +1,102 @@
1
+ {
2
+ "name": "landlord-ts",
3
+ "version": "1.0.2",
4
+ "description": "Monte Carlo MTG on-curve simulator (TypeScript port of landlord)",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "author": "Lacy Garrison",
8
+ "keywords": [
9
+ "mtg",
10
+ "magic-the-gathering",
11
+ "mana",
12
+ "mulligan",
13
+ "simulation",
14
+ "landlord-ts"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/lggarrison/landlord-ts.git"
19
+ },
20
+ "homepage": "https://github.com/lggarrison/landlord-ts#readme",
21
+ "bugs": {
22
+ "url": "https://github.com/lggarrison/landlord-ts/issues"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "engines": {
28
+ "node": ">=22"
29
+ },
30
+ "main": "./dist/index.cjs",
31
+ "module": "./dist/index.js",
32
+ "types": "./dist/index.d.ts",
33
+ "exports": {
34
+ ".": {
35
+ "types": "./dist/index.d.ts",
36
+ "import": "./dist/index.js",
37
+ "require": "./dist/index.cjs"
38
+ }
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "data",
43
+ "LICENSE",
44
+ "README.md"
45
+ ],
46
+ "scripts": {
47
+ "build": "tsup",
48
+ "test": "vitest run",
49
+ "test:watch": "vitest",
50
+ "lint": "eslint .",
51
+ "lint:fix": "eslint . --fix",
52
+ "format": "prettier --write .",
53
+ "format:check": "prettier --check .",
54
+ "card-update": "tsx scripts/card-update.ts",
55
+ "mine-etb": "tsx scripts/mine-etb.ts",
56
+ "release:check": "npm-run-all format:check lint test build wiki:lint wiki:check",
57
+ "prepare": "node .husky/install.mjs",
58
+ "prepublishOnly": "npm run release:check",
59
+ "wiki:help": "llm-wiki-manager help",
60
+ "wiki:lint": "llm-wiki-manager lint",
61
+ "wiki:build": "llm-wiki-manager build",
62
+ "wiki:check": "llm-wiki-manager check",
63
+ "wiki:sync": "llm-wiki-manager sync",
64
+ "wiki:log": "llm-wiki-manager log",
65
+ "wiki:doctor": "llm-wiki-manager doctor",
66
+ "wiki:setup:husky": "llm-wiki-manager setup-husky"
67
+ },
68
+ "lint-staged": {
69
+ "*.{ts,js,mjs,cjs}": [
70
+ "eslint --fix --no-warn-ignored",
71
+ "prettier --write"
72
+ ],
73
+ "wiki/**/*.md": [
74
+ "npm run wiki:build",
75
+ "npm run wiki:lint",
76
+ "prettier --write"
77
+ ],
78
+ "*.{json,md,yml,yaml}": [
79
+ "prettier --write"
80
+ ]
81
+ },
82
+ "dependencies": {
83
+ "fflate": "^0.8.2"
84
+ },
85
+ "devDependencies": {
86
+ "@eslint/js": "^9.28.0",
87
+ "@types/node": "^22.15.0",
88
+ "eslint": "^10.7.0",
89
+ "eslint-config-prettier": "^10.1.5",
90
+ "globals": "^17.7.0",
91
+ "husky": "^9.1.7",
92
+ "lint-staged": "^17.0.8",
93
+ "llm-wiki-manager": "^1.0.3",
94
+ "npm-run-all2": "^9.0.2",
95
+ "prettier": "^3.5.3",
96
+ "tsup": "^8.5.0",
97
+ "tsx": "^4.19.0",
98
+ "typescript": "^5.8.0",
99
+ "typescript-eslint": "^8.33.0",
100
+ "vitest": "^4.1.10"
101
+ }
102
+ }