function turn(unit, units) {
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())
if (inRange.has(String(unit.p))) {
attack(unit, units)
} else {
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)
delete units[unit.p]
unit.p = step
units[step] = unit
if (inRange.has(String(unit.p)))
attack(unit, units)
}
}