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

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more