Skip to content

Commit

Permalink
Merge branch 'master' of gitlab.infosun.fim.uni-passau.de:se2/whisker…
Browse files Browse the repository at this point in the history
…/whisker-main
  • Loading branch information
gofraser committed Aug 19, 2021
2 parents a81890c + 41c35e5 commit c3fad14
Show file tree
Hide file tree
Showing 28 changed files with 421 additions and 133 deletions.
42 changes: 42 additions & 0 deletions config/random.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"test-generator": "random",
"algorithm": "random",
"minEventSize": 2,
"maxEventSize": 30,
"extractor": "naive",
"fitness-function": {
"type": "statement",
"targets": []
},
"stopping-condition": {
"type": "one-of",
"conditions": [
{
"type": "fixed-time",
"duration": 6000000
},
{
"type": "optimal"
}
]
},
"crossover": {
"operator": "singlepointrelative",
"probability": 1
},
"mutation": {
"operator": "biasedVariablelengthConstrained",
"probability": 1,
"gaussianMutationPower": 5,
"maxMutationCountStart": 0,
"maxMutationCountFocusedPhase": 10
},
"selection": {
"operator": "rank",
"randomSelectionProbabilityStart": 0.5,
"randomSelectionProbabilityFocusedPhase": 0
},
"waitStepUpperBound": 200,
"click-duration": 3,
"press-duration": 50
}
8 changes: 5 additions & 3 deletions whisker-main/src/whisker/search/algorithms/SimpleGA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,18 @@ export class SimpleGA<C extends Chromosome> extends SearchAlgorithmDefault<C> {
let population = this.generateInitialPopulation();
await this.evaluatePopulation(population);

// Evaluate population
this.evaluateAndSortPopulation(population);
// Evaluate population, but before check if we have already reached our stopping condition
if(!(this._stoppingCondition.isFinished(this))) {
this.evaluateAndSortPopulation(population);
}

while (!(this._stoppingCondition.isFinished(this))) {
console.log(`Iteration ${this._iterations}, best fitness: ${this._bestFitness}`);

const nextGeneration = this.generateOffspringPopulation(population);
await this.evaluatePopulation(nextGeneration);
if(!(this._stoppingCondition.isFinished(this))) {
this.evaluateAndSortPopulation(nextGeneration)
this.evaluateAndSortPopulation(nextGeneration);
}
population = nextGeneration;
this._iterations++;
Expand Down
30 changes: 23 additions & 7 deletions whisker-main/src/whisker/testcase/NaiveScratchEventExtractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,37 +27,53 @@ import {ScratchEventExtractor} from "./ScratchEventExtractor";
import {MouseDownEvent} from "./events/MouseDownEvent";
import {MouseMoveEvent} from "./events/MouseMoveEvent";
import {KeyPressEvent} from "./events/KeyPressEvent";
import {SoundEvent} from "./events/SoundEvent";
import {TypeTextEvent} from "./events/TypeTextEvent";
import {DragSpriteEvent} from "./events/DragSpriteEvent";
import {ClickSpriteEvent} from "./events/ClickSpriteEvent";
import {ClickStageEvent} from "./events/ClickStageEvent";
import {Randomness} from "../utils/Randomness";
import {SoundEvent} from "./events/SoundEvent";

export class NaiveScratchEventExtractor extends ScratchEventExtractor {

// TODO: Additional keys?
private readonly KEYS = ['space', 'left arrow', 'up arrow', 'right arrow', 'down arrow', 'enter'];

private readonly _text;
private readonly _random: Randomness;

/**
* NaiveScratchEventExtractor adds every type of supported Whisker-Event to the set of avilalbe events.
* Whenever a parameter is required, it is randomly selected.
* @param vm the Scratch-VM
*/
constructor(vm: VirtualMachine) {
super(vm);
this._text = this._randomText(3);
this._random = Randomness.getInstance();
}

public extractEvents(vm: VirtualMachine): List<ScratchEvent> {
const eventList = new List<ScratchEvent>();

eventList.add(new ClickStageEvent());
eventList.add(new WaitEvent());
eventList.add(new TypeTextEvent(this._text));
eventList.addList(this._getTypeTextEvents()); // Just one random string
eventList.add(new TypeTextEvent(this._randomText(3)));
eventList.add(new MouseDownEvent(true));
eventList.add(new MouseDownEvent(false));
eventList.add(new MouseMoveEvent());
// eventList.add(new SoundEvent()); // Not implemented yet

// eventList.add(new SoundEvent()) not implemented yet

// Add specified keys.
for (const key of this.KEYS) {
eventList.add(new KeyPressEvent(key));
}

// Add events requiring a targets as parameters.
for (const target of vm.runtime.targets) {
eventList.add(new DragSpriteEvent(target));
if(!target.isStage) {
eventList.add(new DragSpriteEvent(target));
eventList.add(new ClickSpriteEvent(target));
}
}
return eventList.distinctObjects();
}
Expand Down
10 changes: 2 additions & 8 deletions whisker-main/src/whisker/testcase/ScratchEventExtractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,6 @@ export abstract class ScratchEventExtractor {
}
}


// TODO: How to handle event parameters?
protected _extractEventsFromBlock(target, block): List<ScratchEvent> {
const eventList = new List<ScratchEvent>();
if (typeof block.opcode === 'undefined') {
Expand All @@ -123,7 +121,8 @@ export abstract class ScratchEventExtractor {
break;
}
case 'sensing_mousex':
case 'sensing_mousey': {
case 'sensing_mousey':
case 'pen_penDown': {
// Mouse move
eventList.add(new MouseMoveEvent());
break;
Expand Down Expand Up @@ -190,7 +189,6 @@ export abstract class ScratchEventExtractor {
const field = target.blocks.getFields(distanceMenuBlock);
const value = field.DISTANCETOMENU.value;
if (value == "_mouse_") {
// TODO: Maybe could determine position to move to here?
eventList.add(new MouseMoveEvent());
}
break;
Expand All @@ -207,10 +205,6 @@ export abstract class ScratchEventExtractor {
eventList.add(new MouseDownEvent(!isMouseDown));
break;
}
case 'pen_penDown': {
eventList.add(new MouseMoveEvent())
break;
}
case 'sensing_askandwait':
// Type text
if (Container.vmWrapper.isQuestionAsked()) {
Expand Down
100 changes: 99 additions & 1 deletion whisker-main/src/whisker/testcase/StaticScratchEventExtractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,32 @@ import VirtualMachine from 'scratch-vm/src/virtual-machine.js';
import {ScratchEvent} from "./events/ScratchEvent";
import {WaitEvent} from "./events/WaitEvent";
import {ScratchEventExtractor} from "./ScratchEventExtractor";
import {KeyPressEvent} from "./events/KeyPressEvent";
import {MouseMoveEvent} from "./events/MouseMoveEvent";
import {Randomness} from "../utils/Randomness";
import {DragSpriteEvent} from "./events/DragSpriteEvent";
import {MouseDownEvent} from "./events/MouseDownEvent";
import {ClickSpriteEvent} from "./events/ClickSpriteEvent";
import {ClickStageEvent} from "./events/ClickStageEvent";
import {SoundEvent} from "./events/SoundEvent";
import {TypeTextEvent} from "./events/TypeTextEvent";

export class StaticScratchEventExtractor extends ScratchEventExtractor {

constructor (vm: VirtualMachine) {
// TODO: Additional keys?
private readonly _keys = ['space', 'left arrow', 'up arrow', 'right arrow', 'down arrow', 'enter'];

private readonly _random: Randomness;

/**
* StaticScratchEventExtractor only adds event for which corresponding event handler exist. However, as opposed
* to the DynamicScratchEventExtractor, the parameters are chosen randomly and not inferred from the VM whenever
* possible.
* @param vm the Scratch-VM
*/
constructor(vm: VirtualMachine) {
super(vm);
this._random = Randomness.getInstance();
}

public extractEvents(vm: VirtualMachine): List<ScratchEvent> {
Expand All @@ -48,4 +69,81 @@ export class StaticScratchEventExtractor extends ScratchEventExtractor {

return eventList.distinctObjects();
}

/**
* Extract events for which corresponding event handler exist. The parameter of the selected events are chosen
* randomly.
* @param target the sprite wrapped in a RenderedTarget
* @param block the given block from which events might get extracted.
* @returns List<ScratchEvent> containing all extracted events.
*/
protected _extractEventsFromBlock(target, block): List<ScratchEvent> {
const eventList = new List<ScratchEvent>();
if (typeof block.opcode === 'undefined') {
return eventList;
}

switch (target.blocks.getOpcode(block)) {
case 'event_whenkeypressed':
case 'sensing_keypressed': {
// Only add if we have not yet found any keyPress-Events. No need to add all keys several times
if (!eventList.getElements().some(event => event instanceof KeyPressEvent)) {
for (const key of this._keys) {
eventList.add(new KeyPressEvent(key));
}
}
break;
}
case 'sensing_mousex':
case 'sensing_mousey':
case 'pen_penDown': {
// Mouse move
eventList.add(new MouseMoveEvent());
break;
}
case 'sensing_touchingobject':
case 'sensing_touchingcolor' : {
eventList.add(new DragSpriteEvent(target));
break;
}
case 'sensing_distanceto': {
const distanceMenuBlock = target.blocks.getBlock(block.inputs.DISTANCETOMENU.block);
const field = target.blocks.getFields(distanceMenuBlock);
const value = field.DISTANCETOMENU.value;
if (value == "_mouse_") {
eventList.add(new MouseMoveEvent());
}
break;
}
case 'motion_pointtowards': {
const towards = target.blocks.getBlock(block.inputs.TOWARDS.block)
if (towards.fields.TOWARDS.value === '_mouse_')
eventList.add(new MouseMoveEvent());
break;
}
case 'sensing_mousedown': {
// Mouse down
eventList.add(new MouseDownEvent(true));
eventList.add(new MouseDownEvent(false));
break;
}
case 'sensing_askandwait':
// Type text
eventList.add(new TypeTextEvent(this._randomText(3)));
break;
case 'event_whenthisspriteclicked':
// Click sprite
eventList.add(new ClickSpriteEvent(target));
break;
case 'event_whenstageclicked':
// Click stage
eventList.add(new ClickStageEvent());
break;
case 'event_whengreaterthan':
// Sound
eventList.add(new SoundEvent());
break;
}
return eventList;
}
}
Loading

0 comments on commit c3fad14

Please sign in to comment.