updated templates
This commit is contained in:
parent
51354fa168
commit
673772385d
@ -80,7 +80,7 @@ X |an inanimate object, an object of mystery, an uncertain quantity |
|
||||
|
||||
## 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
|
||||
|
||||
|
1
data/names_female.json
Normal file
1
data/names_female.json
Normal 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
1
data/names_male.json
Normal 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"]
|
2467
data/plotto.json
2467
data/plotto.json
File diff suppressed because it is too large
Load Diff
58
fix.js
58
fix.js
@ -10,7 +10,7 @@ const jsonPath = require('jsonpath');
|
||||
const obj = require('./data/plotto.json');
|
||||
const _ = require('underscore');
|
||||
|
||||
//console.log(JSON.stringify(Object.keys(obj.conflicts), null, '\t'));
|
||||
/*
|
||||
let added = obj.conflicts;
|
||||
for(let ck of Object.keys(obj.conflicts)) {
|
||||
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(JSON.stringify(added, null, '\t'));
|
||||
let allKeys = Object.keys(obj.conflicts);
|
||||
|
||||
let stk = [];
|
||||
for(let o of jsonPath.query(obj, '$.conflicts.*')) {
|
||||
stk.push(o.conflictid);
|
||||
|
||||
expandDesc(o);
|
||||
|
||||
/*
|
||||
for(let arrt of ['leadIns', 'carryOns']) {
|
||||
stk.push(arrt);
|
||||
o[arrt] = expand(o[arrt]);
|
||||
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) {
|
||||
|
||||
if(typeof(item) == "string") {
|
||||
item = fixToCh(item);
|
||||
}
|
||||
|
||||
if(typeof(item) == "string") {
|
||||
|
||||
let ret = item;
|
||||
@ -137,7 +191,7 @@ function expand(item) {
|
||||
//console.log('OUTLIER!!', stk.join('/'), '[' + k + ']:', item, ' ---> ', JSON.stringify(ret), ' ____ ', JSON.stringify(ret2));
|
||||
ret = ret2;
|
||||
} 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
254
index.js
@ -6,44 +6,145 @@ function regEscape(s) {
|
||||
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
};
|
||||
|
||||
function pick(rng, arr) {
|
||||
if(!arr) {
|
||||
return null;
|
||||
}
|
||||
if(arr.length == 0)
|
||||
return null;
|
||||
if(arr.length == 1)
|
||||
return arr[0];
|
||||
//seedrandom doesn't expose the seed used, so generate one here
|
||||
const defaultSeed = seedrandom()(); ;
|
||||
const defaultRng = seedrandom(defaultSeed);
|
||||
|
||||
const genderMap = {
|
||||
"A": "male",
|
||||
"A-2": "male",
|
||||
"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;
|
||||
let max = arr.length;
|
||||
let i =Math.floor(rng() * (max - min)) + min;
|
||||
let ret = arr[i];
|
||||
//console.log(i, ':', JSON.stringify(ret), '!!!!', JSON.stringify(arr));
|
||||
return ret;
|
||||
return function randomNamer(characterSymbol, symbolDescription, gender) {
|
||||
|
||||
let arr;
|
||||
if(gender == 'male') {
|
||||
arr = maleNames;
|
||||
} 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 {
|
||||
|
||||
constructor({ seed=null, flipGenders=undefined } = {}) {
|
||||
//seedrandom doesn't expose the seed used, so generate one here
|
||||
this._seed = seed || seedrandom()();
|
||||
|
||||
this._rng = seedrandom(this._seed);
|
||||
constructor({ picker=undefined, namer=undefined, rng=undefined, flipGenders=undefined } = {}) {
|
||||
|
||||
if(flipGenders === undefined) {
|
||||
this._flipGenders = this._rng() < 0.5; //50% chance true/false
|
||||
} else {
|
||||
this._flipGenders = flipGenders;
|
||||
}
|
||||
this._namer = namer || createRandomNamer();
|
||||
this._picker = picker || createRandomPicker();
|
||||
this._flipGenders = flipGenders;
|
||||
}
|
||||
|
||||
get seed() { return this._seed; }
|
||||
|
||||
get flipGenders() { return this._flipGenders; }
|
||||
set flipGenders(flip) { this._flipGenders = flip; }
|
||||
|
||||
generate() {
|
||||
|
||||
let flip = this._flipGenders;
|
||||
if(flip === undefined) {
|
||||
flip = this._picker([true, false], 'flip genders');
|
||||
}
|
||||
|
||||
let rootTransform = {};
|
||||
if(this._flipGenders) {
|
||||
rootTransform = {
|
||||
@ -67,21 +168,79 @@ class PlotGenerator {
|
||||
"B-9": "A-9"
|
||||
};
|
||||
}
|
||||
|
||||
let preamble = pick(this._rng, plotto.masterClauseA);
|
||||
let resolution = pick(this._rng, plotto.masterClauseC);
|
||||
let masterPlot = pick(this._rng, plotto.masterClauseB);
|
||||
|
||||
let preamble = this._picker(plotto.masterClauseA, 'master clause A');
|
||||
let resolution = this._picker(plotto.masterClauseC, 'master clause C');
|
||||
let masterPlot = this._picker(plotto.masterClauseB, 'master clause B');
|
||||
|
||||
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) {
|
||||
@ -105,9 +264,24 @@ class PlotGenerator {
|
||||
if(typeof(item) == "string") {
|
||||
ret.push(this._expand(plotto.conflicts[item], null, ctx));
|
||||
} 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) {
|
||||
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) {
|
||||
|
||||
if(item.start || item.end) {
|
||||
@ -161,5 +335,5 @@ class PlotGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = PlotGenerator;
|
||||
module.exports.defaultSeed = defaultSeed;
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"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",
|
||||
"keywords": ["procedural", "generative", "fiction", "story", "plot", "ES6"],
|
||||
"main": "index.js",
|
||||
|
Loading…
x
Reference in New Issue
Block a user