Open Tin Tower and Whirl Islands: Suicune, Ho-Oh and Lugia (#32)
Tin Tower's floors and roof and the Whirl Islands are open. The Clear Bell gets the player past the sages to Suicune in Tin Tower; after the League, the Rainbow Wing leads up to Ho-Oh, and the Silver Wing to Lugia. New specials: MonCheck and BeastsCheck (CheckOwnMonAnywhere, with its five-letter OT comparison and no Day-Care search). The playthrough driver plans ledge jumps for the later stages, waterfall descents, warp panels and Surf between warps; the run gains two stages, Routes 45 and 46 back to Ecruteak and Tin Tower's Suicune. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9c3579937c
commit
3fbc6798eb
10 files changed
+369
-21
No files matched your search
+35
-11
@@ -36,6 +36,9 @@ export class Playthrough {
|
||||
readonly log: string[] = [];
|
||||
/** A wild species to throw Poké Balls at instead of fighting. */
|
||||
wanted = 0;
|
||||
/** Plan ledge jumps (Tin Tower, Route 45). Off for the earlier stages,
|
||||
* whose seeded runs were recorded walking around ledges. */
|
||||
ledges = false;
|
||||
seed = 0x2a;
|
||||
private stage = 0;
|
||||
readonly now = () => new Date("2026-09-22T12:00:00");
|
||||
@@ -227,10 +230,11 @@ export class Playthrough {
|
||||
if (world.x === x && world.y === y) return;
|
||||
// A surfer crosses water until it steps ashore.
|
||||
const water = world.state.playerState === PLAYER_SURF;
|
||||
for (const direction of findPath(world, x, y, { water })) {
|
||||
for (const direction of findPath(world, x, y, { water, ledges: this.ledges })) {
|
||||
const end = landing(world, world.x, world.y, direction, {
|
||||
water,
|
||||
target: { x, y },
|
||||
ledges: this.ledges,
|
||||
});
|
||||
if (!world.move(direction)) {
|
||||
for (let tick = 0; tick < 64; tick++) world.tick();
|
||||
@@ -255,7 +259,11 @@ export class Playthrough {
|
||||
this.play();
|
||||
expect(world.mapId, `sailing to ${x},${y}`).toBe(map);
|
||||
if (world.x === x && world.y === y) return;
|
||||
for (const direction of findPath(world, x, y, { water: true, whirlpools })) {
|
||||
for (const direction of findPath(world, x, y, {
|
||||
water: true,
|
||||
whirlpools,
|
||||
ledges: this.ledges,
|
||||
})) {
|
||||
const [dx, dy] = STEP[direction];
|
||||
const from = [world.x, world.y];
|
||||
const ahead = world.assets.collision(world.map, from[0] + dx, from[1] + dy);
|
||||
@@ -298,8 +306,10 @@ export class Playthrough {
|
||||
? [dx < 0 ? 0 : world.map.width - 1, i]
|
||||
: [i, dy < 0 ? 0 : world.map.height - 1],
|
||||
).flatMap(([x, y]) => {
|
||||
// A warp on the edge (a gate's doorway) is not the connection.
|
||||
if (world.map.warps.some((warp) => warp.x === x && warp.y === y)) return [];
|
||||
try {
|
||||
return [{ x, y, length: findPath(world, x, y, { water }).length }];
|
||||
return [{ x, y, length: findPath(world, x, y, { water, ledges: this.ledges }).length }];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
@@ -426,7 +436,7 @@ export class Playthrough {
|
||||
const world = this.world;
|
||||
for (const warp of world.map.warps.filter((warp) => warp.targetMap === target)) {
|
||||
try {
|
||||
findPath(world, warp.x, warp.y);
|
||||
findPath(world, warp.x, warp.y, { ledges: this.ledges });
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
@@ -452,7 +462,7 @@ export class Playthrough {
|
||||
const reachable = () => {
|
||||
if (!goal) return true;
|
||||
try {
|
||||
findPath(world, goal.x, goal.y);
|
||||
findPath(world, goal.x, goal.y, { ledges: this.ledges });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
@@ -464,7 +474,7 @@ export class Playthrough {
|
||||
for (const warp of world.map.warps) {
|
||||
if (!maps.includes(warp.targetMap) && warp.targetMap !== target) continue;
|
||||
try {
|
||||
findPath(world, warp.x, warp.y);
|
||||
findPath(world, warp.x, warp.y, { ledges: this.ledges });
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
@@ -480,23 +490,37 @@ export class Playthrough {
|
||||
};
|
||||
/** The fewest warps (stairs, holes, doors) among `maps` to `target`:
|
||||
* breadth first over the places each warp lands, so a maze of floors
|
||||
* whose holes drop into cut-off corners is solved without looping. */
|
||||
travel = (target: string, maps: string[]) => {
|
||||
* whose holes drop into cut-off corners is solved without looping.
|
||||
* `water` lets a player with Surf and Whirlpool sail to the warps. */
|
||||
travel = (target: string, maps: string[], { water = false } = {}) => {
|
||||
const world = this.world;
|
||||
const queue = [world.save()];
|
||||
const seen = new Set([`${world.mapId} ${world.x},${world.y}`]);
|
||||
for (let i = 0; i < queue.length && i < 200; i++) {
|
||||
for (let i = 0; i < queue.length && i < 1000; i++) {
|
||||
world.restore(queue[i]);
|
||||
const from = world.mapId;
|
||||
for (const warp of world.map.warps) {
|
||||
if (!maps.includes(warp.targetMap) && warp.targetMap !== target) continue;
|
||||
world.restore(queue[i]);
|
||||
try {
|
||||
findPath(world, warp.x, warp.y);
|
||||
findPath(world, warp.x, warp.y, { water, whirlpools: water, ledges: this.ledges });
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
this.walk(warp.x, warp.y);
|
||||
// A surfer reaches a cave's stairs over water, then steps on.
|
||||
if (water) {
|
||||
try {
|
||||
this.sail(warp.x, warp.y, { whirlpools: true });
|
||||
} catch {
|
||||
if (world.mapId === from) continue;
|
||||
}
|
||||
} else
|
||||
try {
|
||||
this.walk(warp.x, warp.y);
|
||||
} catch {
|
||||
// A warp panel on the way can land elsewhere on the same map.
|
||||
if (world.mapId === from) continue;
|
||||
}
|
||||
const exit = WARP_EXITS[world.assets.collision(world.map, warp.x, warp.y)];
|
||||
if (world.mapId === from && exit && world.move(exit)) this.play();
|
||||
if (world.mapId === from) continue;
|
||||
|
||||
Reference in new issue
Block a user