Published
Edited
Nov 13, 2018
3 stars
Insert cell
Insert cell
Insert cell
constraints = {
let goals = [
{event: ['intend-gift', 'friend'], gloss: "need thing for friend"},
{event: ['learn', 'notion'], gloss: "got macguffin identity"},
{event: ['learn-at', 'notion.place'], gloss: "got macguffin location"},
{event: ['travel-quest', 'notion.place'], gloss: "going to get macguffin"},
{event: ['find', 'notion.place', 'thing'], closure: 'lost', gloss: "got macguffin"},
{event: ['reflect', 'thing'], gloss: "reflection: on macguffin"},
{event: ['gift', 'friend', 'thing'], closure: 'thing', gloss: "give macguffin to friend"}
]
return {goals: goals.reverse(), protagonist: 8}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// asBond = ([i,j,tag]) => ({'name': char[i] + char[j], 'tags': [[tag]]})
// [{name: 'AB', tags: [['kin', 'mother']]}, {name: 'BA', tags: [['kin', 'daughter']]}]

// actors = // concatenate some dictionaries
// ({'A': {'kind': 'person'}, })

// return `${ret} ${ret.reverse()} ${ret.reverse()}`
Insert cell
Insert cell
select('#,0').toString()
Insert cell
select('rival').toString()
Insert cell
/* function bind(e, k, focus) {
let us = select(e[k+1])

// if this position has the focus, filter on it.
let sel = ['#'+focus, '#*,'+focus]
if (us && [k*2,k*2+1].includes(i))
us = us.select(sel[i%2])

let u, s
if (us)
u = us.pickOne()
if (!us || !u) {
// oops, we need to make up a relationship to satisfy the goal.
// do not introduce (i.e. terminate) characters at this stage.

// todo: prioritize cast of passage. avoid changing node type.
let n = edges.length,
base = [k*2,k*2+1].includes(i) ? focus : Math.floor(Math.random()*n),
wildcard = Math.floor(Math.random()*n)

link(base, wildcard, c.update['01'])
// console.log(edges.slice(-1)[0])
s = char[focus] + char[k]

if (j < 2) focus = [focus, k][j]
}
else {
// FIXME: update the chosen edge.
let [a,b] = u.body.name.split(',')
link(a, b, c.update['01'])

s = u.toString()
if (j < 2) focus = b[j]
}
return [s, focus]
} */
Insert cell
{
function link(i,j,u) {
if (!edges[i]) edges[i] = []
edges[i][j] = u
return select('#'+i+','+j)
}
let ret = [],
focus = constraints.protagonist,
cast = {},
edge = edges.length
constraints.goals.forEach((c) => {
let e = c.event, exp = eventer[e[0]]
let str = exp.length != 0 ? choose(...exp)
: e[0] + ' {} {}'.repeat(e.length-1)
console.log(e[0])
let bindings = e.map((u,i) => {
if (i==0) return [focus]
console.log(e[i])
let path = e[i].split('.'),
prev = focus
let res = path.reduce((tail, head) => {
let board = select(head).select('#'+prev)
// let fallback = cast[head] ? cast[head] : edges.length
console.log(board.body[0])
let pick = board.isEmpty() ? link(prev, edge, head) : board.pickOne()
let res = board.isEmpty() ? edge++ : pick.body[0].name.split(',')[1]
prev = res
tail.push(parseInt(res))
return tail
}, []) // defines res, the sequence of nodes matching path
return res
}) // defines bindings
let munged = bindings.reduce((U,u) => U.concat(u))
ret.push(format(str, ...munged.map(i => char[i])))
})
return ret
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
class Rel {
constructor(body) {
this.body = [...body] // list of bonds
}
select(kind, verbose = false) {
// e.g. select(kin.daughter)
if (verbose) console.log(this.body.map(u => u.tags[0]))
if (!kind) return undefined
if (kind[0] == '#') {
let [a,b] = kind.slice(1).split(',')
let matches = (a,b) => (u) => {
let [au,bu] = u.name.split(',')
let p = a ? a == au : true,
q = b ? b == bu : true
return p && q
}
if (this.body.some(u => !u.name)) throw 'some bond is nameless. was the charmap exhausted?'
return new Rel(
this.body.filter(matches(a,b))
)
}
else {
// find tags matching 'kind' in each position specified
let test = (t) => kind.split('.').every((k,i) => (k == '*') || t.split('.')[i] == k)
return new Rel(
this.body.filter(u => u.tags.find(test))
)
}
}
pickOne(prioritize = (u) => 1) {
let weights = this.body.map(prioritize),
board = weights.reduce((acc, u) => acc + u, 0)
if (this.body.length == 0) {
// throw 'tried to pick from empty selection'
return undefined
}
// weighted choice within the selection
let roll = Math.floor(Math.random() * board),
i, counter
for (i = 0, counter = 0; i < weights.length; ++i) {
counter += weights[i]
if (counter > roll) break
}
return new Rel( [this.body[i]] )
}
push(kind) {
// select('').push('kin.sire')
// TODO: warn on mutate
}
pop(kind) {
// select('kith.strain.boiling').pop('kith')
// TODO: warn on absent
}
/* reverse() { // FIXME: mutates selection
return new Rel( this.body.map((u) => {
// HACK: lookup by reversing name
return bonds.find(v => v.name == [...u.name].reverse().join(''))
}))
} */
toString() {
return this.body.map(u => u.name.split(',').map(c=>char[c]).join(''))
}
isEmpty() {
return this.body.length == 0
}
}
Insert cell
edgeList = () =>
edges.reduce((U, u, i) => {
u.forEach((v,j) => {if (v) U.push({'name': `${i},${j}`, 'tags': [v]})}); return U
}, [])
Insert cell
function select(kind) {
return new Rel(edgeList()).select(kind)
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
improv = require('https://bundle.run/improv@1.0.0') // @sequitur, uses npm for lodash etc
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more