diff --git a/e2e/game-model/gamesystems/productGamesystems/CreateProductStates.spec.ts b/e2e/game-model/gamesystems/productGamesystems/CreateProductStates.spec.ts new file mode 100644 index 0000000..3ee77ef --- /dev/null +++ b/e2e/game-model/gamesystems/productGamesystems/CreateProductStates.spec.ts @@ -0,0 +1,48 @@ +import { BrowserContext, ElectronApplication, Page, _electron as electron } from 'playwright'; +import { test, expect } from '@playwright/test'; +import * as PATH from 'path'; +import {GameModel} from "../../../src/app/game-model/GameModel"; +import {Gamesystem} from "../../src/app/game-model/gamesystems/Gamesystem"; +import {ScriptAccount} from "../../../src/app/game-model/scriptAccounts/ScriptAccount"; +import {ModelComponentType} from "../../../src/app/game-model/ModelComponentType"; +import {SimpleGamesystem} from "../../../src/app/game-model/gamesystems/SimpleGamesystem"; +import exp = require("node:constants"); +import {end} from "electron-debug"; +import {GamesystemTrainer} from "./GamesystemTrainer"; +import {ProductGamesystem} from "../../../src/app/game-model/gamesystems/ProductGamesystem"; +import {ProductStateTrainer} from "./ProductStateTrainer"; +import {SimpleState} from "../../../../src/app/game-model/gamesystems/SimpleState"; +test.describe('Test Create ProductStates', () => { + + test("Adding already existent ProductState", async () => { + const gamesystem = ProductStateTrainer.givenFullProductGamesystemWithTwoStates(); + const result = gamesystem.createState(gamesystem.states[0].innerStates); + expect(result).toBeUndefined(); + expect(gamesystem.states.length).toEqual(4); + }) + + test("Test empty inputs for ProductState Creation", async ()=> { + const gamesystem = ProductStateTrainer.givenFullProductGamesystemWithTwoStates(); + expect(gamesystem.createState([])).toBeUndefined(); + expect(gamesystem.states.length).toEqual(4); + }) + + test("Test invalid inputs for ProductState Creation", async () => { + const gamesystem = ProductStateTrainer.givenFullProductGamesystemWithTwoStates(); + expect(gamesystem.createState([ProductStateTrainer.givenUnusedInnerStates(gamesystem)[0]])).toBeUndefined(); + expect(gamesystem.states.length).toEqual(4); + }) + + test("Test ProductState Creation with foreign State", async () => { + const gamesystem = ProductStateTrainer.givenFullProductGamesystemWithTwoStates(); + const simpleState = new SimpleState(ProductStateTrainer.INNERSTATE_LETTER_3); + expect(gamesystem.createState([simpleState])).toBeUndefined(); + expect(gamesystem.states.length).toEqual(4); + }) + + test("Test valid ProductStateCreation", async () => { + const gamesystem = ProductStateTrainer.givenFullProductGamesystemWithTwoStates(); + expect(gamesystem.createState(ProductStateTrainer.givenUnusedInnerStates(gamesystem))).toBeDefined(); + expect(gamesystem.states.length).toEqual(5); + }) +}); diff --git a/e2e/game-model/gamesystems/productGamesystems/EqualInnerStates.spec.ts b/e2e/game-model/gamesystems/productGamesystems/EqualInnerStates.spec.ts new file mode 100644 index 0000000..1b6415e --- /dev/null +++ b/e2e/game-model/gamesystems/productGamesystems/EqualInnerStates.spec.ts @@ -0,0 +1,37 @@ +import { BrowserContext, ElectronApplication, Page, _electron as electron } from 'playwright'; +import { test, expect } from '@playwright/test'; +import * as PATH from 'path'; +import {GameModel} from "../../../src/app/game-model/GameModel"; +import {Gamesystem} from "../../src/app/game-model/gamesystems/Gamesystem"; +import {ScriptAccount} from "../../../src/app/game-model/scriptAccounts/ScriptAccount"; +import {ModelComponentType} from "../../../src/app/game-model/ModelComponentType"; +import {SimpleGamesystem} from "../../../src/app/game-model/gamesystems/SimpleGamesystem"; +import exp = require("node:constants"); +import {end} from "electron-debug"; +import {GamesystemTrainer} from "./GamesystemTrainer"; +import {ProductGamesystem} from "../../../src/app/game-model/gamesystems/ProductGamesystem"; +import {ProductStateTrainer} from "./ProductStateTrainer"; +import {SimpleState} from "../../../../src/app/game-model/gamesystems/SimpleState"; +test.describe('Test Check Equal of Innerstates', () => { + + test("Test invalid input for equal checking", async()=> { + const gamesystem = ProductStateTrainer.givenFullProductGamesystemWithTwoStates(); + expect(gamesystem.states[0].equalInnerStates(null)).toBeFalsy(); + expect(gamesystem.states[0].equalInnerStates(undefined)).toBeFalsy(); + }) + + test("Test empty input for equal checking", async ()=> { + const gamesystem = ProductStateTrainer.givenFullProductGamesystemWithTwoStates(); + expect(gamesystem.states[0].equalInnerStates([])).toBeFalsy(); + }) + + test("Test identical inner states for equal", async ()=> { + const gamesystem = ProductStateTrainer.givenFullProductGamesystemWithTwoStates(); + expect(gamesystem.states[0].equalInnerStates(gamesystem.states[0].innerStates)).toBeTruthy(); + }) + + test("Test slightly derivating inner states for Equal", async ()=> { + const gamesystem = ProductStateTrainer.givenFullProductGamesystemWithTwoStates(); + expect(gamesystem.states[0].equalInnerStates(gamesystem.states[1].innerStates)).toBeFalsy(); + }) +}); diff --git a/e2e/game-model/gamesystems/productGamesystems/ProductStateTrainer.ts b/e2e/game-model/gamesystems/productGamesystems/ProductStateTrainer.ts new file mode 100644 index 0000000..9c55c46 --- /dev/null +++ b/e2e/game-model/gamesystems/productGamesystems/ProductStateTrainer.ts @@ -0,0 +1,48 @@ +import {ProductGamesystem} from "../../../../src/app/game-model/gamesystems/ProductGamesystem"; +import {SimpleGamesystem} from "../../../../src/app/game-model/gamesystems/SimpleGamesystem"; +import {ProductState} from "../../../../src/app/game-model/gamesystems/ProductState"; + +export class ProductStateTrainer { + static INNERSTATE_LETTER_1 = "A"; + static INNERSTATE_LETTER_2 = "B"; + static INNERSTATE_LETTER_3 = "C"; + static INNERSTATE_NUMBER_1 = "1"; + static INNERSTATE_NUMBER_2 = "2"; + static INNERSTATE_NUMBER_3 = "3"; + + static LETTERS_GAMESYSTEM_NAME = "Letters"; + static NUMBERS_GAMESYSTEM_NAME = "Numbers"; + static PRODUCT_GAMESYSTEM_NAME = "Product Gamesystem"; + + static givenFullProductGamesystemWithTwoStates() { + const letter_Gamesystem = new SimpleGamesystem(this.LETTERS_GAMESYSTEM_NAME); + const letter_1 = letter_Gamesystem.createState(this.INNERSTATE_LETTER_1, "")!; + const letter_2 = letter_Gamesystem.createState(this.INNERSTATE_LETTER_2, "")!; + const number_gamesystem = new SimpleGamesystem(this.NUMBERS_GAMESYSTEM_NAME); + const number_1 = number_gamesystem.createState(this.INNERSTATE_NUMBER_1, "")!; + const number_2 = number_gamesystem.createState(this.INNERSTATE_NUMBER_2, "")!; + const productGamesystem = new ProductGamesystem(this.PRODUCT_GAMESYSTEM_NAME); + + productGamesystem.states.push(new ProductState( [letter_1, number_1])); + productGamesystem.states.push(new ProductState( [letter_1, number_2])); + productGamesystem.states.push(new ProductState( [letter_2, number_1])); + productGamesystem.states.push(new ProductState( [letter_2, number_2])); + + productGamesystem.innerGamesystems.push(letter_Gamesystem); + productGamesystem.innerGamesystems.push(number_gamesystem); + + return productGamesystem; + + } + + static givenUnusedInnerStates(productGamesystem: ProductGamesystem) { + const letterGamesystem = productGamesystem.findChildSystemByName(ProductStateTrainer.LETTERS_GAMESYSTEM_NAME) as SimpleGamesystem; + const numberGamesystem = productGamesystem.findChildSystemByName(ProductStateTrainer.NUMBERS_GAMESYSTEM_NAME) as SimpleGamesystem; + + const letterGamesystemState = letterGamesystem.createState(ProductStateTrainer.INNERSTATE_LETTER_3)!; + const numberGamesystemState = numberGamesystem.createState(ProductStateTrainer.INNERSTATE_NUMBER_3)!; + + return [letterGamesystemState, numberGamesystemState]; + } + +} diff --git a/src/app/app.component.ts b/src/app/app.component.ts index c23a46e..9218ceb 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -15,6 +15,8 @@ import {DeleteConfirmationDialogComponent} from "./delete-confirmation-dialog/de import {ScriptAccount} from "./game-model/scriptAccounts/ScriptAccount"; import {GamescriptOverviewComponent} from "./side-overviews/gamescript-overview/gamescript-overview.component"; import {SimpleGamesystem} from "./game-model/gamesystems/SimpleGamesystem"; +import {ProductGamesystem} from "./game-model/gamesystems/ProductGamesystem"; +import {ProductState} from "./game-model/gamesystems/ProductState"; @Component({ selector: 'app-root', @@ -150,10 +152,19 @@ export class AppComponent implements OnInit{ const springState = season.createState("Spring", "Spring, also known as springtime, is one of the four temperate seasons, succeeding winter and preceding summer."); const summerState = season.createState("Summer", "Summer is the hottest and brightest of the four temperate seasons, occurring after spring and before autumn. "); - season.createTransition(springState!, summerState!); + const sunnyState = weather.createState("Sunny", "The sun is shining. No clouds, no rain, no storm."); + const rainingState = weather.createState("Raining", "It rains") - this.gameModel.addGamesystem(weather) - this.gameModel.addGamesystem(season); + season.createTransition(springState!, summerState!); + weather.createTransition(sunnyState!, rainingState!); + + const weather_season = new ProductGamesystem("Weather-Season"); + weather_season.addChildGamesystem(weather); + weather_season.addChildGamesystem(season); + + weather_season.createState(ProductState.computeProductStateLabel([springState!, sunnyState!]), "", [springState!, sunnyState!]); + + this.gameModel.addGamesystem(weather_season); } openScriptAccountsOverview() { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 4461bfd..9a9cb35 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -51,6 +51,9 @@ import { SimpleTransitionEditorComponent } from "./editor/gamesystem-editor/transition-editor/simple-transition-editor/simple-transition-editor.component"; import {MatOption, MatSelect} from "@angular/material/select"; +import { + ProductGamesystemEditorComponent +} from "./editor/gamesystem-editor/product-gamesystem-editor/product-gamesystem-editor.component"; // AoT requires an exported function for factories const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new TranslateHttpLoader(http, './assets/i18n/', '.json'); @@ -121,7 +124,8 @@ const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new Transl MatCheckbox, MatSelect, MatOption, - MatHint + MatHint, + ProductGamesystemEditorComponent ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/editor/gamesystem-editor/gamesystem-editor.component.html b/src/app/editor/gamesystem-editor/gamesystem-editor.component.html index 49bfe84..84988b6 100644 --- a/src/app/editor/gamesystem-editor/gamesystem-editor.component.html +++ b/src/app/editor/gamesystem-editor/gamesystem-editor.component.html @@ -1 +1,2 @@ + diff --git a/src/app/editor/gamesystem-editor/product-gamesystem-editor/product-gamesystem-editor.component.html b/src/app/editor/gamesystem-editor/product-gamesystem-editor/product-gamesystem-editor.component.html new file mode 100644 index 0000000..65a72dd --- /dev/null +++ b/src/app/editor/gamesystem-editor/product-gamesystem-editor/product-gamesystem-editor.component.html @@ -0,0 +1 @@ + diff --git a/src/app/editor/gamesystem-editor/product-gamesystem-editor/product-gamesystem-editor.component.scss b/src/app/editor/gamesystem-editor/product-gamesystem-editor/product-gamesystem-editor.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/editor/gamesystem-editor/product-gamesystem-editor/product-gamesystem-editor.component.spec.ts b/src/app/editor/gamesystem-editor/product-gamesystem-editor/product-gamesystem-editor.component.spec.ts new file mode 100644 index 0000000..659bfd4 --- /dev/null +++ b/src/app/editor/gamesystem-editor/product-gamesystem-editor/product-gamesystem-editor.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ProductGamesystemEditorComponent } from './product-gamesystem-editor.component'; + +describe('ProductGamesystemEditorComponent', () => { + let component: ProductGamesystemEditorComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ProductGamesystemEditorComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ProductGamesystemEditorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/editor/gamesystem-editor/product-gamesystem-editor/product-gamesystem-editor.component.ts b/src/app/editor/gamesystem-editor/product-gamesystem-editor/product-gamesystem-editor.component.ts new file mode 100644 index 0000000..5648def --- /dev/null +++ b/src/app/editor/gamesystem-editor/product-gamesystem-editor/product-gamesystem-editor.component.ts @@ -0,0 +1,17 @@ +import {Component, Input} from '@angular/core'; +import {ProductGamesystem} from "../../../game-model/gamesystems/ProductGamesystem"; +import {ProductStateEditorComponent} from "../state-editor/product-state-editor/product-state-editor.component"; + +@Component({ + selector: 'app-product-gamesystem-editor', + standalone: true, + imports: [ + ProductStateEditorComponent + ], + templateUrl: './product-gamesystem-editor.component.html', + styleUrl: './product-gamesystem-editor.component.scss' +}) +export class ProductGamesystemEditorComponent { + + @Input() gamesystem: ProductGamesystem | undefined +} diff --git a/src/app/editor/gamesystem-editor/state-editor/product-state-editor/product-state-editor.component.html b/src/app/editor/gamesystem-editor/state-editor/product-state-editor/product-state-editor.component.html new file mode 100644 index 0000000..1f4a1d3 --- /dev/null +++ b/src/app/editor/gamesystem-editor/state-editor/product-state-editor/product-state-editor.component.html @@ -0,0 +1 @@ +

product-state-editor works!

diff --git a/src/app/editor/gamesystem-editor/state-editor/product-state-editor/product-state-editor.component.scss b/src/app/editor/gamesystem-editor/state-editor/product-state-editor/product-state-editor.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/editor/gamesystem-editor/state-editor/product-state-editor/product-state-editor.component.spec.ts b/src/app/editor/gamesystem-editor/state-editor/product-state-editor/product-state-editor.component.spec.ts new file mode 100644 index 0000000..21f6882 --- /dev/null +++ b/src/app/editor/gamesystem-editor/state-editor/product-state-editor/product-state-editor.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ProductStateEditorComponent } from './product-state-editor.component'; + +describe('ProductStateEditorComponent', () => { + let component: ProductStateEditorComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ProductStateEditorComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ProductStateEditorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/editor/gamesystem-editor/state-editor/product-state-editor/product-state-editor.component.ts b/src/app/editor/gamesystem-editor/state-editor/product-state-editor/product-state-editor.component.ts new file mode 100644 index 0000000..608c881 --- /dev/null +++ b/src/app/editor/gamesystem-editor/state-editor/product-state-editor/product-state-editor.component.ts @@ -0,0 +1,14 @@ +import {Component, Input} from '@angular/core'; +import {ProductGamesystem} from "../../../../game-model/gamesystems/ProductGamesystem"; + +@Component({ + selector: 'app-product-state-editor', + standalone: true, + imports: [], + templateUrl: './product-state-editor.component.html', + styleUrl: './product-state-editor.component.scss' +}) +export class ProductStateEditorComponent { + + @Input() gamesystem: ProductGamesystem | undefined +} diff --git a/src/app/game-model/gamesystems/Gamesystem.ts b/src/app/game-model/gamesystems/Gamesystem.ts index 0dea6ba..2625649 100644 --- a/src/app/game-model/gamesystems/Gamesystem.ts +++ b/src/app/game-model/gamesystems/Gamesystem.ts @@ -11,8 +11,6 @@ export abstract class Gamesystem extends ModelComponent{ super(gamesystemName, "", ModelComponentType.GAMESYTEM); } - abstract createState(label: string, description: string): S|undefined; - abstract createTransition(startingState: S, endingState: S): T|undefined; abstract removeState(state: S): boolean; diff --git a/src/app/game-model/gamesystems/ProductGamesystem.ts b/src/app/game-model/gamesystems/ProductGamesystem.ts index 84a135d..46db3d5 100644 --- a/src/app/game-model/gamesystems/ProductGamesystem.ts +++ b/src/app/game-model/gamesystems/ProductGamesystem.ts @@ -33,8 +33,14 @@ export class ProductGamesystem extends Gamesystem[]): ProductState | undefined { + if(innerStates.length == this.innerGamesystems.length && this.findProductStateByInnerStates(innerStates)== undefined) { + const productState = new ProductState(innerStates); + this.states.push(productState); + return productState; + } else { + return undefined; + } } createTransition(startingState: ProductState, endingState: ProductState): ProductTransition | undefined { @@ -57,4 +63,21 @@ export class ProductGamesystem extends Gamesystem, Transition>) { this.innerGamesystems = this.innerGamesystems.filter(childSystem => childSystem != gamesystem); } + + findProductStateByInnerStates(innerStates: State[]) { + return this.states.find(productState => productState.equalInnerStates(innerStates)); + } + + findChildSystemByName(gamesystemName: string) { + const gamesystemQueue: Gamesystem, Transition>[] = []; + this.innerGamesystems.forEach(gamesystem => gamesystemQueue.push(gamesystem)); + + while(gamesystemName.length > 0 ){ + const currentGamesystem = gamesystemQueue.shift(); + if(currentGamesystem!.componentName === gamesystemName) { + return currentGamesystem; + } + } + return undefined; + } } diff --git a/src/app/game-model/gamesystems/ProductState.ts b/src/app/game-model/gamesystems/ProductState.ts index 8a5cbaf..f499f35 100644 --- a/src/app/game-model/gamesystems/ProductState.ts +++ b/src/app/game-model/gamesystems/ProductState.ts @@ -1,7 +1,42 @@ import {ProductTransition} from "./ProductTransition"; import {State} from "./State"; import {SimpleState} from "./SimpleState"; +import {Transition} from "./Transition"; export class ProductState extends State { - innerStates: SimpleState[] = []; + innerStates: State[] = []; + + constructor(innerStates: State[]) { + super(); + this.innerStates = innerStates; + } + + equalInnerStates(innerStates: State[]) { + if(innerStates == undefined || this.innerStates.length != innerStates.length) { + return false; + } + + for(let i=0; i>): boolean { + if(state instanceof SimpleState || this.innerStates.length != (state as ProductState).innerStates.length) { + return false; + } + + for(let i=0; i { + stateLabel: string = ""; + stateDescription: string = ""; + + + constructor(stateLabel: string, stateDescription: string) { + super(); + this.stateLabel = stateLabel; + this.stateDescription = stateDescription; + } + + equals(state: State>): boolean { + if(!(state instanceof SimpleState)) { + return false; + } + return this.stateLabel === (state as SimpleState).stateLabel; + } + } diff --git a/src/app/game-model/gamesystems/State.ts b/src/app/game-model/gamesystems/State.ts index 5184811..d6b78de 100644 --- a/src/app/game-model/gamesystems/State.ts +++ b/src/app/game-model/gamesystems/State.ts @@ -1,19 +1,12 @@ import {Transition} from "./Transition"; export abstract class State> { - stateLabel: string = ""; - stateDescription: string = ""; + incomingTransitions: T[] =[]; outgoingTransitions: T[] =[]; initial: boolean = false; - - constructor(stateLabel: string, stateDescription: string) { - this.stateLabel = stateLabel; - this.stateDescription = stateDescription; - } - addIncomingTransition(transition: T) { this.incomingTransitions.push(transition); } @@ -29,4 +22,6 @@ export abstract class State> { removeOutgoingTransition(transition: T) { } + + abstract equals(state: State>): boolean; }