Skip to content

Commit

Permalink
Merge pull request #4 from jamiefaye/jffmods
Browse files Browse the repository at this point in the history
Jffmods
  • Loading branch information
jamiefaye authored Jun 13, 2019
2 parents 57026eb + 7d84991 commit 8852d02
Show file tree
Hide file tree
Showing 6 changed files with 368 additions and 318 deletions.
237 changes: 125 additions & 112 deletions hydra-server/app/src/Mutator.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,144 +7,157 @@ const {UndoStack} = require('./UndoStack.js');
class Mutator {

constructor(editor) {
this.editor = editor;
this.undoStack = new UndoStack();
this.initialVector = [];
this.editor = editor;
this.undoStack = new UndoStack();
this.initialVector = [];
}


mutate(options) {
// Get text from CodeMirror.
let text = this.editor.cm.getValue();
this.undoStack.push({text, lastLitX: this.lastLitX});

// Parse to AST
var comments = [];
let ast = Parser.parse(text, {
locations: true,
onComment: comments}
);

// Modify the AST.

this.transform(ast, options);

// Put the comments back.
attachComments(ast, comments);

// Generate JS from AST and set back into CodeMirror editor.
let regen = generate(ast, {comments: true});

this.editor.cm.setValue(regen);

// Evaluate the updated expression.
this.editor.evalAll((code, error) => {
// console.log('evaluated', code, error);
});
// Get text from CodeMirror.
let text = this.editor.cm.getValue();
this.undoStack.push({text, lastLitX: this.lastLitX});
let needToRun = true;
let tryCounter = 5;
while (needToRun && tryCounter-- >= 0) {
// Parse to AST
var comments = [];
let ast = Parser.parse(text, {
locations: true,
onComment: comments}
);

// Modify the AST.

this.transform(ast, options);

// Put the comments back.
attachComments(ast, comments);

// Generate JS from AST and set back into CodeMirror editor.
let regen = generate(ast, {comments: true});

this.editor.cm.setValue(regen);

// Evaluate the updated expression.
this.editor.evalAll((code, error) => {
// If we got an error, keep trying something else.
if (error) {
console.log("Eval error: " + regen);
}
needToRun = error;
});
}
}

doUndo() {
// If the current text is unsaved, save it so we can redo if need be.
if (this.undoStack.atTop()) {
let text = this.editor.cm.getValue();
this.undoStack.push({text, lastLitX: this.lastLitX});
}
// Then pop-off the info to restore.
if (this.undoStack.canUndo()) {
let {text, lastLitX} = this.undoStack.undo();
this.setText(text);
this.lastLitX = lastLitX;
}
// If the current text is unsaved, save it so we can redo if need be.
if (this.undoStack.atTop()) {
let text = this.editor.cm.getValue();
this.undoStack.push({text, lastLitX: this.lastLitX});
}
// Then pop-off the info to restore.
if (this.undoStack.canUndo()) {
let {text, lastLitX} = this.undoStack.undo();
this.setText(text);
this.lastLitX = lastLitX;
}
}

doRedo() {
if(this.undoStack.canRedo()) {
let {text, lastLitX} = this.undoStack.redo();
this.setText(text);
this.lastLitX = lastLitX;
}
if(this.undoStack.canRedo()) {
let {text, lastLitX} = this.undoStack.redo();
this.setText(text);
this.lastLitX = lastLitX;
}
}

setText(text) {
this.editor.cm.setValue(text);
this.editor.evalAll((code, error) => {
});
this.editor.cm.setValue(text);
this.editor.evalAll((code, error) => {
});

}
}

// The options object contains a flag that controls how the
// Literal to mutate is determined. If reroll is false, we
// pick one at random. If reroll is true, we use the same field
// we did last time.
transform(ast, options) {
// An AST traveler that accumulates a list of Literal nodes.
let traveler = makeTraveler({
// An AST traveler that accumulates a list of Literal nodes.
let traveler = makeTraveler({
go: function(node, state) {
if (node.type === 'Literal') {
state.push(node);
} else if (node.type === 'MemberExpression') {
if (node.property && node.property.type === 'Literal') {
// numeric array subscripts are ineligable
return;
}
}
// Call the parent's `go` method
this.super.go.call(this, node, state);
}
});

let literalTab = [];
traveler.go(ast, literalTab);

let litCount = literalTab.length;
if (litCount > 0) {
if (litCount !== this.initialVector.length) {
let nextVect = [];
for(let i = 0; i < litCount; ++i) {
nextVect.push(literalTab[i].value);
}
this.initialVector = nextVect;
}
let litx = 0;
if (options.reroll) {
if (this.lastLitX !== undefined) {
litx = this.lastLitX;
}
} else {
litx = Math.round(Math.random() * litCount);
this.lastLitX = litx;
}
let modLit = literalTab[litx];
if (modLit) {
// let glitched = this.glitchNumber(modLit.value);
let glitched = this.glitchRelToInit(modLit.value, this.initialVector[litx]);
let was = modLit.raw;
modLit.value = glitched;
modLit.raw = "" + glitched;
console.log("Literal: " + litx + " changed from: " + was + " to: " + glitched);
}
}
}
if (node.type === 'Literal') {
state.literalTab.push(node);
} else if (node.type === 'MemberExpression') {
if (node.property && node.property.type === 'Literal') {
// numeric array subscripts are ineligable
return;
}
} else if (node.type === 'CallExpression') {
if (node.callee && node.callee.property && node.callee.property.name && node.callee.property.name !== 'out') {
state.functionTab.push(node);
}
}
// Call the parent's `go` method
this.super.go.call(this, node, state);
}
});

let state = {};
state.literalTab = [];
state.functionTab = [];

traveler.go(ast, state);

let litCount = state.literalTab.length;
let funCount = state.functionTab.length;
if (litCount !== this.initialVector.length) {
let nextVect = [];
for(let i = 0; i < litCount; ++i) {
nextVect.push(state.literalTab[i].value);
}
this.initialVector = nextVect;
}
let litx = 0;
if (options.reroll) {
if (this.lastLitX !== undefined) {
litx = this.lastLitX;
}
} else {
litx = Math.floor(Math.random() * litCount);
this.lastLitX = litx;
}
let modLit = state.literalTab[litx];
if (modLit) {
// let glitched = this.glitchNumber(modLit.value);
let glitched = this.glitchRelToInit(modLit.value, this.initialVector[litx]);
let was = modLit.raw;
modLit.value = glitched;
modLit.raw = "" + glitched;
console.log("Literal: " + litx + " changed from: " + was + " to: " + glitched);
}

}
glitchNumber(num) {
if (num === 0) {
num = 1;
}
let range = num * 2;
let rndVal = Math.round(Math.random() * range * 1000) / 1000;
return rndVal;
if (num === 0) {
num = 1;
}
let range = num * 2;
let rndVal = Math.round(Math.random() * range * 1000) / 1000;
return rndVal;
}

glitchRelToInit(num, initVal) {
if (initVal === undefined) {
return glitchNumber(num);
} if (initVal === 0) {
initVal = 0.5;
}

let rndVal = Math.round(Math.random() * initVal * 2 * 1000) / 1000;
return rndVal;
if (initVal === undefined) {
return glitchNumber(num);
} if (initVal === 0) {
initVal = 0.5;
}

let rndVal = Math.round(Math.random() * initVal * 2 * 1000) / 1000;
return rndVal;
}
} // End of class Mutator.

Expand Down
86 changes: 43 additions & 43 deletions hydra-server/app/src/UndoStack.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,53 @@
// A generalized 'Undo stack' which can keep N levels of revertable state.
class UndoStack {
constructor(limit) {
this.stack = [];
this.index = -1;
this.limit = limit;
}
atTop() {
return this.index === -1;
}
constructor(limit) {
this.stack = [];
this.index = -1;
this.limit = limit;
}
atTop() {
return this.index === -1;
}

canUndo() {
if(this.stack.length === 0) return false;
return this.index === -1 || this.index > 0;
}
canUndo() {
if(this.stack.length === 0) return false;
return this.index === -1 || this.index > 0;
}

canRedo() {
if(this.stack.length === 0 || this.index === -1) return false;
return this.index < this.stack.length - 1;
}
canRedo() {
if(this.stack.length === 0 || this.index === -1) return false;
return this.index < this.stack.length - 1;
}

push(item) {
if (this.index >= 0) {
while (this.index < this.stack.length) this.stack.pop();
this.index = -1;
}
if (this.limit && this.stack.length > this.limit) {
this.stack.shift();
}
this.stack.push(item);
}
push(item) {
if (this.index >= 0) {
while (this.index < this.stack.length) this.stack.pop();
this.index = -1;
}
if (this.limit && this.stack.length > this.limit) {
this.stack.shift();
}
this.stack.push(item);
}

undo() {
if (this.stack.length === 0) return undefined;
if (this.index === -1) { // start one behind the redo buffer
this.index = this.stack.length - 1;
}
if (this.index > 0) this.index--;
let v = this.stack[this.index];
return v;
}
undo() {
if (this.stack.length === 0) return undefined;
if (this.index === -1) { // start one behind the redo buffer
this.index = this.stack.length - 1;
}
if (this.index > 0) this.index--;
let v = this.stack[this.index];
return v;
}

redo() {
if (this.stack.length === 0 || this.index === -1) return undefined;
let nextX = this.index + 1;
if (nextX >= this.stack.length) return undefined;
this.index = nextX;
return this.stack[this.index];
}
redo() {
if (this.stack.length === 0 || this.index === -1) return undefined;
let nextX = this.index + 1;
if (nextX >= this.stack.length) return undefined;
this.index = nextX;
return this.stack[this.index];
}
};


Expand Down
10 changes: 5 additions & 5 deletions hydra-server/app/src/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var isShowing = true
var EditorClass = function () {
console.log("*** Editor class created");
var self = this
let mutator = new Mutator(this);
this.mutator = new Mutator(this);
this.cm = CodeMirror.fromTextArea(document.getElementById('code'), {
theme: 'tomorrow-night-eighties',
value: 'hello',
Expand Down Expand Up @@ -63,16 +63,16 @@ var EditorClass = function () {
// The following CodeMirror extraKeys handlers route keyboard commands
// to our experimental mutator.
'Shift-Ctrl-Left': () => {
mutator.doUndo();
this.mutator.doUndo();
},
'Shift-Ctrl-Right': () => {
mutator.mutate({reroll: false});
this.mutator.mutate({reroll: false});
},
'Shift-Ctrl-Up': () => {
mutator.doRedo();
this.mutator.doRedo();
},
'Shift-Ctrl-Down': () => {
mutator.mutate({reroll: true});
this.mutator.mutate({reroll: true});
},
}
})
Expand Down
Loading

0 comments on commit 8852d02

Please sign in to comment.