Refactor lots of things

This commit is contained in:
Lordmau5 2023-11-15 19:25:42 +01:00
parent e0b05b3aa6
commit 8f714021e0
12 changed files with 321 additions and 257 deletions

View File

@ -122,9 +122,9 @@
import type {
BingoGame
} from '@/js/Bingo.js';
import {
games
} from '@/js/ParseGamesJSON.js';
// import {
// games
// } from '@/js/ParseGamesJSON.js';
@DComponent
export default class EditorComponent extends Vue {

View File

@ -1,15 +1,11 @@
// Example code to convert the old format to the new one
import json from './games.json';
import new_json from './new_games.json';
import {
instanceToPlain, plainToClass, plainToInstance
} from 'class-transformer';
import markdown_text from '@/js/testmarkdown.js';
import BingoGame from '@/js/lib/BingoGame.ts';
import BingoGroup from '@/js/lib/BingoGroup.ts';
import BingoGoal from '@/js/lib/BingoGoal.ts';
import BingoCategory from '@/js/lib/BingoCategory.ts';
import BingoGoalList from '@/js/lib/BingoGoalList.ts';
export const games: Map<string, BingoGame> = new Map;
@ -23,28 +19,29 @@ function run_old_format() {
);
for (const j_cat of j_game.categories) {
const category = new BingoGroup(j_cat.category_name);
game.addGroup(category);
const category = new BingoCategory(j_cat.category_name);
game.addItem(category);
for (const j_group of j_cat.groups) {
const group = new BingoGroup(j_group.group_name, category);
game.addGroup(group);
const goal_list = new BingoGoalList(j_group.group_name);
category.addGoalList(goal_list);
for (const j_goal of j_group.options) {
const goal = new BingoGoal(j_goal.title, group);
const goal = new BingoGoal(j_goal.title);
goal_list.addGoal(goal);
goal.addTag(j_goal.difficulty);
game.addGoal(goal);
}
}
}
games.set(game.name, game);
for (let i = 0; i < 3; i++)
games.set(`${ game.name }-${ i }`, game);
// for (let i = 0; i < 3; i++)
// games.set(`${ game.name }-${ i }`, game);
}
console.log('games', games);
// const game = games.get('Yakuza 0');
// console.log(instanceToPlain(game));
// console.log(JSON.stringify(instanceToPlain(game)));
@ -58,51 +55,4 @@ function run_old_format() {
// console.log(goals);
}
function run_new_format() {
for (const j_game of new_json) {
const game = new BingoGame(
j_game.id,
j_game.name,
j_game.short_description,
j_game.description
);
const group_map: Map<string, BingoGroup> = new Map;
const group_to_group_map: Map<BingoGroup, string> = new Map;
for (const j_group of j_game.groups) {
const group = new BingoGroup(j_group.name);
if (j_group.parent_id)
group_to_group_map.set(group, j_group.parent_id);
group_map.set(j_group.name, group);
game.addGroup(group);
}
for (const [
group,
parent_id
] of group_to_group_map) {
const parent = group_map.get(parent_id);
if (!parent)
continue;
group.parent = parent;
}
for (const j_goal of j_game.goals) {
const group = group_map.get(j_goal.group_id);
if (!group)
continue;
const goal = new BingoGoal(j_goal.name, group);
goal.possible_spaces = j_goal.possible_spaces;
goal.tags = j_goal.tags;
game.addGoal(goal);
}
games.set(game.id, game);
}
}
run_new_format();
run_old_format();

View File

@ -0,0 +1,47 @@
import BingoGoal from '@/js/lib/BingoGoal.ts';
import BingoGoalList from '@/js/lib/BingoGoalList';
import SuccessResponse from '@/js/lib/SuccessResponse.ts';
import {
Type
} from 'class-transformer';
export default class BingoCategory {
name: string;
@Type(() => BingoGoalList)
goal_lists: BingoGoalList[] = [];
constructor(name: string) {
this.name = name;
}
hasGoalList(subgroup: BingoGoalList): boolean {
return this.goal_lists.includes(subgroup);
}
addGoalList(subgroup: BingoGoalList): SuccessResponse {
if (this.hasGoalList(subgroup)) {
return SuccessResponse.error('Group already exists in goal lists');
}
this.goal_lists.push(subgroup);
return SuccessResponse.success();
}
removeGoalList(subgroup: BingoGoalList): SuccessResponse {
this.goal_lists = this.goal_lists.filter(sg => sg !== subgroup);
return SuccessResponse.success();
}
getAllGoals(): BingoGoal[] {
const goals: BingoGoal[] = [];
this.goal_lists.map(subgroup => {
goals.push(...subgroup.goals);
});
return goals;
}
}

View File

@ -1,13 +1,15 @@
import BingoCategory from '@/js/lib/BingoCategory.ts';
import BingoGoal from '@/js/lib/BingoGoal.ts';
import BingoGroup from '@/js/lib/BingoGroup.ts';
import type SuccessResponse from '@/js/lib/SuccessResponse.ts';
import BingoGoalList from '@/js/lib/BingoGoalList.ts';
import SuccessResponse from '@/js/lib/SuccessResponse.ts';
import Util from '@/js/lib/Util.ts';
import {
Type,
instanceToPlain
Transform, plainToInstance, type TransformFnParams
} from 'class-transformer';
type BingoCategoryOrGoalList = BingoCategory | BingoGoalList;
export default class BingoGame {
id: string;
@ -17,14 +19,21 @@ export default class BingoGame {
description: string;
// Funny conversion to allow for both BingoCategory and BingoGoalList
@Transform((params: TransformFnParams) => {
return params.value.map((value: any) => {
if (value?.goal_lists || value instanceof BingoCategory) {
return plainToInstance(BingoCategory, value);
}
else if (value?.goals || value instanceof BingoGoalList) {
return plainToInstance(BingoGoalList, value);
}
});
})
items: BingoCategoryOrGoalList[] = [];
generator: 'simple' | 'srl_v5' | 'srl_v8' = 'simple';
@Type(() => BingoGoal)
goals: BingoGoal[] = [];
@Type(() => BingoGroup)
groups: BingoGroup[] = [];
constructor(
id: string,
name: string,
@ -37,131 +46,71 @@ export default class BingoGame {
this.description = description;
}
addGoal(goal: BingoGoal): SuccessResponse {
const existingGoal = this.goals.find(g => g === goal);
if (existingGoal) {
return Util.returnSuccess(false, 'A goal with this name already exists');
}
else {
this.goals.push(goal);
return Util.returnSuccess(true);
}
}
removeGoal(goal: BingoGoal): SuccessResponse {
const filteredGoals = this.goals.filter(g => g !== goal);
if (filteredGoals.length === this.goals.length) {
return Util.returnSuccess(false, 'Goal not found');
}
else {
this.goals = filteredGoals;
return Util.returnSuccess(true);
}
}
removeGoalByName(name: string): SuccessResponse {
const goal = this.goals.find(g => g.name === name);
if (!goal) {
return Util.returnSuccess(false, 'Goal not found');
}
return this.removeGoal(goal);
}
addGroup(group: BingoGroup): SuccessResponse {
const existingGroup = this.groups.find(g => g === group);
addItem(group: BingoCategoryOrGoalList): SuccessResponse {
const existingGroup = this.items.find(g => g === group);
if (existingGroup) {
return Util.returnSuccess(false, 'A group with this name already exists');
return SuccessResponse.error('This group already exists');
}
else {
this.groups.push(group);
this.items.push(group);
return Util.returnSuccess(true);
return SuccessResponse.success();
}
}
removeGroup(group: BingoGroup): SuccessResponse {
const filteredGroups = this.groups.filter(g => g !== group);
if (filteredGroups.length === this.groups.length) {
return Util.returnSuccess(false, 'Group not found');
removeItem(group: BingoCategoryOrGoalList): SuccessResponse {
if (group instanceof BingoGoalList) {
if (group.goals.length) {
return SuccessResponse.error('Cannot remove a group that has goals');
}
}
else if (group instanceof BingoCategory) {
if (group.goal_lists.length) {
return SuccessResponse.error('Cannot remove a group that has goal lists');
}
}
const filteredGroups = this.items.filter(g => g !== group);
if (filteredGroups.length === this.items.length) {
return SuccessResponse.error('Group not found');
}
else {
// Check if any group has the given group as its parent
const hasParent = this.groups.some(g => g.parent === group);
if (hasParent) {
// Add the group back to the list of groups
this.groups.push(group);
this.items = filteredGroups;
return Util.returnSuccess(false, 'Cannot delete group as it is a parent of another group');
}
else {
this.groups = filteredGroups;
return Util.returnSuccess(true);
}
return SuccessResponse.success();
}
}
removeGroupByName(name: string): SuccessResponse {
const group = this.groups.find(g => g.name === name);
removeItemByName(name: string): SuccessResponse {
const group = this.items.find(
g => Util.stringCompare(g.name, name)
);
if (!group) {
return Util.returnSuccess(false, 'Group not found');
return SuccessResponse.error('Category not found');
}
return this.removeGroup(group);
return this.removeItem(group);
}
getGoalsByTags(...tags: string[]): BingoGoal[] {
return this.goals.filter(goal =>
tags.some(
tag => goal.tags.some(
goalTag => Util.stringCompare(goalTag, tag)
const goals: BingoGoal[] = [];
this.items.map(group => {
const goal_lists = group instanceof BingoGoalList ? [ group ] : group.goal_lists;
goal_lists.map(subgroup => {
subgroup.goals.map(goal => {
if (goal.tags.some(
tag => tags.some(
inputTag => Util.stringCompare(tag, inputTag)
)
));
}
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);
)) {
goals.push(goal);
}
});
return result;
}
toJSON(): Record<string, any> {
this.groups.sort((a, b) => {
if (!a.parent && b.parent) {
return -1;
}
if (a.parent && !b.parent) {
return 1;
}
return 0;
});
});
return instanceToPlain(this);
return goals;
}
}

View File

@ -1,46 +1,28 @@
import type SuccessResponse from '@/js/lib/SuccessResponse.js';
import BingoGroup from './BingoGroup';
import SuccessResponse from '@/js/lib/SuccessResponse.js';
import Util from './Util';
import {
Exclude, Type, Expose
} from 'class-transformer';
export default class BingoGoal {
name: string;
@Exclude({
toPlainOnly: true
})
@Type(() => BingoGroup)
group: BingoGroup;
tags: string[] = [];
tags: string[];
possible_spaces: number[] = [];
possible_spaces: number[];
constructor(name: string, group: BingoGroup) {
constructor(name: string) {
this.name = name;
this.group = group;
this.tags = [];
this.possible_spaces = [];
}
@Expose()
get group_id(): string {
return this.group.name;
}
addTag(tag: string): SuccessResponse {
if (this.tags.some(
existingTag => Util.stringCompare(existingTag, tag)
)) {
return Util.returnSuccess(false, 'Tag already exists');
return SuccessResponse.error('Tag already exists');
}
else {
this.tags.push(tag);
return Util.returnSuccess(true);
return SuccessResponse.success();
}
}
@ -48,14 +30,14 @@ export default class BingoGoal {
if (!this.tags.some(
existingTag => Util.stringCompare(existingTag, tag)
)) {
return Util.returnSuccess(false, 'Tag doesn\'t exist');
return SuccessResponse.error('Tag doesn\'t exist');
}
else {
this.tags = this.tags.filter(
existingTag => !Util.stringCompare(existingTag, tag)
);
return Util.returnSuccess(true);
return SuccessResponse.success();
}
}
}

View File

@ -0,0 +1,24 @@
import BingoGoal from '@/js/lib/BingoGoal.ts';
import {
Type
} from 'class-transformer';
export default class BingoGoalList {
name: string;
@Type(() => BingoGoal)
goals: BingoGoal[] = [];
constructor(name: string) {
this.name = name;
}
addGoal(goal: BingoGoal): void {
if (!this.goals.includes(goal))
this.goals.push(goal);
}
removeGoal(goal: BingoGoal): void {
this.goals = this.goals.filter(g => g !== goal);
}
}

View File

@ -1,47 +0,0 @@
import {
Expose, Exclude
} from 'class-transformer';
export default class BingoGroup {
name: string;
#parent?: BingoGroup;
constructor(name: string, parent?: BingoGroup) {
this.name = name;
this.parent = parent;
}
@Exclude()
get parent(): BingoGroup | undefined {
return this.#parent;
}
set parent(newParent: BingoGroup | undefined) {
if (newParent === this) {
throw new Error('A BingoGroup cannot be a parent of itself.');
}
// if (!newParent) {
// this.#parent = undefined;
// return;
// }
let temp = newParent;
while (temp) {
if (temp === this) {
throw new Error('Circular reference detected.');
}
temp = temp.parent;
}
this.#parent = newParent;
}
@Expose()
get parent_id(): string | undefined {
return this.#parent?.name;
}
}

98
src/js/lib/Parser.ts Normal file
View File

@ -0,0 +1,98 @@
import BingoGame from '@/js/lib/BingoGame.ts';
import {
plainToInstance, instanceToPlain
} from 'class-transformer';
export default class Parser {
// add_children(game: BingoGame, children: (BingoGoal | BingoGroup)[]): void {
// for (const child of children) {
// if (child instanceof BingoGroup) {
// const group = new BingoGroup(child.name);
// game.addGroup(group);
// this.add_children(game, child.children);
// }
// if (child instanceof BingoGoal) {
// this.add_children(child, child.children);
// }
// }
// }
// parse_new(input: BingoGame[]) {
// const games: Map<string, BingoGame> = new Map;
// for (const j_game of input) {
// const game = new BingoGame(
// j_game.id,
// j_game.name,
// j_game.short_description,
// j_game.description
// );
// for (const j_group of j_game.groups) {
// this.add_children(j_group)
// const group = new BingoGroup(j_group.name);
// game.addGroup(group);
// const children = this.get_children(j_group.children);
// for (const child of children) {
// if (child instanceof BingoGoal) {
// const goal = new BingoGoal(child.name, group);
// goal.possible_spaces = child.possible_spaces;
// goal.tags = child.tags;
// }
// }
// }
// const group_map: Map<string, BingoGroup> = new Map;
// const group_to_group_map: Map<BingoGroup, string> = new Map;
// for (const j_group of j_game.groups) {
// const group = new BingoGroup(j_group.name);
// if (j_group.parent_id)
// group_to_group_map.set(group, j_group.parent_id);
// group_map.set(j_group.name, group);
// game.addGroup(group);
// }
// for (const [
// group,
// parent_id
// ] of group_to_group_map) {
// const parent = group_map.get(parent_id);
// if (!parent)
// continue;
// group.parent = parent;
// }
// for (const j_goal of j_game.goals) {
// const group = group_map.get(j_goal.group_id);
// if (!group)
// continue;
// const goal = new BingoGoal(j_goal.name, group);
// goal.possible_spaces = j_goal.possible_spaces;
// goal.tags = j_goal.tags;
// game.addGoal(goal);
// }
// games.set(game.id, game);
// }
// }
static try_parse_neat(input: string): BingoGame {
const json: JSON = JSON.parse(input);
const game = plainToInstance(BingoGame, json);
return game;
}
static instance_to_plain(input: BingoGame): Record<string, any> {
const json = instanceToPlain(input);
return json;
}
}

View File

@ -18,4 +18,12 @@ export default class SuccessResponse {
get message(): string | undefined {
return this.#message;
}
static success() {
return new SuccessResponse(true);
}
static error(message: string) {
return new SuccessResponse(false, message);
}
}

58
src/js/lib/Test.ts Normal file
View File

@ -0,0 +1,58 @@
import BingoGame from '@/js/lib/BingoGame.ts';
import BingoGoal from '@/js/lib/BingoGoal.ts';
import BingoGoalList from '@/js/lib/BingoGoalList.js';
import Parser from './Parser.js';
import BingoCategory from '@/js/lib/BingoCategory.js';
function test() {
const game = new BingoGame(
'yakuza-0',
'Yakuza 0',
'A silly Yakuza game, the prequel for the series',
'Long description with markdown support'
);
const adventure = new BingoCategory('Adventure');
game.addItem(adventure);
const general_group = new BingoGoalList('General');
game.addItem(general_group);
const talk_to_people = new BingoGoalList('Talk to people');
adventure.addGoalList(talk_to_people);
const goal_easy = new BingoGoal('Talk to people 50 times');
talk_to_people.addGoal(goal_easy);
goal_easy.addTag('Easy');
const goal_normal = new BingoGoal('Talk to people 150 times');
talk_to_people.addGoal(goal_normal);
goal_normal.addTag('Normal');
const goal_hard = new BingoGoal('Talk to people 300 times');
talk_to_people.addGoal(goal_hard);
goal_hard.addTag('Hard');
const goal_general = new BingoGoal('Get $500');
general_group.addGoal(goal_general);
goal_general.addTag('Easy');
// adventure.addChild(goal_easy);
// talk_to_people.addChild(goal_normal);
// something_else.addChild(goal_hard);
// something_else.addChild(talk_to_people);
// console.log(game);
const plain = Parser.instance_to_plain(game);
console.log('plain', plain);
// const plain = instanceToPlain(game);
// const text = JSON.stringify(plain);
// console.log(text);
const parsed: BingoGame = Parser.try_parse_neat(JSON.stringify(plain));
console.log('parsed', parsed);
}
test();

View File

@ -1,13 +1,7 @@
import SuccessResponse from './SuccessResponse';
export default class Util {
static stringCompare(str1: string, str2: string): boolean {
return str1.localeCompare(str2, undefined, {
sensitivity: 'accent'
}) === 0;
}
static returnSuccess(success: boolean, message?: string): SuccessResponse {
return new SuccessResponse(success, message);
}
}

View File

@ -3,5 +3,6 @@
</template>
<script setup lang="ts">
import '@/js/lib/Test.ts';
import '@/js/ParseGamesJSON.ts';
</script>