updated templates

This commit is contained in:
Justin Overton 2017-08-06 22:02:13 +00:00
parent 51354fa168
commit 673772385d
7 changed files with 2543 additions and 242 deletions

View File

@ -80,7 +80,7 @@ X |an inanimate object, an object of mystery, an uncertain quantity |
## 1928 and Civil Rights Discrepancies ## 1928 and Civil Rights Discrepancies
Plotto was written in 1928 decades before the civil rights area in the US. Therefore the generated plots may seem to favor males and have some references to race. Plotto was written in 1928 decades before the civil rights era in the US. Therefore the generated plots may seem to favor males and have some references to race.
### Protagonist Gender ### Protagonist Gender

1
data/names_female.json Normal file
View File

@ -0,0 +1 @@
["SOPHIA","MADISON","MIA","EMMA","OLIVIA","ISABELLA","ABIGAIL","EMILY","AVA","SOFIA","ASHLEY","SARAH","SAMANTHA","LEAH","KAYLA","CHLOE"]

1
data/names_male.json Normal file
View File

@ -0,0 +1 @@
["JOHN","JUSTIN","AIDEN","JACOB","JASON","ALEXANDER","WILLIAM","JOSEPH","MATTHEW","JAYDEN","BENJAMIN","GABRIEL","CHRISTOPHER","MASON","JAMES","LIAM","DAVID","SAMUEL","NOAH","MICHAEL","ANTHONY","CHRISTIAN","BRANDON","JOSHUA","LOGAN","ETHAN","RYAN","LUCAS","DANIEL","DYLAN","TYLER","NICHOLAS","JONATHAN","ANDREW","JACK","KEVIN","THOMAS"]

File diff suppressed because it is too large Load Diff

58
fix.js
View File

@ -10,7 +10,7 @@ const jsonPath = require('jsonpath');
const obj = require('./data/plotto.json'); const obj = require('./data/plotto.json');
const _ = require('underscore'); const _ = require('underscore');
//console.log(JSON.stringify(Object.keys(obj.conflicts), null, '\t')); /*
let added = obj.conflicts; let added = obj.conflicts;
for(let ck of Object.keys(obj.conflicts)) { for(let ck of Object.keys(obj.conflicts)) {
let conflict = obj.conflicts[ck]; let conflict = obj.conflicts[ck];
@ -59,22 +59,76 @@ for(let ck of Object.keys(obj.conflicts)) {
//console.log(idxs[i].name, ':', data); //console.log(idxs[i].name, ':', data);
} }
} }
*/
//console.log(JSON.stringify(added, null, '\t')); //console.log(JSON.stringify(added, null, '\t'));
let allKeys = Object.keys(obj.conflicts); let allKeys = Object.keys(obj.conflicts);
let stk = []; let stk = [];
for(let o of jsonPath.query(obj, '$.conflicts.*')) { for(let o of jsonPath.query(obj, '$.conflicts.*')) {
stk.push(o.conflictid); stk.push(o.conflictid);
expandDesc(o);
/*
for(let arrt of ['leadIns', 'carryOns']) { for(let arrt of ['leadIns', 'carryOns']) {
stk.push(arrt); stk.push(arrt);
o[arrt] = expand(o[arrt]); o[arrt] = expand(o[arrt]);
stk.pop(); stk.pop();
} }
*/
stk.pop(); stk.pop();
} }
function expandDesc(item) {
if(typeof(item.description) != "string") {
return;
}
let rg = /\{([^}]+)\}/g;
let m = rg.exec(item.description);
if(!m) {
return;
}
let desc = item.description;
let repl = [];
let prevStart = 0;
do {
let s = desc.slice(prevStart, m.index);
if(s) { repl.push(s); }
let refNodes = expand(_.map(m[1].split("|"), function(idstr) {
let addOp = idstr.split(';');
if(addOp.length > 1) {
return { op:'+', v:addOp};
}
return idstr;
}));
repl.push(refNodes);
prevStart = m.index + m[0].length + 1;
} while((m = rg.exec(item.description)));
item.description = repl;
//console.log(item.conflictid, ':', desc);
//console.log('===', JSON.stringify(repl));
}
function expand(item) { function expand(item) {
if(typeof(item) == "string") {
item = fixToCh(item);
}
if(typeof(item) == "string") { if(typeof(item) == "string") {
let ret = item; let ret = item;
@ -137,7 +191,7 @@ function expand(item) {
//console.log('OUTLIER!!', stk.join('/'), '[' + k + ']:', item, ' ---> ', JSON.stringify(ret), ' ____ ', JSON.stringify(ret2)); //console.log('OUTLIER!!', stk.join('/'), '[' + k + ']:', item, ' ---> ', JSON.stringify(ret), ' ____ ', JSON.stringify(ret2));
ret = ret2; ret = ret2;
} else { } else {
//console.log('!!!!!OUTLIER!!', stk.join('/'), '[' + k + ']:', item, ' ---> ', JSON.stringify(ret), ' ____ ', d); console.log('!!!!!OUTLIER!!', stk.join('/'), '[' + k + ']:', item, ' ---> ', JSON.stringify(ret), ' ____ ', d);
} }

254
index.js
View File

@ -6,44 +6,145 @@ function regEscape(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}; };
function pick(rng, arr) { //seedrandom doesn't expose the seed used, so generate one here
if(!arr) { const defaultSeed = seedrandom()(); ;
return null; const defaultRng = seedrandom(defaultSeed);
}
if(arr.length == 0) const genderMap = {
return null; "A": "male",
if(arr.length == 1) "A-2": "male",
return arr[0]; "A-3": "male",
"A-4": "male",
"A-5": "male",
"A-6": "male",
"A-7": "male",
"A-8": "male",
"A-9": "male",
"B": "female",
"B-2": "female",
"B-3": "female",
"B-4": "female",
"B-5": "female",
"B-6": "female",
"B-7": "female",
"B-8": "female",
"B-9": "female",
"F-A": "father",
"M-A": "mother",
"BR-A": "male",
"SR-A": "female",
"SN-A": "male",
"D-A": "female",
"U-A": "male",
"AU-A": "female",
"CN-A": "male",
"NW-A": "male",
"NC-A": "female",
"GF-A": "male",
"GM-A": "female",
"SF-A": "male",
"SM-A": "female",
"GCH-A": "any",
"F-B": "male",
"M-B": "female",
"BR-B": "male",
"SR-B": "female",
"SN-B": "male",
"D-B": "female",
"U-B": "male",
"AU-B": "female",
"CN-B": "female",
"NW-B": "male",
"NC-B": "female",
"GF-B": "male",
"GM-B": "female",
"SF-B": "male",
"SM-B": "female",
"GCH-B": "any",
"BR": "male",
"SR": "female",
"SN": "male",
"D": "female",
"CN": "any",
"CH": "any",
"AX": "male",
"BX": "female",
"X": "none"
};
function createRandomPicker(rng=defaultRng) {
return function randomPicker(arr) {
if(!arr) {
return null;
}
if(arr.length == 0)
return null;
if(arr.length == 1)
return arr[0];
let min = 0;
let max = arr.length;
let i =Math.floor(rng() * (max - min)) + min;
let ret = arr[i];
return ret;
};
}
let maleNames = [];
let femaleNames = [];
function createRandomNamer(rng=defaultRng) {
let min = 0; return function randomNamer(characterSymbol, symbolDescription, gender) {
let max = arr.length;
let i =Math.floor(rng() * (max - min)) + min; let arr;
let ret = arr[i]; if(gender == 'male') {
//console.log(i, ':', JSON.stringify(ret), '!!!!', JSON.stringify(arr)); arr = maleNames;
return ret; } else if(gender == 'female') {
arr = femaleNames;
} else if(gender == 'any') {
if(rng() < 0.5) {
arr = maleNames;
} else {
arr = femaleNames;
}
} else {
return characterSymbol;
}
let min = 0;
let max = arr.length;
let i =Math.floor(rng() * (max - min)) + min;
let ret = arr[i];
arr.splice(i,1);
return ret || characterSymbol;
};
} }
class PlotGenerator { class PlotGenerator {
constructor({ seed=null, flipGenders=undefined } = {}) { constructor({ picker=undefined, namer=undefined, rng=undefined, flipGenders=undefined } = {}) {
//seedrandom doesn't expose the seed used, so generate one here
this._seed = seed || seedrandom()();
this._rng = seedrandom(this._seed);
if(flipGenders === undefined) { this._namer = namer || createRandomNamer();
this._flipGenders = this._rng() < 0.5; //50% chance true/false this._picker = picker || createRandomPicker();
} else { this._flipGenders = flipGenders;
this._flipGenders = flipGenders;
}
} }
get seed() { return this._seed; }
get flipGenders() { return this._flipGenders; } get flipGenders() { return this._flipGenders; }
set flipGenders(flip) { this._flipGenders = flip; } set flipGenders(flip) { this._flipGenders = flip; }
generate() { generate() {
let flip = this._flipGenders;
if(flip === undefined) {
flip = this._picker([true, false], 'flip genders');
}
let rootTransform = {}; let rootTransform = {};
if(this._flipGenders) { if(this._flipGenders) {
rootTransform = { rootTransform = {
@ -67,21 +168,79 @@ class PlotGenerator {
"B-9": "A-9" "B-9": "A-9"
}; };
} }
let preamble = pick(this._rng, plotto.masterClauseA); let preamble = this._picker(plotto.masterClauseA, 'master clause A');
let resolution = pick(this._rng, plotto.masterClauseC); let resolution = this._picker(plotto.masterClauseC, 'master clause C');
let masterPlot = pick(this._rng, plotto.masterClauseB); let masterPlot = this._picker(plotto.masterClauseB, 'master clause B');
let subject = [masterPlot.group, ' / ', masterPlot.subgroup, ': ', masterPlot.description].join(''); let subject = [masterPlot.group, ' / ', masterPlot.subgroup, ': ', masterPlot.description].join('');
let conflict = plotto.conflicts[pick(this._rng, masterPlot.nodes)]; let conflict = plotto.conflicts[this._picker(masterPlot.nodes, 'main conflict')];
let cast = [];
let plot = this._expand(conflict, rootTransform, {leadIns:1, carryOns:1}).replace(/\*/g, '');
plot = this._applyNames(plot, cast);
return {
group: masterPlot.group,
subgroup: masterPlot.subgroup,
description: masterPlot.description,
cast: cast,
plot: [
preamble,
plot,
resolution
].join('\n\n').trim()
};
}
_applyNames(text, cast) {
//reset default name lists
maleNames = require('./data/names_male.json').slice(0);
femaleNames = require('./data/names_female.json').slice(0);
//randomNamer(characterSymbol, symbolDescription, gender)
let nameCache = {};
let ks = Object.keys(plotto.characters);
if(ks.length > 0) {
let pattern = '\\b(?:' + ks.map(function(s) {
return '(?:' + regEscape(s) + ')';
}).join('|') + ')(?=^|$| |,|\\.)';
let rg = new RegExp(pattern, 'g');
text = text.replace(rg,(match) => {
if(!match || match.length == 0)
return '';
let t = plotto.characters[match];
if(!t) {
console.log('Could not replace match in template: ' + match);
return match;
}
let ret = nameCache[match];
if(!ret) {
ret = this._namer(match, t, genderMap[match]);
nameCache[match] = ret;
cast.push({
symbol: match,
name: ret,
description: t
});
}
return ret;
});
}
return text;
return [
subject,
preamble,
this._expand(conflict, rootTransform, {leadIns:1, carryOns:1}).replace(/\*/g, ''),
resolution
].join('\n\n').trim();
} }
_expand(item, transform, ctx, start, end) { _expand(item, transform, ctx, start, end) {
@ -105,9 +264,24 @@ class PlotGenerator {
if(typeof(item) == "string") { if(typeof(item) == "string") {
ret.push(this._expand(plotto.conflicts[item], null, ctx)); ret.push(this._expand(plotto.conflicts[item], null, ctx));
} else if(Array.isArray(item)) { } else if(Array.isArray(item)) {
ret.push(this._expand(pick(this._rng, item), null, ctx)); ret.push(this._expand(this._picker(item, 'plot option'), null, ctx));
} else if(item.conflictid) { } else if(item.conflictid) {
ret.push(item.description);
if(typeof(item.description) == "string") {
ret.push(item.description);
} else {
for(let subdesc of item.description) {
if(typeof(subdesc) == "string") {
ret.push(subdesc);
} else {
ret.push(this._expand(subdesc, null, ctx));
}
}
}
} else if(item.v) { } else if(item.v) {
if(item.start || item.end) { if(item.start || item.end) {
@ -161,5 +335,5 @@ class PlotGenerator {
} }
} }
module.exports = PlotGenerator; module.exports = PlotGenerator;
module.exports.defaultSeed = defaultSeed;

View File

@ -1,6 +1,6 @@
{ {
"name": "plottoriffic", "name": "plottoriffic",
"version": "1.0.0", "version": "2.0.0",
"description": "Generate plots based on Plotto: A New Method of Plot Suggestion for Writers of Creative Fiction", "description": "Generate plots based on Plotto: A New Method of Plot Suggestion for Writers of Creative Fiction",
"keywords": ["procedural", "generative", "fiction", "story", "plot", "ES6"], "keywords": ["procedural", "generative", "fiction", "story", "plot", "ES6"],
"main": "index.js", "main": "index.js",