Notebooks 2.0 is here.

Public
Edited
Dec 17, 2022
Importers
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
parseMove = text => {
const [direction, steps] = text.split(' ')

return {direction: directionLookup[direction], steps: parseInt(steps)}
}
Insert cell
expandMove = ({direction, steps}) => Array.from({length: steps}).map(() => [...direction])
Insert cell
simulateRope = length => simulate(moveSnakeOneStep, Array.from({length}).map(() => [0, 0]))
Insert cell
prepareInput = R.pipe(
R.split('\n'),
R.map(parseMove),
R.chain(expandMove),
)
Insert cell
simulatePart1 = R.pipe(
prepareInput,
simulateRope(2)
)
Insert cell
computeTailPositionHash = R.pipe(
R.last,
R.join('/')
)
Insert cell
getUniqueTailPositions = R.pipe(
R.map(computeTailPositionHash),
x => new Set(x),
x => x.size
)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
simulatePart2 = R.pipe(
prepareInput,
simulateRope(10)
)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
visualize = (history, frame) => Generators.observe(notify => {
const positions = history.flatMap(R.identity)
const bounds = [
[Math.min(...positions.map(R.head)), Math.min(...positions.map(R.last))],
[Math.max(...positions.map(R.head)), Math.max(...positions.map(R.last))],
]

const snake = history[frame]
const tailTrace = history.slice(0, frame).map(R.last)

notify(renderFrame(bounds, snake, tailTrace))
})
Insert cell
renderFrame = ([min, max], snake, tailTrace) => {
const size = add(subtract(max, min), [1, 1])

const offset = subtract([0, 0], min)

const cells = Array.from({length: size[1]}).map(() => Array.from({length: size[0]}).fill(useAsciiVisualization ? '.' : '⬜'))

tailTrace.forEach(pos => {
const [x, y] = add(pos, offset)
cells[y][x] = useAsciiVisualization ? '#' : '🟪'
})
cells[offset[1]][offset[0]] = useAsciiVisualization ? 's' : '🏁'

const reversedSnake = [...snake].reverse()

reversedSnake.forEach((pos, i) => {
const [x, y] = add(pos, offset)

const tailIndex = reversedSnake.length - 1 - i

const symbol = useAsciiVisualization
? tailIndex || 'H'
: tailIndex ? `${String.fromCodePoint(0x30 + tailIndex)}${String.fromCodePoint(0xfe0f)}${String.fromCodePoint(0x20e3)}` : '🐍'
cells[y][x] = symbol
})

return htl.html`<pre>${cells.map(x => x.join('')).reverse().join('\n')}</pre>`
}
Insert cell
Insert cell
moveSnake = R.curry(({steps, direction}, snake) => simulate(moveSnakeOneStep, snake, new Array(steps).fill(direction)).slice(1))
Insert cell
Insert cell
moveSnakeOneStep = R.curry((direction, snake) => {
const [head, ...tail] = R.clone(snake)

const movedSnake = [add(head, direction), ...tail]
movedSnake.forEach((segment, i) => {
if (i === 0) return
movedSnake[i] = follow(movedSnake[i - 1], segment)
})

return movedSnake
})
Insert cell
follow = (target, segment) => {
const delta = subtract(target, segment)
const movement = delta.some(n => Math.abs(n) > 1) ? delta.map(n => Math.sign(n)) : [0, 0]
return add(segment, movement)
}
Insert cell
simulate = R.curry((f, initial, moves, {discardHistory} = {}) => R.reduce((history, move) => discardHistory ? [f(move, R.last(history))] : [...history, f(move, R.last(history))], [initial], moves))
Insert cell
add = R.pipe(R.zip, R.map(R.sum))
Insert cell
subtract = R.pipe(R.zip, R.map(R.apply(R.subtract)))
Insert cell
directionLookup = ({
L: [-1, 0],
R: [1, 0],
U: [0, 1],
D: [0, -1],
})
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