The-Bingo-Experience/src/js/Bingo.ts
2023-11-14 23:32:01 +01:00

325 lines
8.1 KiB
TypeScript

import {
Exclude,
Expose,
Type,
instanceToPlain,
plainToClass,
plainToInstance
} from 'class-transformer';
function stringCompare(a: string, b: string) {
return a.localeCompare(b, undefined, {
sensitivity: 'accent'
}) === 0;
}
export class BingoGame {
id!: string;
name!: string;
short_description!: string;
description!: string;
generator: 'simple' | 'srl_v5' | 'srl_v8' = 'simple';
@Type(() => BingoGoal)
goals: BingoGoal[] = [];
@Type(() => BingoGroup)
groups: BingoGroup[] = [];
constructor(id: string, name: string, short_description: string, description: string) {
this.id = id;
this.name = name;
this.short_description = short_description;
this.description = description;
}
addGoal(goal: BingoGoal): void {
if (this.goals.some(_goal => stringCompare(_goal.name, goal.name)))
return;
this.goals.push(goal);
}
removeGoal(goal: BingoGoal): void {
this.goals = this.goals.filter(_goal => !stringCompare(_goal.name, goal.name));
}
removeGoalByName(name: string): void {
const goal = this.goals.find(goal => stringCompare(goal.name, name));
if (goal)
this.removeGoal(goal);
}
addGroup(group: BingoGroup): void {
/*
* TODO: Right now we can do circular groups.
* 1.parent = 2, 2.parent = 1
* This will loop the objects infinitely.
* Make sure that going through the tree we don't have this problem.
*
* Option B:
* Trash idea of groups-in-groups (parent system)
* Just have groups and be done with them.
*
* Option C:
* Bring back categories so we have categories + groups.
*/
if (this.groups.some(_group => stringCompare(_group.name, group.name)))
return;
this.groups.push(group);
}
removeGroup(group: BingoGroup): boolean {
// Group has parent, don't delete
const isParent = this.groups.find(
_group => _group.parent && stringCompare(_group.parent.name, group.name)
);
if (isParent) {
console.error('This group is still a parent of:', isParent.name);
return false;
}
// One or more goals still use this group, don't delete
const isGoal = this.goals.find(
goal => goal.group && stringCompare(goal.group.name, group.name)
);
if (isGoal) {
console.error('This group is still used by:', isGoal.name);
return false;
}
this.groups = this.groups.filter(_group => !stringCompare(_group.name, group.name));
return true;
}
removeGroupByName(name: string): boolean {
const group = this.groups.find(group => stringCompare(group.name, name));
return group ? this.removeGroup(group) : false;
}
getGroupByName(name: string): BingoGroup | undefined {
return this.groups.find(group => stringCompare(group.name, name));
}
getGoalsByTags(...tags: string[]): BingoGoal[] {
return this.goals.filter(
goal => goal.tags.some(
_tag => tags.some(
tag => stringCompare(_tag, tag)
)
)
);
}
getGoalsByGroup(includeParents: boolean, ...groups: BingoGroup[]): BingoGoal[] {
const result: BingoGoal[] = [];
this.goals.map(goal => {
const goal_group = goal.group;
if (includeParents) {
let parent = goal_group.parent;
while (parent) {
if (groups.includes(parent)) {
result.push(goal);
return;
}
parent = parent.parent;
}
}
if (groups.includes(goal_group)) {
result.push(goal);
}
});
return result;
}
}
function getRandomInt(max: number): number {
return Math.floor(Math.random() * max);
}
export class BingoGoal {
// id: string; // uuid-by-string maybe? https://www.npmjs.com/package/uuid-by-string
name!: string;
@Exclude({
toPlainOnly: true
})
@Type(() => BingoGroup)
group!: BingoGroup;
tags: string[] = [];
possible_spaces: number[] = [];
constructor(name: string, group: BingoGroup) {
this.name = name;
this.group = group;
for (let i = 0; i < 25; i++) {
if (getRandomInt(2) == 0)
continue;
this.possible_spaces.push(i);
}
}
@Expose()
get group_id(): string {
return this.group.name;
}
addTag(tag: string): BingoGoal {
if (this.tags.includes(tag))
return this;
this.tags.push(tag);
return this;
}
removeTag(tag: string): BingoGoal {
this.tags = this.tags.filter(_tag => !stringCompare(_tag, tag));
return this;
}
setGroup(group: BingoGroup) {
this.group = group;
}
setPossibleSpaces(possible_spaces: number[]) {
this.possible_spaces = possible_spaces;
}
}
export class BingoGroup {
name: string;
// @Exclude({
// toPlainOnly: true
// })
// @Type(() => BingoGroup)
#parent?: BingoGroup;
constructor(name: string, parent?: BingoGroup | undefined) {
this.name = name;
this.#parent = parent;
}
@Exclude()
get parent(): BingoGroup | undefined {
return this.#parent;
}
@Expose()
get parent_id(): string | undefined {
return this.#parent?.name;
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function test(): void {
const game = new BingoGame(
'yakuza_0',
'Yakuza 0',
'The funny game we used for all bingos.',
'Very long text here haha lmao'
);
const group1 = new BingoGroup('1');
const group2 = new BingoGroup('2', group1);
// group1.parent = group2;
// group2.parent = group1;
game.addGroup(group1);
game.addGroup(group2);
const jsonifiedInstance = instanceToPlain(game, {
enableCircularCheck: true
});
const fixed = JSON.parse(JSON.stringify(jsonifiedInstance));
console.log(JSON.stringify([ fixed ]));
// const group1 = new BingoGroup('1');
// const group2 = new BingoGroup('2', group1);
// const group3 = new BingoGroup('3', group2);
// game.addGroup(group1);
// game.addGroup(group2);
// game.addGroup(group3);
// const battle_group = new BingoGroup('Battle');
// const defeat_enemies = new BingoGroup('Defeat enemies on the street', battle_group);
// game.addGroup(battle_group);
// game.addGroup(defeat_enemies);
// const easy = new BingoGoal('Defeat 50 enemies on the street', group1).addTag('Easy');
// const normal = new BingoGoal('Defeat 150 enemies on the street', group2).addTag('Normal');
// const hard = new BingoGoal('Defeat 300 enemies on the street', group3).addTag('Hard');
// game.addGoal(easy);
// game.addGoal(normal);
// game.addGoal(hard);
// console.log(game.goals[0].group);
// game.groups[0].name = 'not 1';
// game.groups[0].parent = game.groups[1];
// console.log(game.goals[0].group);
// const jsonifiedInstance = instanceToPlain(game);
// const fixed = JSON.parse(JSON.stringify(jsonifiedInstance));
// console.log(JSON.stringify([ fixed ]));
// console.log(JSON.stringify(game));
// const got_group1 = game.getGroupByName('1');
// console.log(got_group1);
// const got_group3 = game.getGroupByName('3');
// console.log(got_group3);
// console.log(got_group1 === got_group3?.parent?.parent);
// const jsonifiedString = JSON.stringify(jsonifiedInstance);
// const parsedInstance: JSON = JSON.parse(jsonifiedString);
// const parsedgame = plainToClass(BingoGame, parsedInstance);
// console.log(parsedgame);
// console.log(parsedgame === game);
// const got_group1 = parsedgame.getGroupByName('1');
// console.log(got_group1);
// const got_group3 = parsedgame.getGroupByName('3');
// console.log(got_group3);
// console.log(got_group1 === got_group3?.parent?.parent);
// const json_string = '{"id":"yakuza_0","name":"Yakuza 0","description":"The funny game we used for all bingos.","generator":"simple","goals":[{"name":"Defeat 50 enemies on the street","tags":["Easy"],"group":{"name":"Defeat enemies on the street","parent":{"name":"Battle"}},"possible_spaces":[]},{"name":"Defeat 150 enemies on the street","tags":["Normal"],"group":{"name":"Defeat enemies on the street","parent":{"name":"Battle"}},"possible_spaces":[]},{"name":"Defeat 300 enemies on the street","tags":["Hard"],"group":{"name":"Defeat enemies on the street","parent":{"name":"Battle"}},"possible_spaces":[]}],"groups":[{"name":"Battle"},{"name":"Defeat enemies on the street","parent":{"name":"Battle"}}]}';
// const parsed: JSON = JSON.parse(json_string);
// const parsed_game = plainToInstance(BingoGame, parsed);
// console.log(parsed_game);
// console.log(JSON.stringify(other_game) === JSON.stringify(jsonifiedInstance));
// parsed_game.removeGroup(defeat_enemies);
// console.log(parsed_game.goals);
}
test();