[TASK] Solved Day 17

This commit is contained in:
2021-12-19 14:42:04 +01:00
parent 2c3c1f8f08
commit 9fc38b9b88

View File

@@ -23,14 +23,6 @@ class Day17 implements Day {
// steps = 5 -> 0-1-2-3-4 = -10
// steps = 6 -> 0-1-2-3-4-5 = -15
// v = -1 * x
// Theoretically, the displacement should be:
// s(t) = s_0 + v_0 t + 1/2 a t^2
// Filling that in for s_0 = 0 and a = -1, we get:
// s(t) = v_0 t - 1/2 t ^ 2
// But that gives us non-integer values, for example when t = 3, we get: v_0 * 3 - 4.5
part1(input: string[]): number | string {
const targetArea = TargetArea.parse(input[0]);
@@ -41,20 +33,21 @@ class Day17 implements Day {
// Note that the triangle value of x must be at least the minimal x of the target area, otherwise the probe won't
// reach the target area before it's velocity reaches zero:
const vx0_min = this.triangleInvCeil(targetArea.minX);
const v_x0_min = Day17.triangleInvCeil(targetArea.minX);
// Calculate the x-position we reach after v_x0 steps:
const x_min = this.triangle(vx0_min);
const x_min = Day17.triangle(v_x0_min);
if (targetArea.isInX(x_min)) {
// We have an x-speed that reaches the target area in x_min steps and stays there (e.g. reaches velocity zero in the target area),
// so we can just find the biggest y that at some point ends up in the target area.
// We already know that the y speed when the "probe" comes down is:
// -v_y0 - 1
// So if we choose as starting y that is one higher than the minimal y value, we end up in the bottom of the target area, while reaching the maximum height:
const vy0_max = Math.abs(targetArea.minY) - 1;
return this.triangle(vy0_max);
// So if we choose as starting y that is one higher than the minimal y value, we end up in the bottom of the target area, while
// reaching the maximum height:
const v_y0_max = Math.abs(targetArea.minY) - 1;
return Day17.triangle(v_y0_max);
} else {
throw Error("fuck :)");
throw Error("Unsupported target area, only a target area with an x values that is a triangle number is supported");
}
}
@@ -66,33 +59,144 @@ class Day17 implements Day {
const targetArea = TargetArea.parse(input[0]);
const v_x0_min = Day17.triangleInvCeil(targetArea.minX);
const v_x0_max_static = Day17.triangleInvFloor(targetArea.maxX);
// const maxYSteps0 = Day17.triangleInvCeil(Math.abs(targetArea.minY));
// TODO implement
return 0;
}
simulateY(initialSpeedY: number, steps: number): { maxY: number, finalY: number} {
let solutionCount = 0;
let curSpeed = initialSpeedY;
let y = 0;
let maxY = 0;
for (let step = 0; step < steps; step++) {
y += curSpeed;
curSpeed--;
if (y > maxY) {
maxY = y;
for (let v_x0 = v_x0_min; v_x0 <= targetArea.maxX; v_x0++) {
const maxSteps = v_x0 <= v_x0_max_static ? 1000 : v_x0_max_static;
for (let v_y0 = targetArea.minY; v_y0 < Math.abs(targetArea.minY); v_y0++) {
for (let steps = 1; steps < maxSteps; steps++) {
const xRangeResult = this.isInXAfterSteps(v_x0, steps, targetArea);
if (xRangeResult === RangeResult.TOO_HIGH) {
break;
} if (xRangeResult === RangeResult.YES) {
// Check what y values work for this x and step count:
const yRangeResult = this.isInYAfterSteps(v_y0, steps, targetArea);
if (yRangeResult === RangeResult.YES) {
// console.log(`${v_x0}, ${v_y0}`)
solutionCount++;
break;
}
}
}
}
}
return { finalY: y, maxY: maxY };
return solutionCount;
// // We gonna have to semi-brute-force anyway,
//
// // But first, let's find all x-es that perpetually stay inside the target area
// const v_x0_min = Day17.triangleInvCeil(targetArea.minX);
// const v_x0_max_static = Day17.triangleInvFloor(targetArea.maxX);
//
// // const vxStaticCount = (v_x0_max_static - v_x0_min) + 1;
// //
// // Then we count all y's that end up in the target area after v_x0_min steps
// // Note that any
// // if (v_x0_min * 2 + 1 > Math.abs(targetArea.maxY) + 1);
// let vyStaticCount = 0;
// // With a start y speed of 0, we can stay in the target area for at most this many steps:
// const maxYSteps0 = Day17.triangleInvCeil(Math.abs(targetArea.minY));
//
// for (let vy_0 = targetArea.minY; vy_0 < Math.abs(targetArea.minY); vy_0++) {
// const minSteps = vy_0 > 0 ? Math.max(v_x0_min, 2 * vy_0 + 1) : v_x0_min;
// const maxSteps = vy_0 > 0 ? minSteps + maxYSteps0 : maxYSteps0;
// for (let steps = minSteps; steps < maxSteps; steps++) {
// const rangeResult = this.isInYAfterSteps(vy_0, steps, targetArea);
// if (rangeResult === RangeResult.YES) {
// vyStaticCount++;
// break;
// } else if (rangeResult === RangeResult.TOO_LOW) {
// // We've fallen below the target area, no need to scan this range any further
// break;
// }
// }
// }
//
// const staticSolutions = (v_x0_max_static - v_x0_min + 1) * vyStaticCount;
//
// // Any more numbers of steps will overshoot the x direction
// let dynamicSolutions = 0;
// for (let steps = 1; steps <= v_x0_max_static; steps++) {
// for (let vx_0 = v_x0_min; vx_0 < targetArea.maxX + 1; vx_0++) {
// if (this.isInXAfterSteps(vx_0, steps, targetArea) !== RangeResult.YES) continue;
// for (let vy_0 = targetArea.minY; vy_0 < Math.abs(targetArea.minY) - 1; vy_0++) {
// const rangeResult = this.isInYAfterSteps(vy_0, steps, targetArea);
// if (rangeResult === RangeResult.YES) {
// dynamicSolutions++;
// } else if (rangeResult === RangeResult.TOO_HIGH) {
// // We've fallen below the target area
// break;
// }
// }
// }
// }
// return dynamicSolutions + staticSolutions;
}
triangle(n: number): number {
isInXAfterSteps(v_x0: number, steps: number, targetArea: TargetArea): RangeResult {
if (steps >= v_x0) {
// reached final position, which is the triangle number of the initial speed:
return targetArea.isInX(Day17.triangle(v_x0));
} else {
// On our way to the final position:
const x = Day17.triangle(v_x0) - Day17.triangle(v_x0 - steps);
return targetArea.isInX(x);
}
}
isInYAfterSteps(v_y0: number, steps: number, targetArea: TargetArea): RangeResult {
if (targetArea.maxY >= 0) throw Error("Only target area's below the starting point are supported")
if (v_y0 > 0) {
if (steps < 2 * v_y0) {
// We haven't gone below the
return RangeResult.TOO_HIGH;
} else {
// This is equivalent to firing with a negative y speed after v_y0 * 2 + 1 steps (so the steps left = steps - 2 * v_y0 - 1)
return this.isInYAfterSteps(-v_y0 - 1, steps - 2 * v_y0 - 1, targetArea);
}
} else {
// Firing downward:
const y = - Day17.calcPositionAfterIncreasingVelocity(Math.abs(v_y0), steps);
return targetArea.isInY(y);
}
}
static calcPositionAfterIncreasingVelocity(v: number, steps: number): number {
if (v < 0) throw Error("Velocity must be positive");
return Day17.triangle(v - 1 + steps) - Day17.triangle(v - 1);
}
static triangle(n: number): number {
return (n*(n + 1)) / 2;
}
triangleInvCeil(x: number): number {
return Math.ceil((Math.sqrt(8 * x + 1) - 1) / 2);
static triangleInvCeil(x: number): number {
return Math.ceil(this.triangleInv(x));
}
static triangleInvFloor(x: number): number {
return Math.floor(this.triangleInv(x));
}
static triangleInv(x: number): number {
return (Math.sqrt(8 * x + 1) - 1) / 2;
}
}
enum RangeResult {
TOO_LOW = -1,
TOO_HIGH = 0,
YES = 1
}
class TargetArea {
@@ -126,16 +230,20 @@ class TargetArea {
)
}
isInX(x: number): boolean {
return x >= this.minX && x <= this.maxX;
isInX(x: number): RangeResult {
if (x < this.minX) return RangeResult.TOO_LOW;
else if (x > this.maxX) return RangeResult.TOO_HIGH;
else return RangeResult.YES;
}
isInY(y: number): boolean {
return y >= this.minY && y <= this.maxY;
isInY(y: number): RangeResult {
if (y < this.minY) return RangeResult.TOO_LOW;
else if (y > this.maxY) return RangeResult.TOO_HIGH;
else return RangeResult.YES;
}
isIn(x: number, y: number): boolean {
return this.isInX(x) && this.isInY(y);
return this.isInX(x) === RangeResult.YES && this.isInY(y) === RangeResult.YES;
}
}