Skip to content

Commit

Permalink
Bank changes
Browse files Browse the repository at this point in the history
  • Loading branch information
gc committed Sep 11, 2024
1 parent c116108 commit f211cad
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 20 deletions.
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,8 @@ export {
EItem,
EMonster,
};

export * from "./data/itemConstants";
export * from "./structures/Items";
export * from "./meta/types";
export type { default as Monster } from "./structures/Monster";
6 changes: 6 additions & 0 deletions src/simulation/clues/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,10 @@ import Hard from "./Hard";
import Master from "./Master";
import Medium from "./Medium";

export * from "./Beginner";
export * from "./Easy";
export * from "./Elite";
export * from "./Hard";
export * from "./Master";
export * from "./Medium";
export { Beginner, Easy, Elite, Hard, Master, Medium };
85 changes: 68 additions & 17 deletions src/structures/Bank.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,48 @@ const frozenErrorStr = "Tried to mutate a frozen Bank.";

const isValidInteger = (str: string): boolean => /^-?\d+$/.test(str);

type ItemResolvable = Item | string | number;

function isValidBankQuantity(qty: number): boolean {
return typeof qty === "number" && qty >= 1 && Number.isInteger(qty);
}

function sanitizeItemBank(mutSource: ItemBank) {
for (const [key, qty] of Object.entries(mutSource)) {
if (!isValidBankQuantity(qty)) {
delete mutSource[key];
}
const item = Items.get(Number.parseInt(key));
if (!item) {
delete mutSource[key];
}
}
}

export default class Bank {
private map: Map<number, number>;
public frozen = false;

static withSanitizedValues(source: ItemBank | IntKeyBank): Bank {
const mutSource = { ...source };
sanitizeItemBank(mutSource);
return new Bank(mutSource);
}

constructor(initialBank?: IntKeyBank | ItemBank | Bank) {
this.map = this.makeFromInitialBank(initialBank);
}

private resolveItemID(item: Item | string | number): number {
public removeInvalidValues(): Bank {
for (const [key, qty] of this.map.entries()) {
if (!isValidBankQuantity(qty) || !Items.has(key)) {
this.map.delete(key);
}
}
return this;
}

private resolveItemID(item: ItemResolvable): number {
if (typeof item === "number") return item;
if (typeof item === "string") return itemID(item);
return item.id;
Expand Down Expand Up @@ -47,13 +80,13 @@ export default class Bank {
}
}

get bank() {
public toJSON(): ItemBank {
return Object.fromEntries(this.map);
}

public set(item: string | number, quantity: number): this {
public set(item: ItemResolvable, quantity: number): this {
if (this.frozen) throw new Error(frozenErrorStr);
const id = typeof item === "string" ? itemID(item) : item;
const id = this.resolveItemID(item);
this.map.set(id, quantity);
return this;
}
Expand All @@ -64,9 +97,9 @@ export default class Bank {
return this;
}

public amount(item: string | number): number {
const itemIDNum = typeof item === "string" ? itemID(item) : item;
return this.map.get(itemIDNum) ?? 0;
public amount(item: ItemResolvable): number {
const id = this.resolveItemID(item);
return this.map.get(id) ?? 0;
}

public addItem(item: number, quantity = 1): this {
Expand All @@ -79,14 +112,14 @@ export default class Bank {

public removeItem(item: number | string, quantity = 1): this {
if (this.frozen) throw new Error(frozenErrorStr);
const itemIDNum = typeof item === "string" ? itemID(item) : item;
const currentValue = this.map.get(itemIDNum);
const id = this.resolveItemID(item);
const currentValue = this.map.get(id);

if (currentValue === undefined) return this;
if (currentValue - quantity <= 0) {
this.map.delete(itemIDNum);
this.map.delete(id);
} else {
this.map.set(itemIDNum, currentValue - quantity);
this.map.set(id, currentValue - quantity);
}

return this;
Expand Down Expand Up @@ -153,16 +186,10 @@ export default class Bank {
if (item instanceof Bank) {
for (const [itemID, qty] of item.map.entries()) {
this.removeItem(itemID, qty);
if (this.length === 0) break;
}
return this;
}

if (Array.isArray(item)) {
for (const _item of item) this.remove(_item.item, _item.quantity);
return this;
}

this.remove(new Bank(item));
return this;
}
Expand Down Expand Up @@ -272,4 +299,28 @@ export default class Bank {
public difference(otherBank: Bank): Bank {
return this.clone().remove(otherBank).add(otherBank.clone().remove(this));
}

public validate(): string[] {
const errors: string[] = [];
for (const [item, quantity] of this.map.entries()) {
if (typeof quantity !== "number" || quantity < 1 || !Number.isInteger(quantity)) {
errors.push(`Item ${item} has a quantity of ${quantity}`);
}
if (typeof item !== "number" || !item || !Items.get(item)?.id) {
errors.push(`Item ${item} does not exist.`);
}
}
return errors;
}

public validateOrThrow() {
const errors = this.validate();
if (errors.length > 0) {
throw new Error(`Bank validation failed: ${errors.join(", ")}`);
}
}

get itemIDs(): number[] {
return Array.from(this.map.keys());
}
}
160 changes: 158 additions & 2 deletions test/Bank.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { describe, expect, test } from "vitest";
import { describe, expect, it, test } from "vitest";

import { EItem } from "../src";
import type { ItemBank } from "../src/meta/types";
import Bank from "../src/structures/Bank";
import Items from "../src/structures/Items";
import { addItemToBank, itemID, resolveNameBank } from "../src/util";
import { addItemToBank, getItemOrThrow, itemID, resolveNameBank } from "../src/util";

describe("Bank", () => {
test("convert string bank to number bank", () => {
Expand Down Expand Up @@ -178,12 +180,14 @@ describe("Bank", () => {
try {
bank.addItem(itemID("Twisted bow"));
} catch {}
expect(() => bank.removeItem("Twisted bow")).toThrowError();
try {
bank.remove(itemID("Twisted bow"));
} catch {}
try {
bank.multiply(5);
} catch {}

try {
bank.set("Twisted bow", 1000);
} catch {}
Expand Down Expand Up @@ -238,4 +242,156 @@ describe("Bank", () => {
bank.set("Twisted bow", 1);
expect(bank.amount("Twisted bow")).toEqual(1);
});

test("withSanitizedValues", () => {
const badBank: ItemBank = {
[-1]: 1,
1: Number.NaN,
2: Number.POSITIVE_INFINITY,
3: Number.NEGATIVE_INFINITY,
9: "",
5: 1,
} as any as ItemBank;
const bank = Bank.withSanitizedValues(badBank);
expect(bank.length).toEqual(1);
expect(bank.amount(5)).toEqual(1);
});

function badBank() {
return {
[-1]: 1,
1: Number.NaN,
2: Number.POSITIVE_INFINITY,
3: Number.NEGATIVE_INFINITY,
9: "",
5: 1,
} as any as ItemBank;
}

test("removeInvalidValues", () => {
const bank = new Bank(badBank());
bank.removeInvalidValues();
expect(bank.length).toEqual(1);
expect(bank.amount(5)).toEqual(1);
});

it("should validate bad bank", () => {
const bank = new Bank(badBank());
const result = bank.validate();
expect(result.length).toBeGreaterThan(0);
});

it("should validate good bank", () => {
for (const bank of [new Bank(), new Bank().add("Coal", 1)]) {
const result = bank.validate();
expect(result.length).toEqual(0);
}
});

it("should validateOrThrow bad bank", () => {
const bank = new Bank(badBank());
expect(() => bank.validateOrThrow()).toThrow();
});

it("should validateOrThrow good bank", () => {
for (const bank of [new Bank(), new Bank().add("Coal", 1)]) {
expect(() => bank.validateOrThrow()).not.toThrow();
}
});

test("itemIDs", () => {
expect(new Bank().itemIDs).toEqual([]);
expect(new Bank().add("Coal", 1).itemIDs.sort()).toEqual([itemID("Coal")].sort());
expect(new Bank().add("Coal", 1).add("Coal", 1).itemIDs.sort()).toEqual([itemID("Coal")].sort());
expect(new Bank().add("Coal", 1).add("Trout", 1).itemIDs.sort()).toEqual(
[itemID("Coal"), itemID("Trout")].sort(),
);
});

it("clears banks", () => {
expect(new Bank().clear().length).toEqual(0);
for (const bank of [new Bank().add("Coal", 1).add("Trout", 100000), new Bank().add("Coal", 1)]) {
expect(bank.length).toBeGreaterThan(0);
bank.clear();
expect(bank.length).toEqual(0);
}
});

it("doesnt clear bank if frozen", () => {
const bank = new Bank().add("Coal", 1).add("Trout", 100000);
bank.freeze();
expect(bank.length).toBeGreaterThan(0);
expect(() => bank.clear()).toThrow();
expect(bank.length).toBeGreaterThan(0);
});

it("checks amount", () => {
const bank = new Bank().add(itemID("Coal"));
expect(bank.amount("Coal")).toEqual(1);
expect(bank.amount(itemID("Coal"))).toEqual(1);
expect(bank.amount(EItem.COAL)).toEqual(1);
expect(bank.amount(getItemOrThrow("Coal"))).toEqual(1);
});

it("sets and clears items", () => {
const methods = ["Coal", itemID("Coal"), EItem.COAL, getItemOrThrow("Coal")];
for (const setMethod of methods) {
for (const amountMethod of methods) {
const bank = new Bank().set(setMethod, 5).add("Trout", 100000);
expect(bank.amount(amountMethod)).toEqual(5);
bank.clear(setMethod);
expect(bank.amount(amountMethod)).toEqual(0);
expect(bank.amount("Trout")).toEqual(100000);
}
}
});

it("adds itembank", () => {
const bank = new Bank().add("Coal", 100).add("Trout", 100);
const bankToAdd = resolveNameBank({
Coal: 50,
Trout: 50,
});
bank.add(bankToAdd);
expect(bank.amount("Coal")).toEqual(150);
expect(bank.amount("Trout")).toEqual(150);
expect(bank.length).toEqual(2);
});

it("adds namebank", () => {
const bank = new Bank().add("Coal", 100).add("Trout", 100);
const bankToAdd = {
Coal: 50,
Trout: 50,
};
bank.add(bankToAdd);
expect(bank.amount("Coal")).toEqual(150);
expect(bank.amount("Trout")).toEqual(150);
expect(bank.length).toEqual(2);
});

it("throws if adding invalid name", () => {
const bank = new Bank().add("Coal", 100).add("Trout", 100);
const bankToAdd = {
Casdfoal: 50,
};
expect(() => bank.add(bankToAdd)).toThrow();
});

it("removes itembank", () => {
const bank = new Bank().add("Coal", 100).add("Trout", 100);
const bankToRemove = resolveNameBank({
Coal: 50,
Trout: 50,
});
bank.remove(bankToRemove);
expect(bank.amount("Coal")).toEqual(50);
expect(bank.amount("Trout")).toEqual(50);
expect(bank.length).toEqual(2);
});

it("converts to json", () => {
const bank = new Bank().add("Coal", 100).add("Trout", 100);
expect(bank.toJSON()).toEqual(resolveNameBank({ Coal: 100, Trout: 100 }));
});
});
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@
"esModuleInterop": true,
"skipLibCheck": true,
"lib": ["ES2023"]
}
},
"include": ["src/**/*"]
}

0 comments on commit f211cad

Please sign in to comment.