diff --git a/src/components/EditorComponent.vue b/src/components/EditorComponent.vue index 920fc85..feedbf3 100644 --- a/src/components/EditorComponent.vue +++ b/src/components/EditorComponent.vue @@ -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 { diff --git a/src/js/ParseGamesJSON.ts b/src/js/ParseGamesJSON.ts index ed2d814..12dcdb0 100644 --- a/src/js/ParseGamesJSON.ts +++ b/src/js/ParseGamesJSON.ts @@ -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 = 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 = new Map; - const group_to_group_map: Map = 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(); diff --git a/src/js/lib/BingoCategory.ts b/src/js/lib/BingoCategory.ts new file mode 100644 index 0000000..df1026b --- /dev/null +++ b/src/js/lib/BingoCategory.ts @@ -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; + } +} diff --git a/src/js/lib/BingoGame.ts b/src/js/lib/BingoGame.ts index 8750d4d..b8cbf87 100644 --- a/src/js/lib/BingoGame.ts +++ b/src/js/lib/BingoGame.ts @@ -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[] = []; - getGoalsByGroup(includeParents: boolean, ...groups: BingoGroup[]): BingoGoal[] { - const result: BingoGoal[] = []; + this.items.map(group => { + const goal_lists = group instanceof BingoGoalList ? [ group ] : group.goal_lists; - 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; + goal_lists.map(subgroup => { + subgroup.goals.map(goal => { + if (goal.tags.some( + tag => tags.some( + inputTag => Util.stringCompare(tag, inputTag) + ) + )) { + goals.push(goal); } - - parent = parent.parent; - } - } - - if (groups.includes(goal_group)) { - result.push(goal); - } + }); + }); }); - return result; - } - - toJSON(): Record { - 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; } } diff --git a/src/js/lib/BingoGoal.ts b/src/js/lib/BingoGoal.ts index 2860c94..bced002 100644 --- a/src/js/lib/BingoGoal.ts +++ b/src/js/lib/BingoGoal.ts @@ -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(); } } } diff --git a/src/js/lib/BingoGoalList.ts b/src/js/lib/BingoGoalList.ts new file mode 100644 index 0000000..3ee6738 --- /dev/null +++ b/src/js/lib/BingoGoalList.ts @@ -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); + } +} diff --git a/src/js/lib/BingoGroup.ts b/src/js/lib/BingoGroup.ts deleted file mode 100644 index e6320a9..0000000 --- a/src/js/lib/BingoGroup.ts +++ /dev/null @@ -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; - } -} - diff --git a/src/js/lib/Parser.ts b/src/js/lib/Parser.ts new file mode 100644 index 0000000..d670f7b --- /dev/null +++ b/src/js/lib/Parser.ts @@ -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 = 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 = new Map; + // const group_to_group_map: Map = 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 { + const json = instanceToPlain(input); + + return json; + } +} diff --git a/src/js/lib/SuccessResponse.ts b/src/js/lib/SuccessResponse.ts index 017e1a0..8b29172 100644 --- a/src/js/lib/SuccessResponse.ts +++ b/src/js/lib/SuccessResponse.ts @@ -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); + } } diff --git a/src/js/lib/Test.ts b/src/js/lib/Test.ts new file mode 100644 index 0000000..7670dbe --- /dev/null +++ b/src/js/lib/Test.ts @@ -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(); diff --git a/src/js/lib/Util.ts b/src/js/lib/Util.ts index 5d5a400..e3672f6 100644 --- a/src/js/lib/Util.ts +++ b/src/js/lib/Util.ts @@ -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); - } } diff --git a/src/views/MainPage.vue b/src/views/MainPage.vue index d0fa972..8e865b5 100644 --- a/src/views/MainPage.vue +++ b/src/views/MainPage.vue @@ -3,5 +3,6 @@