[TASK] Solved Day 17
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user