• No results found

5.2 Het generiek systeem

5.2.3 Interpreters

Voor adaptatie zal zowel de score van het spel als de hartslag van de gebruiker gebruikt worden. Er zullen bijgevolg twee interpreters voorzien moeten worden: één voor de gegevens uit de game en één voor de hartslaggegevens. Beiden moeten ze de gegevens, die door offsite adaptatie over het internet gestuurd worden met behulp van Kafka, kunnen ontvangen. Vervolgens dienen de ontvangen gegevens terug gedeserialiseerd te worden waarna deze gegevens naar uniforme objecten kunnen geïnterpreteerd worden.

Voor elke interpreter wordt een ontvanger voorzien die de gegevens van een bepaalde topic van kafka ontvangt. Zo een ontvanger implementeert de IReceiver<T> interface, zoals in Listing 5.2 zichtbaar is, die de methode receive(T data) bevat. De ontvanger zal, na het ontvangen van het bericht, het bericht laten deserialiseren door de transformer waarna het gedeserialiseerde object geïnterpreteerd kan worden. De implementatie voorziet een ontvanger voor gegevens van het spel en een ontvanger voor hartslaggegevens, respectievelijk GameDataReceiver en HeartbeatSensor-

Receiver genaamd. De templateparameter T van de IReceiver interface is bij beide ontvangers

van het type ConsumerRecord<String, String> wat een Kafka-bericht beschrijft met zowel het sleuteltype als het waardetype een String.

5.2. HET GENERIEK SYSTEEM 51

1 public interface IReceiver <T> { 2 public void receive(T data);

3 }

Listing 5.2: IReceiver interface

De transformer van de interpreter zal de interface ITransformer<T,V>, zoals in Listing 5.3 ge- toond wordt, implementeren. T staat voor het type data waarnaar V omgevormd moet worden. In deze implementatie moet het bericht van de game met de score, voorgesteld als een String uit de ConsumerRecord<String, String>, omgevormd worden naar een object waaruit de velden gelezen kunnen worden. De ITransformer zal bijgevolg een String transformeren naar een Json-

Object. Uit dit object kunnen eenvoudig de verschillende velden van het bericht gehaald worden.

De templateparameters T en V voor de GameDataTransformer zijn respectievelijk van het type

JsonObject en het type String. Omdat de hartslaggegevens enkel bestaan uit Strings met daarin

de hartslagwaarde, dient hiervoor geen ITransformer voorzien te worden.

1 public interface ITransformer <T, V> { 2 public T transform(V data);

3 }

Listing 5.3: ITransformer interface

Nadat berichten getransformeerd zijn, kunnen ze geïnterpreteerd worden naar uniforme objecten. Er wordt voor zowel de scores van het spel als de hartslaggegevens een interpreter voorzien. Deze interpreters implementeren beide de IInterpreter<T> interface zoals in Listing 5.4 zichtbaar is. In het geval van de gegevens uit de game, is de templateparameter voor deze interface van het type JsonObject. De transformer transformeert de String immers naar een JsonObject. Voor de hartslaggegevens interpreteert de component een String naar een Integer. In de implementatie krijgen de interpreters de namen GameDataInterpreter en HeartbeatSensorInterpreter.

1 public interface IInterpreter <T> { 2 public void interpret(T data);

52 HOOFDSTUK 5. IMPLEMENTATIE

Listing 5.4: IInterpreter interface

Als laatste moeten de geïnterpreteerde gegevens verder doorheen het systeem gestuurd kunnen worden. Hiervoor wordt de ISender<T, V> interface gebruikt, zoals in Listing 5.5 getoond wordt. Deze interface bevat drie methodes die toelaten om geïnteresseerden in de gegevens te registreren en te deregistreren en om gegevens door te sturen naar geïnteresseerde partijen. Indien een component gegevens van een andere component wil ontvangen, dient deze component zich te registreren als geïnteresseerde bij de andere component en moet de IReceiver interface geïmplementeerd worden. De templateparameters T en V staan hier respectievelijk voor het type van de te zenden data en het soort data.

Omdat in de implementatie de interpreters van de gamedata en van de hartslaggegevens de berichten naar uniforme objecten interpreteren zal de templateparameter T van de ISender in- terface van het type UnifObject zijn. De parameter V zal van het type UnifObjectType zijn. In Figuur 5.1 wordt een algemeen overzicht gegeven van hoe de interpreters van deze implementatie eruit zien.

1 public interface ISender <T, V> { 2 public void send(T data , V type);

3 public void registerReceiver(IReceiver <T> receiver , V type); 4 public void unregisterReceiver(IReceiver <T> receiver);

5 }

Listing 5.5: ISender interface

5.2.4 Data Integrator

Zoals in hoofdstuk 4 besproken werd, is er een integrator nodig die de gegevens op een correcte manier in de adaptive engine brengt. In de implementatie heet de integrator de DataIntegra-

tor. De integrator ontvangt gegevens van alle interpreters die uniforme objecten maken. Om

deze gegevens te kunnen ontvangen dient de IReceiver<T> interface, uit Listing 5.2, geïmple- menteerd te zijn. Het doorsturen en ontvangen van de uniforme objecten van de interpreters

5.2. HET GENERIEK SYSTEEM 53

Figuur 5.1: Algemeen overzicht van de interpreters van de implementatie

naar de integrator verloop lokaal. Er is dus geen communicatie over het internet en bijgevolg is er geen serialisatie en deserialisatie nodig. De templateparameter van de IReceiver interface is bij de DataIntegrator van het type UnifObject omdat de DataIntegrator uniforme objecten ontvangt. Nog een interface die de DataIntegrator moet implementeren is de IIntegrator<T> interface, zoals getoond in Listing 5.6. Deze interface laat toe om adaptive engines te registreren en te deregistreren bij de integrator. De integrator kan zo, wanneer de push-operatie opgeroepen wordt om de gegevens naar een adaptive engine te sturen, een adaptive engine selecteren die de data verder zal verwerken. Omdat in de Flappy Bird implementatie van het systeem slechts één adaptive engine gebruikt wordt, hoeft geen adaptive engine geselecteerd te worden en zal de integrator, wanneer gegevens ontvangen worden, direct de push-operatie oproepen. De push- operatie zal voor een bepaald type uniform object, de juiste operatie van de adaptive engine oproepen. Een algemeen overzicht van de component in deze implementatie wordt getoond in Figuur 5.2.

1 public interface IIntegrator <T> { 2 public void push(T data);

3 public void registerEngine(IAdaptiveEngine <T> engine); 4 public void unregisterEngine(IAdaptiveEngine <T> engine);

5 }

54 HOOFDSTUK 5. IMPLEMENTATIE

Figuur 5.2: Algemeen overzicht van de integrator van de implementatie

5.2.5 Adaptive Engine

De volgende stap is om de adaptive engine van het systeem te voorzien. Deze component zal één model bevatten die de score van een spel en de hartslag van de speler gebruikt voor analyse en adaptatie. Als eerste wordt de interface IAdaptiveEngine<T>, die te zien is in Listing 5.7, in de adaptive engine geïmplementeerd. Om gegevens van de integrator te kunnen ontvangen, dient elke adaptive engine zich te registreren bij een integrator door de methode registerEngine van de IInterpreter interface op te roepen. Voor de adaptive engine in deze implementatie, zal de template parameter van de IAdaptiveEngine van the type UnifObject zijn.

1 public interface IAdaptiveEngine <T> { 2 void notifyEngine(T data);

3 IAdaptiveEngineModel <T> getModel(EModel m);

4 }

Listing 5.7: IAdaptiveEngine interface

Taskmodel

Om functionaliteit aan de adaptive engine toe te voegen, wordt een model toegevoegd. Dit model zorgt voor analyse en genereert nieuwe opdrachten voor de game of levert statistische gegevens. Voor deze implementatie kreeg dit model de naam TaskModel. Het TaskModel gebruikt

5.2. HET GENERIEK SYSTEEM 55 hartslagen en scores van Flappy Bird om nieuwe opdrachten te selecteren. De opdracht van het spel die gebruikt zal worden is de SetSpeed-methode. Deze laat toe om de snelheid van het spel aan te passen. Hoe hoger de snelheid, hoe moeilijker het spel en vice versa.

Als eerste dient het model de interface IAdaptiveEngineModel<T> te implementeren. Deze in- terface voorziet twee methodes, zoals in Listing 5.8 te zien is, die toelaten om een adaptive engine te registreren en deregistreren. Telkens wanneer het model een nieuwe opdracht wil door- geven, kan de notifyEngine-methode van de adaptive engine gebruikt worden om deze opdracht door te spelen aan de adaptive engine. De adaptive engine zal er vervolgens voor zorgen dat de opdracht verder in het systeem gebracht wordt. Ook hier zal de templateparameter T van het type UnifObject zijn.

1 public interface IAdaptiveEngineModel <T> {

2 void registerEngine(IAdaptiveEngine <T> engine); 3 void unregisterEngine(IAdaptiveEngine <T> engine);

4 }

Listing 5.8: IAdaptiveEngineModel interface

Wanneer de adaptive engine opdrachten of statistische gegevens ontvangt, dienen deze gege- vens verder gestuurd te worden doorheen het systeem. Omdat voor deze implementatie gebruik gemaakt wordt van offsite adaptatie, moeten deze gegevens terug over het internet gestuurd worden, waarvoor opnieuw Kafka gebruikt wordt. Voor het zenden van de gegevens wordt terug gebruik gemaakt van de ISender<T, V> interface. Het verschil van deze send-methode met die van de interpreters is dat het zenden hier via Kafka gaat en dus niet lokaal gebeurt. Omdat terug over het internet gezonden moet worden, moeten de gegevens geserialiseerd worden. Hier- voor kan terug de ITransformer<T,V> interface gebruikt worden. De templateparameters T en

V van deze transformer zijn respectievelijk String en UnifObject. De templateparameter T van

de sender is bijgevolg van het type String. Afhankelijk van het type uniform object wordt het geserialiseerde bericht op een ander Kafka-topic gestuurd. Een overzicht van de sender voor de adaptive engine, is te zien in Listing 5.9.

56 HOOFDSTUK 5. IMPLEMENTATIE

>{

2 @Autowired

3 private KafkaTemplate <String , String > kafkaTemplate; 4

5 @Override

6 public void send(String data , UnifObjectType type) { 7 if(type.equals(UnifObjectType.TASKS)) {

8 kafkaTemplate.send("tasks", data);

9 }

10 }

11 // ...

12 }

13 // /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ 14 public class AdaptiveEngine implements IAdaptiveEngine <UnifObject >{

15 // ...

16 @Override

17 public void notifyEngine(UnifObject data) {

18 sender.send(transformer.transorm(data), data.getType());

19 }

20 // ...

21 }

Listing 5.9: Implementatie van de sender voor de Adaptive Engine

Om ervoor te zorgen dat gegevens op een goede manier in het juiste model van de adaptive engine terecht komen, worden in de adaptive engine enkele operaties geïmplementeerd. Deze operaties zullen afhankelijk van het type uniform object opgeroepen worden door de DataIntegrator. Er wordt een operatie processEndScore(UnifObject), waarvan de implementatie te zien is in Listing 5.10, geïmplementeerd. In deze operatie wordt het TaskModel opgehaald met de getModel me- thode en wordt de score in het TaskModel gebracht. Hetzelfde gebeurt voor een hartslagwaarde. Hiervoor wordt de methode processHeartbeat(UnifObject) geïmplementeerd. Wanneer meerdere modellen gebruikt zouden worden, kunnen deze operaties gebruikt worden om de gegevens in meerdere modellen tegelijkertijd te brengen. Een algemeen overzicht van de adaptive engine en bijhorende modellen voor deze implementatie wordt getoond in Figuur 5.3.

5.2. HET GENERIEK SYSTEEM 57

Figuur 5.3: Algemeen overzicht van de adaptive engine met bijhorend model van de implementatie

1 @Override

2 public void processEndScore(UnifObject score) {

3 TaskModel taskmod = (TaskModel)models.get(EModel.TASKMODEL); 4 taskmod.newEndScore(score);

5 }

Listing 5.10: Implementatie van de processEndScore-operatie in de Adaptive Engine

5.2.6 TaskInterpreter

Nieuwe opdrachten die door de adaptive engine gestuurd werden, moeten geïnterpreteerd en uitgevoerd worden in het spel. Hiervoor wordt de TaskInterpreter geïmplementeerd. Deze component zal berichten van de Kafka-topic tasks opvangen, verwerken en uitvoeren. Om de be- richten te ontvangen wordt opnieuw gebruik gemaakt van de IReceiver<T> interface. Nadat het bericht ontvangen is, moet het gedeserialiseerd worden omdat de adaptive engine de berichten serialiseert vooraleer ze gezonden worden. Deze deserialisatie gebeurt terug met behulp van de

ITransformer<T, V> interface. Nadat de opdrachten gedeserialiseerd zijn, kunnen ze geïnter-

preteerd worden door middel van de IInterpreter interface waarna ze uitgevoerd kunnen worden in de Flappy Bird serious game. Het Flappy Bird spel is in de programmeertaal C# geschreven.

58 HOOFDSTUK 5. IMPLEMENTATIE De TaskInterpreter moet bijgevolg ook in C# geschreven worden omdat de TaskInterpreter bij de game hoort. Hoe een serie opdrachten eruitziet in deze implementatie is te zien in Listing 5.11. De sleutels van de dictionary zijn de namen van de methodes. De lijst van objecten hieraan gepaard zijn de argumenten die de methode nodig heeft. In het geval van Flappy Bird heet de methode die gebruikt wordt SetSpeed en wordt als argument een Float met de snelheid verwacht.

6 public class TaskList {

7 public Dictionary <string , List <object >> tasks {get; set;}

8 }

9 // /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ 10 public class TaskInterpreter: IInterpreter <TaskList >{

11 // ...

12 public void Interpret(TaskList gametasks) {

13 Type gamecontroltype = Type.GetType("GameControl"); 14 foreach(string task in gametasks.tasks.Keys) {

15 MethodInfo method = gamecontroltype.GetMethod(GameControl.

instance.gamesettings.mappings[task]);

16 if(method != null) {

17 method.Invoke(GameControl.instance.gametasks.tasks[task

].ToArray());

18 }catch(Exception e) {

19 Debug.Log("Could not find method");

20 } 21 } 22 } 23 } 24 // ... 25 }

5.3. CONCLUSIE 59

5.3 Conclusie

Nu alle componenten van de architectuur geïmplementeerd werden in de Flappy Bird serious game, kan de serious game geadapteerd en gepersonaliseerd worden. Elke keer dat Flappy Bird een nieuwe score ter beschikking heeft, wordt deze naar de adaptive engine gestuurd door ge- bruik te maken van de GameDataInterpreter en de DataIntegrator. Vervolgens wordt in het

TaskModel van de AdaptiveEngine, de score in combinatie met de laatste hartslagwaarde ge-

analyseerd, waarna beslist wordt of het spel makkelijker of moeilijker gemaakt moet worden. De hartslagwaarde werd vanuit de hartslagsimulator via de HeartbeatsensorInterpreter naar de

DataIntegrator gebracht. De nieuwe opdracht wordt vanuit het model naar de adaptive engine

gestuurd die vervolgens de opdrachten naar de TaskInterpreter zal sturen. Hierin worden de op- drachten uit het bericht van de adaptive engine geïnterpreteerd, waarna ze uitgevoerd worden op het spel. De moeilijkheid van de Flappy Bird serious game is bijgevolg geadapteerd en geperso- naliseerd dankzij de architectuur. Een overzicht van het volledige systeem van de implementatie wordt getoond in Figuur 5.4. In het volgende hoofdstuk worden testen en hypotheses opgesteld die deze architectuur nog verder zullen evalueren.

Figuur 5.4: Algemeen overzicht van de volledige architectuur toegepast op de imple- mentatie

6

Evaluatie

Om na te gaan of de generieke architectuur de serious game wel degelijk adaptief en geperso- naliseerd maakt, wordt een evaluatieplan opgesteld. In dit evaluatieplan is het de bedoeling om na te gaan of drie hypothesen, die ook in deze sectie besproken worden, voldaan zijn. Deze testen zullen zowel functionele als niet functionele vereisten van de architectuur aankaarten. Omdat de tijd om deze thesis uit te werken slechts één semester is, zal niet gewerkt worden met test- personen die werkelijk fysieke revalidatie-oefeningen nodig hebben maar zullen enkele simulaties uitgevoerd worden. Deze simulaties worden uitgevoerd op de implementatie van de architectuur in Flappy Bird. Op basis van de resultaten van deze simulaties kan het systeem geëvalueerd worden. Daarboven wordt ook de manier waarop het systeem geïmplementeerd kan worden, ge- ëvalueerd. Een stappenplan om het systeem voor een bepaalde serious game te implementeren zal bepalen of het systeem gemakkelijk toelaat een serious game adaptief en gepersonaliseerd te maken. In de volgende paragraaf worden de hypothesen in detail besproken. Vervolgens wor-

6.1. HYPOTHESEN 61 den de testen, waarmee nagegaan kan worden of de hypothesen voldaan zijn, geïdentificeerd en verklaard.

6.1 Hypothesen

Het systeem moet ervoor zorgen dat een serious game adaptief en gepersonaliseerd kan worden gemaakt. Hierbij werden drie hypothesen geïdentificeerd die verder in meer detail besproken worden.

• Hypothese 1: Het systeem laat toe een serious game adaptief en gepersonaliseerd te maken aan de hand van een stappenplan zonder overbodige aanpassingen in de code van de originele game.

• Hypothese 2: Het systeem laat toe een serious game te adapteren door gebruik te maken van de verschillende adaptatietechnieken, gezien in Sectie 2.2.1, en laat toe deze onderling uit te wisselen zonder overbodige aanpassingen in het systeem en de game.

• Hypothese 3: Het Flappy Bird spel kan realtime geadapteerd en gepersonaliseerd worden door het systeem door gebruik te maken van externe bronnen zoals, een hartslagsensor en een persoonlijk profiel.

De eerste hypothese is voldaan wanneer het systeem te implementeren is in een bestaande serious game gebruik makend van een stappenplan. Het stappenplan moet zo weinig mogelijk onder- scheid maken van de soort serious game om zo te garanderen dat het generiek genoeg blijft. Elke serious game dient op dezelfde manier met dezelfde architectuur adaptief en gepersonaliseerd te kunnen gemaakt worden. Daarna moet het systeem het spel adapteren na het analyseren van gegevens van het spel zoals scores en tijden. Deze gegevens dienen op een eenvoudige manier te integreren zijn in het systeem. Het moet mogelijk zijn om opdrachten van het spel uit te voeren vanuit het systeem. Deze opdrachten zijn methodes die bijvoorbeeld toelaten om de snelheid van het spel in te stellen of om een nieuw level van het spel in te laden.

Hypothese 2 zal voldaan zijn wanneer in de architectuur meerdere adaptatietechnieken, zoals besproken in Sectie 2.2.1, geïntegreerd kunnen worden. De adaptatietechnieken moeten hierbij

62 HOOFDSTUK 6. EVALUATIE eenvoudig uit te wisselen zijn.

De laatste hypothese zal voldaan zijn wanneer realtime personalisatie van de serious game mo- gelijk is. Door adaptatie en personalisatie realtime te voorzien kan het systeem zo snel mogelijk reageren op bepaalde gegevens. Bij offline adaptiviteit zou tot het volgende spel gewacht moeten worden om de aanpassingen op te merken. Een hartslagmonitor kan een persoonlijk element zijn dat gebruikt kan worden voor adaptatie en personalisatie in de game. Wanneer de gebruiker een te hoge hartslag heeft, kan het spel direct makkelijker gemaakt worden en, vice versa, bij een te hoge hartslag kan het spel moeilijker gemaakt worden. Het gebruik van een persoonlijk profiel is nog een voorbeeld van zo een persoonlijk element. Aan het begin van het spel kan een profiel van de speler gebruikt worden om de startwaarden van het spel in te stellen. Het systeem moet zo snel mogelijk op deze externe bronnen kunnen reageren.