Rating
64.4
No. 2Per building
The Quiet Annexe
53.1
Morning Rush
75.4
Home Time
50.7
Lunch
79.0
The Tower
63.4
Bad Monday
64.9
Rating
64.4
No. 2Per building
The Quiet Annexe
53.1
Morning Rush
75.4
Home Time
50.7
Lunch
79.0
The Tower
63.4
Bad Monday
64.9
Everyone in the run, bucketed. The lines are the two thresholds you are scored on.
| Wait | People | Share |
|---|---|---|
| 0–15s | 9 | 15.8% |
| 15–30s | 9 | 15.8% |
| 30–45s | 7 | 12.3% |
| 45–60s | 5 | 8.8% |
| 60–75s | 3 | 5.3% |
| 90–105s | 1 | 1.8% |
| 150–165s | 2 | 3.5% |
| 180–195s | 1 | 1.8% |
| 195–210s | 4 | 7.0% |
| 210–225s | 1 | 1.8% |
| 225–240s | 3 | 5.3% |
| 240–255s | 1 | 1.8% |
| 255–270s | 1 | 1.8% |
| 270–285s | 3 | 5.3% |
| 300s+ | 7 | 12.3% |
Median and 90th percentile of how long each person waited, by the minute they got in.
| At | p50 | p90 | Waiting |
|---|---|---|---|
| 0s | 6.0s | 6.0s | 2 |
| 30s | 15.1s | 22.3s | 3 |
| 60s | 24.4s | 34.8s | 0 |
| 90s | 6.9s | 6.9s | 4 |
| 120s | 4.4s | 4.4s | 6 |
| 150s | 55.1s | 67.4s | 3 |
| 180s | 2.2s | 11.6s | 7 |
| 210s | 38.8s | 91.1s | 10 |
| 240s | 36.6s | 60.0s | 5 |
| 270s | 0.0s | 0.0s | 7 |
| 300s | 0.0s | 0.0s | 10 |
| 330s | 0.0s | 0.0s | 12 |
| 360s | 0.0s | 0.0s | 16 |
| 390s | 0.0s | 0.0s | 21 |
| 420s | 0.0s | 0.0s | 23 |
| 450s | 0.0s | 0.0s | 23 |
| 480s | 0.0s | 0.0s | 23 |
| 510s | 0.0s | 0.0s | 23 |
| 540s | 0.0s | 0.0s | 23 |
| 570s | 0.0s | 0.0s | 23 |
First seed in full
Everyone in the run, bucketed. The lines are the two thresholds you are scored on.
| Wait | People | Share |
|---|---|---|
| 0–15s | 194 | 70.5% |
| 15–30s | 63 | 22.9% |
| 30–45s | 16 | 5.8% |
| 45–60s | 2 | 0.7% |
Median and 90th percentile of how long each person waited, by the minute they got in.
| At | p50 | p90 | Waiting |
|---|---|---|---|
| 0s | 1.0s | 1.0s | 0 |
| 30s | 1.4s | 3.6s | 0 |
| 60s | 1.2s | 6.5s | 2 |
| 90s | 0.7s | 5.1s | 0 |
| 120s | 1.0s | 4.7s | 1 |
| 150s | 1.0s | 6.4s | 1 |
| 180s | 11.4s | 14.7s | 9 |
| 210s | 10.5s | 16.3s | 2 |
| 240s | 4.3s | 10.4s | 8 |
| 270s | 16.7s | 22.4s | 7 |
| 300s | 19.7s | 27.4s | 7 |
| 330s | 7.9s | 14.8s | 19 |
| 360s | 37.7s | 44.2s | 12 |
| 390s | 12.7s | 18.7s | 7 |
| 420s | 10.2s | 28.5s | 6 |
| 450s | 9.5s | 17.6s | 1 |
| 480s | 11.5s | 16.1s | 9 |
| 510s | 13.2s | 17.8s | 12 |
| 540s | 18.8s | 22.3s | 4 |
| 570s | 6.8s | 10.5s | 2 |
| 600s | 6.4s | 7.8s | 0 |
| 630s | 3.2s | 9.3s | 0 |
| 660s | 1.0s | 4.2s | 3 |
| 690s | 5.3s | 9.4s | 3 |
| 720s | 5.6s | 10.7s | 0 |
| 750s | 0.0s | 0.0s | 0 |
| 780s | 0.0s | 0.0s | 0 |
| 810s | 0.0s | 0.0s | 0 |
| 840s | 0.0s | 0.0s | 0 |
| 870s | 0.0s | 0.0s | 0 |
First seed in full
Everyone in the run, bucketed. The lines are the two thresholds you are scored on.
| Wait | People | Share |
|---|---|---|
| 0–15s | 136 | 45.2% |
| 15–30s | 32 | 10.6% |
| 30–45s | 22 | 7.3% |
| 45–60s | 18 | 6.0% |
| 60–75s | 12 | 4.0% |
| 75–90s | 7 | 2.3% |
| 90–105s | 8 | 2.7% |
| 105–120s | 7 | 2.3% |
| 120–135s | 5 | 1.7% |
| 135–150s | 1 | 0.3% |
| 150–165s | 6 | 2.0% |
| 165–180s | 5 | 1.7% |
| 180–195s | 7 | 2.3% |
| 195–210s | 6 | 2.0% |
| 210–225s | 5 | 1.7% |
| 225–240s | 9 | 3.0% |
| 240–255s | 2 | 0.7% |
| 270–285s | 3 | 1.0% |
| 285–300s | 2 | 0.7% |
| 300s+ | 8 | 2.7% |
Median and 90th percentile of how long each person waited, by the minute they got in.
| At | p50 | p90 | Waiting |
|---|---|---|---|
| 0s | 2.9s | 2.9s | 1 |
| 30s | 6.4s | 9.3s | 3 |
| 60s | 9.2s | 42.2s | 1 |
| 90s | 5.1s | 8.2s | 1 |
| 120s | 5.3s | 10.6s | 1 |
| 150s | 8.0s | 10.5s | 2 |
| 180s | 8.3s | 14.9s | 16 |
| 210s | 11.1s | 37.9s | 14 |
| 240s | 16.5s | 49.8s | 14 |
| 270s | 16.0s | 95.2s | 16 |
| 300s | 14.2s | 89.2s | 25 |
| 330s | 11.9s | 65.7s | 27 |
| 360s | 48.0s | 64.6s | 27 |
| 390s | 8.8s | 49.7s | 24 |
| 420s | 46.6s | 202.8s | 16 |
| 450s | 5.2s | 5.6s | 20 |
| 480s | 5.8s | 8.0s | 31 |
| 510s | 37.7s | 154.2s | 31 |
| 540s | 17.7s | 57.2s | 36 |
| 570s | 7.3s | 7.9s | 45 |
| 600s | 19.0s | 85.9s | 42 |
| 630s | 13.8s | 102.0s | 46 |
| 660s | 179.3s | 234.9s | 48 |
| 690s | 51.2s | 236.0s | 47 |
| 720s | 53.6s | 307.2s | 39 |
| 750s | 110.2s | 313.1s | 25 |
| 780s | 231.3s | 239.6s | 19 |
| 810s | 91.3s | 91.3s | 18 |
| 840s | 244.3s | 413.0s | 10 |
| 870s | 195.7s | 239.4s | 0 |
First seed in full
Everyone in the run, bucketed. The lines are the two thresholds you are scored on.
| Wait | People | Share |
|---|---|---|
| 0–15s | 230 | 62.2% |
| 15–30s | 93 | 25.1% |
| 30–45s | 21 | 5.7% |
| 45–60s | 17 | 4.6% |
| 60–75s | 2 | 0.5% |
| 75–90s | 1 | 0.3% |
| 90–105s | 1 | 0.3% |
| 135–150s | 5 | 1.4% |
Median and 90th percentile of how long each person waited, by the minute they got in.
| At | p50 | p90 | Waiting |
|---|---|---|---|
| 0s | 2.9s | 6.8s | 1 |
| 30s | 5.9s | 6.9s | 3 |
| 60s | 6.4s | 12.2s | 1 |
| 90s | 6.8s | 15.6s | 5 |
| 120s | 11.4s | 22.0s | 6 |
| 150s | 22.3s | 26.0s | 3 |
| 180s | 11.1s | 27.9s | 7 |
| 210s | 16.3s | 36.5s | 6 |
| 240s | 7.8s | 33.1s | 1 |
| 270s | 4.1s | 10.0s | 0 |
| 300s | 2.9s | 5.1s | 8 |
| 330s | 14.2s | 25.6s | 5 |
| 360s | 11.2s | 17.3s | 5 |
| 390s | 15.4s | 47.1s | 4 |
| 420s | 10.1s | 19.7s | 0 |
| 450s | 4.1s | 15.5s | 4 |
| 480s | 12.9s | 29.1s | 1 |
| 510s | 4.7s | 12.2s | 38 |
| 540s | 8.7s | 28.0s | 20 |
| 570s | 34.4s | 49.7s | 30 |
| 600s | 26.7s | 87.8s | 20 |
| 630s | 50.6s | 62.9s | 7 |
| 660s | 139.3s | 146.1s | 4 |
| 690s | 4.4s | 28.4s | 6 |
| 720s | 5.0s | 21.6s | 2 |
| 750s | 11.4s | 14.8s | 4 |
| 780s | 8.6s | 26.5s | 3 |
| 810s | 6.7s | 19.3s | 6 |
| 840s | 6.8s | 13.7s | 6 |
| 870s | 24.1s | 33.0s | 4 |
| 900s | 6.2s | 24.9s | 3 |
| 930s | 1.6s | 12.6s | 2 |
| 960s | 4.2s | 11.5s | 5 |
| 990s | 9.2s | 19.2s | 1 |
| 1020s | 3.4s | 5.1s | 6 |
| 1050s | 12.6s | 23.2s | 0 |
| 1080s | 0.0s | 0.0s | 0 |
| 1110s | 0.0s | 0.0s | 0 |
| 1140s | 0.0s | 0.0s | 0 |
| 1170s | 0.0s | 0.0s | 0 |
First seed in full
Everyone in the run, bucketed. The lines are the two thresholds you are scored on.
| Wait | People | Share |
|---|---|---|
| 0–15s | 377 | 60.5% |
| 15–30s | 154 | 24.7% |
| 30–45s | 49 | 7.9% |
| 45–60s | 27 | 4.3% |
| 60–75s | 8 | 1.3% |
| 75–90s | 2 | 0.3% |
| 90–105s | 3 | 0.5% |
| 105–120s | 1 | 0.2% |
| 150–165s | 2 | 0.3% |
Median and 90th percentile of how long each person waited, by the minute they got in.
| At | p50 | p90 | Waiting |
|---|---|---|---|
| 0s | 1.0s | 6.8s | 4 |
| 30s | 9.1s | 21.1s | 7 |
| 60s | 6.7s | 12.2s | 4 |
| 90s | 7.7s | 43.5s | 9 |
| 120s | 3.2s | 12.6s | 6 |
| 150s | 15.2s | 45.5s | 8 |
| 180s | 4.9s | 15.3s | 14 |
| 210s | 12.8s | 30.7s | 22 |
| 240s | 19.9s | 36.1s | 16 |
| 270s | 1.9s | 96.8s | 11 |
| 300s | 29.3s | 92.0s | 15 |
| 330s | 10.0s | 41.7s | 14 |
| 360s | 26.9s | 49.1s | 13 |
| 390s | 13.9s | 38.5s | 8 |
| 420s | 12.1s | 21.8s | 3 |
| 450s | 5.0s | 12.5s | 3 |
| 480s | 4.6s | 8.2s | 2 |
| 510s | 5.7s | 25.2s | 7 |
| 540s | 8.3s | 26.8s | 3 |
| 570s | 8.0s | 32.3s | 15 |
| 600s | 11.4s | 27.2s | 5 |
| 630s | 10.8s | 42.9s | 11 |
| 660s | 14.3s | 37.4s | 3 |
| 690s | 5.4s | 19.1s | 6 |
| 720s | 15.2s | 38.5s | 6 |
| 750s | 7.8s | 26.1s | 7 |
| 780s | 5.7s | 15.2s | 12 |
| 810s | 5.2s | 39.9s | 10 |
| 840s | 11.7s | 60.6s | 13 |
| 870s | 21.9s | 53.9s | 15 |
| 900s | 11.0s | 20.1s | 17 |
| 930s | 22.4s | 48.9s | 17 |
| 960s | 13.6s | 28.4s | 13 |
| 990s | 18.9s | 27.6s | 16 |
| 1020s | 15.4s | 44.2s | 12 |
| 1050s | 23.2s | 27.6s | 1 |
| 1080s | 0.0s | 0.0s | 1 |
| 1110s | 0.0s | 0.0s | 1 |
| 1140s | 0.0s | 0.0s | 1 |
| 1170s | 0.0s | 0.0s | 1 |
First seed in full
Everyone in the run, bucketed. The lines are the two thresholds you are scored on.
| Wait | People | Share |
|---|---|---|
| 0–15s | 67 | 41.1% |
| 15–30s | 8 | 4.9% |
| 30–45s | 4 | 2.5% |
| 45–60s | 9 | 5.5% |
| 60–75s | 14 | 8.6% |
| 75–90s | 9 | 5.5% |
| 90–105s | 10 | 6.1% |
| 105–120s | 8 | 4.9% |
| 120–135s | 1 | 0.6% |
| 135–150s | 1 | 0.6% |
| 150–165s | 2 | 1.2% |
| 180–195s | 2 | 1.2% |
| 195–210s | 3 | 1.8% |
| 210–225s | 3 | 1.8% |
| 225–240s | 3 | 1.8% |
| 240–255s | 10 | 6.1% |
| 255–270s | 3 | 1.8% |
| 270–285s | 5 | 3.1% |
| 300s+ | 1 | 0.6% |
Median and 90th percentile of how long each person waited, by the minute they got in.
| At | p50 | p90 | Waiting |
|---|---|---|---|
| 0s | 1.0s | 1.0s | 0 |
| 30s | 1.4s | 1.6s | 4 |
| 60s | 7.1s | 14.2s | 1 |
| 90s | 4.2s | 6.8s | 0 |
| 120s | 1.0s | 5.1s | 0 |
| 150s | 1.6s | 6.4s | 2 |
| 180s | 9.3s | 14.7s | 4 |
| 210s | 10.7s | 19.5s | 1 |
| 240s | 6.4s | 9.1s | 1 |
| 270s | 4.2s | 12.1s | 4 |
| 300s | 3.2s | 32.0s | 9 |
| 330s | 21.3s | 30.1s | 19 |
| 360s | 56.5s | 74.3s | 12 |
| 390s | 11.0s | 32.7s | 16 |
| 420s | 8.8s | 8.8s | 22 |
| 450s | 104.7s | 115.8s | 10 |
| 480s | 12.2s | 51.4s | 18 |
| 510s | 99.5s | 101.2s | 19 |
| 540s | 62.1s | 69.5s | 15 |
| 570s | 7.7s | 8.6s | 21 |
| 600s | 87.0s | 90.5s | 18 |
| 630s | 0.0s | 0.0s | 24 |
| 660s | 0.0s | 0.0s | 30 |
| 690s | 0.0s | 0.0s | 33 |
| 720s | 0.0s | 0.0s | 35 |
| 750s | 117.0s | 117.0s | 34 |
| 780s | 142.3s | 254.0s | 31 |
| 810s | 265.4s | 367.6s | 22 |
| 840s | 269.5s | 271.8s | 18 |
| 870s | 233.9s | 245.6s | 6 |
First seed in full
Every submission is public. That is the point — the interesting part is how somebody else solved it.
// RSR — Relative System Response, the algorithm Otis patented.//// LOOK decides what order one car visits its stops in. RSR is a different kind of// idea: it decides *which car goes*, by scoring every car against every lit button// and taking the lowest score. The stop ordering underneath is still a LOOK sweep.//// score = time to reach the caller// + penalty for how busy the car already looks// + penalty if another car is already going there the same way// - bonus if the car is already heading that way and the call is ahead of it// - bonus if the car is idle and nearby// - bonus if the car has nothing aboard//// Lower is better. The two penalties are what stop every car chasing the same call// and arriving in a convoy, which is the failure mode of naive nearest-car.//// Nothing tells you how many people are in a lift, or how many are waiting. But a// full car leaves a trace: it answers a call, and the button comes straight back on.// This one watches for that and stops trusting a car for a while afterwards, which// is most of what a load sensor would have bought it.//// The whole assignment is recomputed every few seconds rather than every tick, so a// call can be handed to a better car as conditions change — but not so often that// cars thrash between destinations and never arrive anywhere.const SECONDS_PER_FLOOR = 0.6const SECONDS_PER_STOP = 5const BUSY_PENALTY = 1.8 // per floor already requested from insideconst BUNCHING_PENALTY = 22 // another car is already going there, the same wayconst DIRECTION_BONUS = 9 // already sweeping that way, and the call is aheadconst IDLE_NEARBY_BONUS = 14 // sitting doing nothing within two floorsconst EMPTY_BONUS = 6 // nothing aboard, so it will not fill up en routeconst REOPTIMISE_EVERY = 5 // secondsconst DISTRUST_SECONDS = 12 // how long to doubt a car we think just filled upconst DISTRUST_PENALTY = 40 // heavy, but not a ban — it may still be the only carexport default { setup(building) { this.top = building.floors - 1 this.heading = new Map() this.assigned = new Map() // "floor:direction" -> car id this.served = new Map() // car id -> the call it has just answered this.distrust = new Map() // car id -> time until we stop believing it has room this.lastPass = -Infinity }, // Remember what this car just answered, so the next few ticks can tell us whether // it actually cleared the landing or filled up and left people standing. stop({ elevator, floor, car }, world) { this.served.set(elevator, { floor, direction: car.indicated, at: world.time }) }, tick(world) { const cars = world.elevators.filter((car) => !car.outOfService) if (cars.length === 0) return const byId = new Map(cars.map((car) => [car.id, car])) const live = new Set(world.calls.map((call) => call.floor + ':' + call.direction)) // Did anybody we just served put their button straight back on? If so that car // went away full, and we should stop offering it work for a bit. for (const [carId, stop] of [...this.served]) { if (world.time - stop.at > 2) { this.served.delete(carId) continue } if (stop.direction && live.has(stop.floor + ':' + stop.direction)) { this.distrust.set(carId, world.time + DISTRUST_SECONDS) this.served.delete(carId) } } // Forget calls that have been answered, and cars that have gone out of service. for (const key of [...this.assigned.keys()]) { if (!live.has(key) || !byId.has(this.assigned.get(key))) this.assigned.delete(key) } // A full re-optimisation clears the slate; otherwise only brand new calls get // assigned, and everything already promised to a car stays promised. const reoptimise = world.time - this.lastPass >= REOPTIMISE_EVERY if (reoptimise) { this.lastPass = world.time this.assigned.clear() } for (const call of world.calls) { const key = call.floor + ':' + call.direction if (this.assigned.has(key)) continue let best = null let bestScore = Infinity let fallback = null let fallbackLoad = Infinity for (const car of cars) { const committed = car.pressed.length + this.promised(car.id) if (committed < fallbackLoad) { fallbackLoad = committed fallback = car } // Do not keep loading a car that has already promised a building's worth of // work. With no load reading, this cap is the only brake there is. if (committed >= car.capacity) continue const score = this.score(car, call, cars, world) if (score < bestScore) { bestScore = score best = car } } // Somebody has to go, even if everybody is busy. const chosen = best || fallback if (chosen) this.assigned.set(key, chosen.id) } const work = new Map() for (const car of cars) work.set(car.id, []) for (const call of world.calls) { const carId = this.assigned.get(call.floor + ':' + call.direction) if (carId !== undefined && work.has(carId)) work.get(carId).push(call) } for (const car of cars) this.drive(car, work.get(car.id), cars) }, // How many calls we have already promised this car in the current assignment. promised(carId) { let count = 0 for (const id of this.assigned.values()) if (id === carId) count++ return count }, score(car, call, cars, world) { const heading = this.heading.get(car.id) const floors = this.reach(car.floor, heading, call.floor, call.direction) let score = floors * SECONDS_PER_FLOOR // We watched this one answer a call and the button came straight back on, so we // think it went away full. A heavy penalty rather than a ban: in a quiet // building it may still be the only car that can go, and refusing outright // strands people rather than merely delaying them. if ((this.distrust.get(car.id) || 0) > world.time) { if (car.pressed.length === 0) this.distrust.delete(car.id) else score += DISTRUST_PENALTY } // Every floor already requested from inside is a stop this car has to make // before it gets anywhere else. It is the closest thing to a load reading you // are given, and it is a lower bound rather than a count. score += car.pressed.length * BUSY_PENALTY // Anti-bunching: if somebody else is already going there the same way, a second // car usually buys nothing and costs a lift somewhere else in the building. // // Tempting to fade this out for calls that have been lit a long time, on the // theory that a persistent button means one car cannot cope. Measured: it makes // the morning peak markedly worse, because the cars pile into the lobby and the // rest of the building starves. Left alone deliberately. for (const other of cars) { if (other.id === car.id) continue const goingThere = other.queue.includes(call.floor) if (goingThere && this.heading.get(other.id) === call.direction) { score += BUNCHING_PENALTY break } } // Already sweeping that way with the call ahead of it: nearly free to serve. const ahead = heading === 'up' ? call.floor >= car.floor : heading === 'down' ? call.floor <= car.floor : false if (heading === call.direction && ahead) score -= DIRECTION_BONUS // An idle car two floors away is the best possible answer to a hall call. if (!heading && car.queue.length === 0 && Math.abs(car.floor - call.floor) <= 2) { score -= IDLE_NEARBY_BONUS } // Empty, so it will not fill up before it gets there. if (car.pressed.length === 0) score -= EMPTY_BONUS return score }, // How far this car has to travel, on its current sweep, to answer that call. reach(position, heading, floor, wants) { if (!heading) return Math.abs(position - floor) if (heading === 'up') { if (floor >= position && wants === 'up') return floor - position if (wants === 'down') return this.top - position + (this.top - floor) return this.top - position + this.top + floor } if (floor <= position && wants === 'down') return position - floor if (wants === 'up') return position + floor return position + this.top + (this.top - floor) }, // Everything below is the same LOOK sweep. RSR chooses the car; the car still has // to decide what order to visit things in, and LOOK is the right answer to that. drive(car, calls, cars) { const here = car.atFloor const loading = car.doors !== 'closed' const stops = [] for (const floor of car.pressed) stops.push({ floor, kind: 'car' }) for (const call of calls) stops.push({ floor: call.floor, kind: call.direction }) const pending = stops.filter((s) => !(loading && here !== null && s.floor === here)) if (pending.length === 0) { this.heading.delete(car.id) car.indicate(null) const lobbyCovered = cars.some( (other) => other.id !== car.id && (other.floor < 0.5 || other.queue[0] === 0), ) if (!lobbyCovered && car.floor > 0.5) car.goTo(0) return } let start = this.heading.get(car.id) if (!start) { let nearest = Infinity for (const stop of pending) { const distance = Math.abs(stop.floor - car.floor) if (distance < nearest) { nearest = distance start = stop.floor >= car.floor ? 'up' : 'down' } } } const plan = this.sweep(car.floor, start, pending) this.heading.set(car.id, plan.heading) car.setQueue(plan.queue) car.indicate(this.lantern(here, loading, plan, pending)) }, sweep(position, start, stops) { const left = stops.slice() const order = [] let cursor = position let heading = start let leading = null for (let leg = 0; leg < 4 && left.length > 0; leg++) { const floors = left.map((s) => s.floor) const reach = heading === 'up' ? Math.max(cursor, ...floors) : Math.min(cursor, ...floors) const taken = left.filter((s) => { const wanted = s.kind === 'car' || s.kind === heading const onTheWay = heading === 'up' ? s.floor >= cursor - 0.001 : s.floor <= cursor + 0.001 return wanted && onTheWay }) taken.sort((a, b) => (heading === 'up' ? a.floor - b.floor : b.floor - a.floor)) if (taken.length > 0 && !leading) leading = heading for (const stop of taken) { left.splice(left.indexOf(stop), 1) if (!order.includes(stop.floor)) order.push(stop.floor) } cursor = reach heading = heading === 'up' ? 'down' : 'up' } for (const stop of left) if (!order.includes(stop.floor)) order.push(stop.floor) return { queue: order, heading: leading || start } }, lantern(here, loading, plan, stops) { const anchor = loading && here !== null ? here : plan.queue[0] if (anchor === undefined) return plan.heading if (anchor <= 0) return 'up' if (anchor >= this.top) return 'down' const next = loading && here !== null ? plan.queue[0] : plan.queue[1] const onward = next !== undefined && next !== anchor ? (next > anchor ? 'up' : 'down') : null const wanted = new Set() for (const stop of stops) { if (stop.floor === anchor && stop.kind !== 'car') wanted.add(stop.kind) } if (wanted.size === 1) return [...wanted][0] return onward || plan.heading },}