M9a: stationer i flera våningar med trappor
All checks were successful
release / build-release (push) Successful in 1m34s
All checks were successful
release / build-release (push) Successful in 1m34s
- level: Point har Z; banor kan ha floors (lika stora rutnät med namn), gamla grid-banor läses som en våning; trappor U/D på samma x,y en våning upp/ner valideras parvis; Neighbors ger grannar över våningar så att Path, lösbarhet, spärregel och Unreachable fungerar; skyltar har z - sim: att kliva på en trappa flyttar spelaren till andra änden (FloorChanges); dimman är per våning och avslöjar trappans andra ände; tåg hörs/syns bara på egen våning - game: bara spelarens våning ritas (följare på andra våningar döljs), HUD och minikarta visar våningens namn, trappgrafik i genart och färg på minikartan - bana 004 Tokyo (svårighet 3, tre i sällskapet): start under mark på B1, trappa upp till 1F med perronger; generatorn gör våningar med trappor som flaskhalsar; ordboken utökad - testbotar och tools/shots förstår trappor; två våningsbilder - docs/README/wiki uppdaterade Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -85,21 +85,29 @@ func dirTo(from, to level.Point) Dir {
|
||||
// walk låter en bot följa en väg ruta för ruta, en bildruta i taget. Tangenten
|
||||
// släpps så fort steget mot nästa ruta påbörjats, så att boten stannar exakt
|
||||
// där även när farten inte är bildruteexakt (telefon- eller sällskapsfaktor).
|
||||
// Trappor: att kliva på en trappruta flyttar en till andra änden i samma
|
||||
// steg, så vägens nästa punkt (motparten) är redan nådd.
|
||||
func walk(t *testing.T, r *Run, path []level.Point) {
|
||||
t.Helper()
|
||||
for _, next := range path[1:] {
|
||||
d := dirTo(r.Player.Tile, next)
|
||||
for i := 0; i < 600; i++ {
|
||||
if r.Player.Tile == next && !r.Player.Moving() {
|
||||
break
|
||||
}
|
||||
if r.Player.Target == next || r.Player.Tile == next {
|
||||
for idx := 1; idx < len(path); idx++ {
|
||||
next := path[idx]
|
||||
cur := r.Player.Tile
|
||||
if next.Z != cur.Z && next.X == cur.X && next.Y == cur.Y {
|
||||
continue // våningsbytet skedde när vi klev på trappan
|
||||
}
|
||||
d := dirTo(cur, next)
|
||||
arrived := func() bool {
|
||||
p := r.Player.Tile
|
||||
return !r.Player.Moving() && (p == next || (r.Level.At(next).IsStairs() && p.X == next.X && p.Y == next.Y))
|
||||
}
|
||||
for i := 0; i < 600 && !arrived(); i++ {
|
||||
if r.Player.Target == next || r.Player.Tile == next || (r.Player.Tile.X == next.X && r.Player.Tile.Y == next.Y) {
|
||||
r.Step(frame, None, false)
|
||||
} else {
|
||||
r.Step(frame, d, false)
|
||||
}
|
||||
}
|
||||
if r.Player.Tile != next || r.Player.Moving() {
|
||||
if !arrived() {
|
||||
t.Fatalf("boten fastnade på %v på väg mot %v", r.Player.Tile, next)
|
||||
}
|
||||
}
|
||||
@@ -203,7 +211,7 @@ func TestGateNeedsCardBalance(t *testing.T) {
|
||||
r.Player.Tile, r.Player.Target = gate, gate
|
||||
r.Wallet.Card = 0
|
||||
other := level.Point{X: gate.X + 1, Y: gate.Y}
|
||||
if l.At(other.X, other.Y) == level.Gate && !r.CanEnter(other) {
|
||||
if l.At(other) == level.Gate && !r.CanEnter(other) {
|
||||
t.Fatal("att gå mellan två spärrutor ska inte kosta")
|
||||
}
|
||||
}
|
||||
@@ -635,7 +643,7 @@ func TestPhoneSpeedRescanAndBattery(t *testing.T) {
|
||||
gate := l.Find(level.Gate)[0]
|
||||
path := l.Path(r.Player.Tile, gate, r.CanEnter)
|
||||
for i, p := range path { // stanna på den första spärrutan – där sker skanningen
|
||||
if l.At(p.X, p.Y) == level.Gate {
|
||||
if l.At(p) == level.Gate {
|
||||
path = path[:i+1]
|
||||
break
|
||||
}
|
||||
@@ -810,7 +818,7 @@ func TestIndividualCardsAtGateAndMachine(t *testing.T) {
|
||||
before := r.Wallet.Spent
|
||||
path := l1.Path(r.Player.Tile, gate, r.CanEnter)
|
||||
for i, p := range path {
|
||||
if l1.At(p.X, p.Y) == level.Gate {
|
||||
if l1.At(p) == level.Gate {
|
||||
path = path[:i+1]
|
||||
break
|
||||
}
|
||||
@@ -981,7 +989,7 @@ func TestRunOptionsFromProgression(t *testing.T) {
|
||||
}
|
||||
path := l.Path(r.Player.Tile, gate, r.CanEnter)
|
||||
for i, p := range path {
|
||||
if l.At(p.X, p.Y) == level.Gate {
|
||||
if l.At(p) == level.Gate {
|
||||
path = path[:i+1]
|
||||
break
|
||||
}
|
||||
@@ -1001,3 +1009,58 @@ func TestRunOptionsFromProgression(t *testing.T) {
|
||||
t.Fatal("landmärken från skillen")
|
||||
}
|
||||
}
|
||||
|
||||
const stairsLevel = `{
|
||||
"id": "stairs", "name": {"sv": "Trappor"}, "difficulty": 1, "destination": {"sv": "x", "ja": "新宿"},
|
||||
"floors": [
|
||||
{"name": "B1", "grid": ["#######", "#S..U.#", "#######"]},
|
||||
{"name": "1F", "grid": ["#######", "#.P.D.#", "#######"]}
|
||||
],
|
||||
"trains": [{"platform": "P", "track": "1", "line": {"en": "A"}, "destination": {"en": "B", "ja": "新宿"}, "correct": true}]
|
||||
}`
|
||||
|
||||
func TestStairsMoveBetweenFloors(t *testing.T) {
|
||||
l, err := level.Parse([]byte(stairsLevel))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r := NewRun(l, RunOptions{Seed: rng.FromInt(1)})
|
||||
if r.Player.Tile.Z != 0 || r.Fog.Seen(level.Point{X: 2, Y: 1, Z: 1}) {
|
||||
t.Fatal("startar på B1; 1F är inte sedd än (utom trappans ände)")
|
||||
}
|
||||
walk(t, r, []level.Point{pt(1, 1), pt(2, 1), pt(3, 1)})
|
||||
if !r.Fog.Seen(level.Point{X: 4, Y: 1, Z: 1}) {
|
||||
t.Fatal("nära trappan avslöjas dess andra ände")
|
||||
}
|
||||
// Gå in på trappan: hamnar på 1F i trappan ner.
|
||||
for i := 0; i < 120 && r.Player.Tile.Z == 0; i++ {
|
||||
r.Step(frame, Right, false)
|
||||
}
|
||||
if r.Player.Tile != (level.Point{X: 4, Y: 1, Z: 1}) || r.Player.Moving() || r.FloorChanges != 1 {
|
||||
t.Fatalf("efter trappan: %v moving=%v changes=%d", r.Player.Tile, r.Player.Moving(), r.FloorChanges)
|
||||
}
|
||||
if _, _, _, _, _, ok := r.NearTrack(2); !ok {
|
||||
t.Fatal("på 1F är perrongen nära")
|
||||
}
|
||||
// Dimman på 1F är nu avslöjad runt trappan.
|
||||
if !r.Fog.Seen(level.Point{X: 2, Y: 1, Z: 1}) {
|
||||
t.Fatal("dimman på 1F ska lätta runt trappan")
|
||||
}
|
||||
// Tillbaka ner: kliv av trappan och gå på den igen.
|
||||
walk(t, r, []level.Point{r.Player.Tile, {X: 3, Y: 1, Z: 1}})
|
||||
walk(t, r, []level.Point{{X: 3, Y: 1, Z: 1}, {X: 4, Y: 1, Z: 1}})
|
||||
if r.Player.Tile != (level.Point{X: 4, Y: 1, Z: 0}) || r.FloorChanges != 2 {
|
||||
t.Fatalf("tillbaka till B1: %v changes=%d", r.Player.Tile, r.FloorChanges)
|
||||
}
|
||||
// Hela vägen till perrongen med BFS över våningar. Man står i trappans
|
||||
// nedre ände, så kliv av först – trappan tar en när man kliver PÅ den.
|
||||
walk(t, r, []level.Point{r.Player.Tile, {X: 3, Y: 1, Z: 0}})
|
||||
path := l.Path(r.Player.Tile, l.CorrectPlatform(), r.CanEnter)
|
||||
if path == nil {
|
||||
t.Fatal("ingen väg")
|
||||
}
|
||||
walk(t, r, path)
|
||||
if r.Player.Tile != l.CorrectPlatform() {
|
||||
t.Fatalf("boten nådde inte perrongen: %v", r.Player.Tile)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user