Skip to content

Commit

Permalink
Add minecraft visualization
Browse files Browse the repository at this point in the history
  • Loading branch information
letelete committed Dec 23, 2024
1 parent 069994e commit 765ac93
Show file tree
Hide file tree
Showing 3 changed files with 304 additions and 12 deletions.
18 changes: 9 additions & 9 deletions 2024/days/day-20/README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
# Benchmark

```
Platform: darwin arm64
CPU: Apple M3 Pro 11 Cores
Memory: 18.00 GB
Platform: win32 x64
CPU: Intel(R) Core(TM) i7-14700KF 28 Cores
Memory: 31.84 GB
```

## Sample 1

| part | time (~) | μs |
| ---- | -------- | ------------------ |
| 1 | 3.69ms | 3.6905 |
| 2 | 0.63ms | 0.6251669999999905 |
| 1 | 5.76ms | 5.7621 |
| 2 | 1.23ms | 1.2307000000000023 |

## Answer

| part | time (~) | μs |
| ---- | -------- | ------------------ |
| 1 | 117.15ms | 117.14849999999998 |
| 2 | 151.22ms | 151.219791 |
| part | time (~) | μs |
| ---- | -------- | -------- |
| 1 | 103.39ms | 103.3902 |
| 2 | 402.32ms | 402.3219 |
16 changes: 13 additions & 3 deletions 2024/days/day-20/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function cheat(path, at, duration) {
const dist = manhattanDist(path[at], path[i]);
const save = i - at - dist;
if (dist <= duration && save > 0) {
saved.push(save);
saved.push([save, { from: at, to: i }]);
}
}
return saved;
Expand All @@ -70,7 +70,7 @@ function getAllCheats(path, maxDuration) {

function countOptimalCheats(cheats, minTimeSaved) {
return cheats.reduce(
(sum, timeSaved) => (timeSaved >= minTimeSaved ? sum + 1 : sum),
(sum, [timeSaved]) => (timeSaved >= minTimeSaved ? sum + 1 : sum),
0
);
}
Expand All @@ -95,4 +95,14 @@ function part2(data) {
return countOptimalCheats(cheats, 100);
}

module.exports = { parse, part1, part2 };
module.exports = {
parse,
part1,
part2,
symbols,
findTilePos,
getPath,
cheat,
getAllCheats,
countOptimalCheats,
};
282 changes: 282 additions & 0 deletions 2024/days/day-20/minecraft/generator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
const fs = require('fs');
const crypto = require('crypto');

const colors = {
white: 0,
orange: 1,
magenta: 2,
light_blue: 3,
yellow: 4,
lime: 5,
pink: 6,
gray: 7,
light_gray: 8,
cyan: 9,
purple: 10,
blue: 11,
brown: 12,
green: 13,
red: 14,
black: 15,
};

const config = {
pathFileLoad: './load.mcscript',
pathFileTick: './main.mcscript',
pathFileSolution: '../main.js',
pathFileData: '../in.sample.txt',

codeIndentSpaces: 4,

racetrackY: 55,
racetrackGroundBlock: 'minecraft:moss_block',
racetrackWallBlocks: ['minecraft:moss_block', 'minecraft:moss_block'],
racetrackStartBlock: 'minecraft:glowstone',
racetrackEndBlock: 'minecraft:glowstone',

cheatDuration: 2,
cheatOptimalMinSave: 0,

actorColorMain: colors.white,
actorColorCheater: colors.red,
};

let state = {
actors: {
main: null,
cheaters: [],
},
};

function indent(spaces = config.codeIndentSpaces) {
return new Array(spaces).fill(' ').join('');
}

function buildFunction(signatures, body) {
const declaration = signatures.map(([name, ...args]) =>
args.length ? `${name}(${args.join(', ')})` : `${name}`
);
return [
`${declaration.join(', ')} {`,
`${body.map((line) => `${indent()}${line}`).join('\n')}`,
`}`,
].join('\n');
}

function buildRacetrack(data, path, startPos, endPos) {
const [x, y, z] = [0, config.racetrackY, 0];
const sizeX = data.length;
const sizeZ = data[0].length;

const xyz0 = ({ _x, _y, _z } = {}) => `${_x ?? x} ${_y ?? y} ${_z ?? z}`;
const xyz1 = ({ _x, _y, _z } = {}) =>
`${_x ?? x + sizeX - 1} ${_y ?? y} ${_z ?? z + sizeZ - 1}`;

const body = [
`log("Generating race track ${sizeX} x ${sizeZ}")\n`,

'// remove all blocks in the racetrack area',
`/fill ${xyz0({ _x: x - 4, _z: z - 4 })} ${xyz1({
_x: x + sizeX + 3,
_y: y + 11,
_z: z + sizeZ + 3,
})} air replace`,

'// set racetrack ground',
`/fill ${xyz0()} ${xyz1()} ${config.racetrackGroundBlock} replace`,

'// fill racetrack with walls everywhere',
...config.racetrackWallBlocks.map((blockId, dy) => {
const _y = y + dy + 1;
return `/fill ${xyz0({ _y })} ${xyz1({ _y })} ${blockId} replace`;
}),

'// clear racetrack walls on path coordinates',
...path.map(
([_x, _z]) =>
`/fill ${xyz0({ _x, _y: y + 1, _z })} ${xyz1({
_x,
_y: y + 1 + config.racetrackWallBlocks.length,
_z,
})} air replace`
),

'// set racetrack start block',
`/setblock ${xyz0({ _x: startPos[0], _z: startPos[1] })} ${
config.racetrackStartBlock
}`,

'// set racetrack end block',
`/setblock ${xyz0({ _x: endPos[0], _z: endPos[1] })} ${
config.racetrackEndBlock
}`,
];

return buildFunction(
[
['as', '@a'],
['at', '@s'],
],
body
);
}

class Actor {
constructor(x, z, path, step, cheater, incrByTick = 0.5) {
this.entityId = 'minecraft:sheep';
this.tag = crypto.randomUUID();
this.x = x;
this.y = config.racetrackY + 1;
this.z = z;
this.path = path;
this.step = step;
this.cheater = cheater;
this.incrByTick = incrByTick;
this.entityConfig = `{Tags:[${this.tag}],Invulnerable:1b,NoAI:1b,Color:${
cheater ? config.actorColorCheater : config.actorColorMain
}}`;
}

move() {
if (this.step + 1 >= this.path.length) {
return;
}
const [destX, destZ] = this.path[this.step + 1];
const [dx, dz] = [destX - this.x, destZ - this.z].map((delta) =>
delta >= 0
? Math.min(this.incrByTick, delta)
: Math.max(-this.incrByTick, delta)
);

this.x += dx;
this.z += dz;
if (this.x === destX && this.z === destZ) {
this.step++;
}
}

getXYZ() {
return `${this.x} ${this.y} ${this.z}`;
}
}

const buildSummonActor = (actor) => {
return `/summon ${actor.entityId} ${actor.getXYZ()} ${actor.entityConfig}`;
};

const buildMoveActor = (actor) => {
return `/tp @e[type=${actor.entityId},tag=${actor.tag}] ${actor.getXYZ()}`;
};

function buildInitActor(path, startPos) {
const actor = new Actor(startPos[0], startPos[1], path, 0, false);
const body = [`/kill @e[type=${actor.entityId}]`, buildSummonActor(actor)];

state = { ...state, actors: { ...state.actors, main: actor, cheaters: [] } };

return buildFunction(
[
['as', '@a'],
['at', '@s'],
],
body
);
}

function buildTicks(data, path, startPos, endPos, cheat) {
const actorReachedEnd = (actor) => {
return actor.x === endPos[0] && actor.z === endPos[1];
};

let tick = 0;

while (!actorReachedEnd(state.actors.main)) {
const fileName = `tick-${tick}`;
const body = [];

cheat(path, state.actors.main.step, config.cheatDuration).forEach(
([saved, { to }]) => {
if (saved >= config.cheatOptimalMinSave) {
const [x, y] = path[to];
const actor = new Actor(x, y, path, to, true);
state.actors.cheaters.push(actor);
body.push(buildSummonActor(actor));
}
}
);

[
state.actors.main,
...state.actors.cheaters
].forEach((actor) => {
actor.move();
body.push(buildMoveActor(actor));
});

if (!actorReachedEnd(state.actors.main)) {
body.push(`/schedule function oac:tick${tick + 1} 0.05s`);
}

fs.appendFileSync(
config.pathFileLoad,
buildFunction([[`function tick${tick}`]], body) + '\n'
);

tick++;
}
}

function buildSetupCamera(data) {
const [x, y, z] = [0, config.racetrackY, 0];
const sizeX = data.length;
const sizeZ = data[0].length;
const yFactor = 0.75;

const body = [
`/tp @s ${x + (sizeX - x) / 2} ${y + yFactor * Math.max(sizeX, sizeZ)} ${
z + (sizeZ - z) / 2
} 0 90`,
];

return buildFunction(
[
['as', '@a'],
['at', '@s'],
],
body
);
}

(() => {
const {
parse,
symbols,
findTilePos,
getPath,
cheat,
} = require(config.pathFileSolution);
const data = parse(fs.readFileSync(config.pathFileData, 'utf-8'));

const startPos = findTilePos(data, symbols.start);
const endPos = findTilePos(data, symbols.end);
const path = getPath(data, startPos, endPos);

fs.writeFileSync(
config.pathFileLoad,
[
'#file: ./load',
`log('AoC: Day 20 Initialized!')`,
'/gamerule doEntityDrops false',
'/gamerule doTileDrops false',
'/gamerule doTileDrops false',
'/time set night',
'// build racetrack',
buildRacetrack(data, path, startPos, endPos),
'// setup camera actor',
buildInitActor(path, startPos),
buildSetupCamera(data),
].join('\n\n') + '\n'
);

buildTicks(data, path, startPos, endPos, cheat);
})();

0 comments on commit 765ac93

Please sign in to comment.