Published
Edited
Dec 22, 2021
2 stars
Insert cell
Insert cell
Insert cell
wires = (await FileAttachment("day3input.txt").text())
.trim()
.split('\n')
.map(line =>
line
.split(',')
.map(x => ({ direction: x[0], magnitude: parseInt(x.slice(1)) }))
)
Insert cell
Insert cell
Insert cell
turnPositions = [].concat.apply(
[],
wires.map(
wire =>
wire.reduce(
({ curPos: { x, y }, positions }, { direction, magnitude }) => {
const { dx, dy } = delta({ direction, magnitude });
x = x + dx;
y = y + dy;
positions.push({ x, y });
return { curPos: { x, y }, positions };
},
{ curPos: { x: 0, y: 0 }, positions: [] }
).positions
)
)
Insert cell
Insert cell
bounds = getBounds(turnPositions)
Insert cell
Insert cell
segments = wires.map(
wire =>
wire.reduce(
({ curPos: { x, y }, positions }, { direction, magnitude }) => {
const { dx, dy } = delta({ direction, magnitude });

const newX = x + dx;
const newY = y + dy;
positions.push({
xMin: Math.min(x, newX),
xMax: Math.max(x, newX),
yMin: Math.min(y, newY),
yMax: Math.max(y, newY)
});
return { curPos: { x: newX, y: newY }, positions };
},
{ curPos: { x: 0, y: 0 }, positions: [] }
).positions
)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
positions = wires.map(
wire =>
wire.reduce(
({ curPos: { x, y, d }, positions }, { direction, magnitude }) => {
const { dx, dy } = delta({ direction, magnitude });

const destX = x + dx;
const destY = y + dy;
while (x !== destX || y !== destY) {
d++;
x = x + Math.sign(dx);
y = y + Math.sign(dy);
positions.push({ x, y, d });
}

return { curPos: { x, y, d }, positions };
},
{ curPos: { x: 0, y: 0, d: 0 }, positions: [{ x: 0, y: 0, d: 0 }] }
).positions
)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
async function buildModule(numBytes) {
const pageSize = 65536;
const memory = new WebAssembly.Memory({
initial: Math.ceil(numBytes / pageSize)
});
const spots = new Int8Array(memory.buffer);
const importObj = {
e: {
m: memory
}
};

const linear = codeWithModuleBoilerplate(testCode);
const linearBuffer = new Uint8Array(linear);
const module = await WebAssembly.compile(linearBuffer);
const instance = await WebAssembly.instantiate(module, importObj);
return spots;
}
Insert cell
buildModule(1)
Insert cell
bigMemory = buildModule(1024 * 1024 * 1024)
Insert cell
Insert cell
Insert cell
Insert cell
binaryen = require("binaryen@100")
Insert cell
Insert cell
module = new binaryen.Module()
Insert cell
exampleModule = {
const module = new binaryen.Module();
//module.autoDrop(); // automatically pops values off the stack if they aren't last in a block
module.addFunctionImport('print', 'env', 'print', [binaryen.i32], null);

module.addFunction(
"main",
[],
binaryen.i32,
[],
module.block(null, [module.return(module.i32.const(42))])
);
module.addFunctionExport("main", "main");

if (!module.validate()) {
throw new Error("validation error");
}

return module;
}
Insert cell
Insert cell
exampleModule.emitText()
Insert cell
Insert cell
runWASM = module => {
const importObj = {
env: {
print: x => {
console.log(x);
}
}
};

var wasmData = module.emitBinary();
var compiled = new WebAssembly.Module(wasmData);
var instance = new WebAssembly.Instance(compiled, importObj);
return instance.exports.main();
}
Insert cell
runWASM(exampleModule)
Insert cell
Insert cell
Insert cell
buildPart1Module = () => {
const module = new binaryen.Module();
//module.autoDrop(); // automatically pops values off the stack if they aren't last in a block

// Just for debugging
module.addFunctionImport(
'print',
'env',
'print',
[binaryen.i32],
binaryen.i32
);

for (let name of ['xMin', 'xMax', 'yMin', 'yMax']) {
module.addGlobal(name, binaryen.i32, 1, module.i32.const(0));
}

const memSize = Math.floor((1024 * 1024 * 1024) / 65536);
module.setMemory(memSize, memSize, 'memory');

module.addFunction(
"setBounds",
binaryen.createType([
binaryen.i32,
binaryen.i32,
binaryen.i32,
binaryen.i32
]),
binaryen.i32,
[],
module.block(null, [
...['xMin', 'xMax', 'yMin', 'yMax'].map((name, i) =>
module.global.set(name, module.local.get(i, binaryen.i32))
),

//see if memory is enough
module.if(
module.i32.lt_s(
module.memory.size(),
module.i32.div_s(
module.i32.mul(
module.i32.add(
module.i32.sub(
module.global.get('xMax', binaryen.i32),
module.global.get('yMax', binaryen.i32)
),
module.i32.const(1)
),
module.i32.add(
module.i32.sub(
module.global.get('yMax', binaryen.i32),
module.global.get('yMin', binaryen.i32)
),
module.i32.const(1)
)
),
module.i32.const(65536)
)
),
module.return(module.i32.const(1))
),
module.return(module.i32.const(0))
])
);
module.addFunctionExport("setBounds", "setBounds");

/** Get location of spot at x and y (local parameters 0 and 1) in linear memory */
const arrayMath = () => {
return module.i32.add(
// x - xMin
module.i32.sub(
module.local.get(0, binaryen.i32),
module.global.get('xMin', binaryen.i32)
),
// (y - yMin) * (xMax - xMin + 1)
module.i32.mul(
module.i32.sub(
module.local.get(1, binaryen.i32),
module.global.get('yMin', binaryen.i32)
),
module.i32.add(
module.i32.sub(
module.global.get('xMax', binaryen.i32),
module.global.get('xMin', binaryen.i32)
),
module.i32.const(1)
)
)
);
};

module.addFunction(
"recordSpot",
binaryen.createType([binaryen.i32, binaryen.i32]),
null,
[],
module.block(null, [
module.i32.store8(0, 0, arrayMath(), module.i32.const(1))
])
);
module.addFunctionExport("recordSpot", "recordSpot");

module.addFunction(
"checkSpot",
binaryen.createType([binaryen.i32, binaryen.i32]),
binaryen.i32,
[],
module.block(null, [module.return(module.i32.load8_s(0, 0, arrayMath()))])
);
module.addFunctionExport("checkSpot", "checkSpot");

if (!module.validate()) {
throw new Error("validation error");
}

return module;
}
Insert cell
constructPart1Module = () => {
const importObj = {
env: {
print: x => {
console.log(x);
return x;
}
}
};

var module = buildPart1Module();
var wasmData = module.emitBinary();
var compiled = new WebAssembly.Module(wasmData);
return new WebAssembly.Instance(compiled, importObj);
}
Insert cell
part1Result = {
const crossings = [[], []];

for (let i of [0, 1]) {
const instance = constructPart1Module();
const result = instance.exports.setBounds(
bounds.xMin,
bounds.xMax,
bounds.yMin,
bounds.yMax
);
if (result) {
throw Error(
'Linear memory of ' +
instance.exports.memory.buffer.byteLength +
' bytes not large enough for bounds ' +
bounds
);
}

const recordWire = positions[i];
const checkWire = positions[1 - i];

for (let { x, y } of recordWire) {
//console.log('recording spot:', x, y);
try {
instance.exports.recordSpot(x, y);
} catch (e) {
console.log(x, y);
console.log(
'should be',
x - bounds.xMin,
y - bounds.yMin,
x - bounds.xMin + bounds.width * (y - bounds.yMin)
);
throw e;
}
}

let d = 0;
for (let { x, y } of checkWire) {
//console.log('checking spot:', x, y);
const result = instance.exports.checkSpot(x, y);
if (result) {
crossings[i].push({ x, y, d });
}
d++;
}
}

return crossings;
}
Insert cell
Insert cell
crossingLocations = {
const crossingLocations = {};
const key = obj => obj.x + ":" + obj.y;
for (const crossingSet of part1Result) {
for (const crossing of crossingSet) {
if (!crossingLocations[key(crossing)]) {
crossingLocations[key(crossing)] = { ...crossing };
crossingLocations[key(crossing)].manhattan =
Math.abs(crossing.x) + Math.abs(crossing.y);
} else {
crossingLocations[key(crossing)].d1 =
crossingLocations[key(crossing)].d;
crossingLocations[key(crossing)].d2 = crossing.d;
crossingLocations[key(crossing)].d =
crossingLocations[key(crossing)].d + crossing.d;
}
}
}
return Object.values(crossingLocations);
}
Insert cell
closestToOrigin = {
const crossings = crossingLocations.slice();
crossings.sort((a, b) => a.manhattan - b.manhattan);
// ignore first item, that's the crossing point at the origin
return crossings[1].manhattan;
}
Insert cell
Insert cell
fewestCombinedSteps = {
const crossings = crossingLocations.slice();
crossings.sort((a, b) => a.d - b.d);
// ignore first item, that's the crossing point at the origin
return crossings[1].d;
}
Insert cell
data = segments
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