Published
Edited
Nov 13, 2019
1 star
Insert cell
Insert cell
Insert cell
// part1 = {
// let n, units = _.cloneDeep(initial.units)
// yield units
// try {
// for (n = 0; n < 100; n++) {
// units = round(units)
// yield { units }
// }
// } catch(e) {
// yield { n, units: e.units, result: n * _.sum(Object.values(e.units).map(u => u.health)) }
// }
// }
Insert cell
Insert cell
function draw(map, units) {
const values = Object.entries(map).map(([p, type]) => ({
p: p.split(','),
type: type.type || type,
health: 200,
})).concat(Object.values(units))
// const svg = d3.select(DOM.svg(width, width))
// const g = svg.append('g')
// // .attr('transform', 'scale(10, 10)')
// const zoom = d3.scaleLinear()
// .domain(d3.extent(values, d => d.p[0]))
// .range([0, width])
// const text = g.selectAll('text')
// .data(values)
// text.enter()
// .append('text')
// .attr('x', d => zoom(d.p[0]))
// .attr('y', d => zoom(d.p[1]))
// .text(d => d.type)
// return svg.node()
return vegalite({
data: { values },
mark: 'text',
encoding: {
x: {
field: 'p[0]',
type: 'quantitative',
axis: { grid: false },
scale: { padding: 1 },
},
y: {
field: 'p[1]',
type: 'quantitative',
axis: { grid: false },
scale: { domain: d3.extent(values, d => +d.p[1]).reverse(), padding: 1 }
},
text: { field: 'type', type: 'nominal' },
color: { field: 'type', type: 'nominal' },
opacity: { field: 'health', type: 'quantitative', scale: { domain: [0, 200], range: [.1, 1] } },
}
})
}
Insert cell
function round(units) {
// units = _.cloneDeep(units)
for (const unit of _.sortBy(Object.values(units), c => readingOrder(c.p))) {
// TURN
if (unit.health > 0)
turn(unit, units)
}
return units
}
Insert cell
function attack(unit, units) {
const next = new Set([...adjacent(unit.p)].map(String))
const targets = Object.values(units).filter(other => other.type !== unit.type && next.has(String(other.p))),
minHealth = _.min(targets.map(t => t.health)),
unhealthy = targets.filter(t => t.health === minHealth)
const target = _.sortBy(unhealthy, t => readingOrder(t.p))[0]
target.health -= unit.attack
if (target.health <= 0)
delete units[target.p]
}
Insert cell
function turn(unit, units) {
// find targets
const targets = Object.values(units).filter(other => other.type !== unit.type)
if (!targets.length) {
const err = new Error('no targets!')
err.units = units
throw err
}
const inRange = _.transform(targets, (inRange, target) => {
for (const a of adjacent(target.p))
if (map[a] === '.')
inRange.add(String(a))
}, new Set())

// check attack
if (inRange.has(String(unit.p))) {
// attack
attack(unit, units)
} else {
// move
const paths = pathsFrom({...map, ...units}, unit.p),
reachable = [...inRange].filter(p => p in paths),
minDistance = _.min(reachable.map(p => paths[p][0].length)),
nearest = reachable.filter(p => paths[p][0].length === minDistance),
chosen = _.minBy(nearest, readingOrder)

if (!chosen)
return unit

const step = _.minBy(paths[chosen].map(p => p[0]), readingOrder)
// console.log({ unit, chosen, paths: paths[chosen], step, units })
delete units[unit.p]
unit.p = step
units[step] = unit
// check attack
if (inRange.has(String(unit.p)))
attack(unit, units)
}
}
Insert cell
{
try { return pathsFrom({...initial.map, ...initial.units}, [10,16]) }
catch(e) { return Object.keys(e.paths) }
}
Insert cell
function pathsFrom(map, p) {
const queue = [ { p, path: [] } ],
seen = {},
paths = {}
let n = 0
while (queue.length) {
const { p, path } = queue.shift()
if (path.slice(0, path.length-1).some(c => String(c) === String(p)))
continue
//seen[p] = true
for (const a of adjacent(p)) {
if (map[a] !== '.')
continue
const aPath = path.concat([ a ])
queue.push({ p: a, path: aPath })
if (!paths[a] || paths[a][0].length > aPath.length)
paths[a] = [ aPath ]
else if (paths[a][0].length === aPath.length)
paths[a].push(aPath)
}
if (++n > 10000) {
const err = new Error('Too many iterations while generating graph.')
err.paths = paths
err.queue = queue
throw err
}
// return paths
}
return paths
}
Insert cell
Insert cell
Insert cell
Insert cell
initial = {
const map = {},
units = {}
for (let y = 0; y < lines.length; y++) {
const line = lines[y].split('')
for (let x = 0; x < line.length; x++) {
const type = line[x], p = [x,y]
switch (type) {
case '.':
case '#':
map[p] = type
break
case 'E':
case 'G':
map[p] = '.'
units[p] = { p, type, health: 200, attack: 3 }
}
}
}
return { map, units }
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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