Achtzehn Wege über das Bull, und der Test sah nur den letzten Pfeil
Eine Regel stand im Code, ausformuliert und begründet. Sie galt für den Schlusspfeil und nicht für die beiden davor. Kein Test bemerkte es, weil er dieselbe Stelle ansah wie die Regel.
- Tests
- Eigenschaftstests
- TypeScript
- Dart
Eine Zähl-App für Dart behält niemand wegen der Addition. Die behält man wegen der Zahlen daneben: Average, Doppelquote, und der Vorschlag, wie sich der Rest ausmachen lässt. Genau dieser Vorschlag ist die empfindlichste Stelle des Produkts, denn er wird an der Scheibe gegen etwas gehalten, das die meisten Spieler im Kopf haben.
Rechnerisch gibt es auf 40 Punkte über achtzig Wege. Gespielt wird genau einer. Eine Suche, die den ersten Treffer zurückgibt, gibt Unsinn zurück: S1 S1 D19 ergibt genauso 40 wie D20. Also bekommt jeder Weg Kosten, und die Kosten bilden ab, was am Board zählt.
Die Regel stand längst da
Eine davon betrifft das Bull. Der Doppelring ist ein langes, schmales Band, das Bullseye ein winziger Kreis. Deshalb steht in jeder gedruckten Tafel auf 95 die D19 und nicht T15 BULL. Aufs Bull geht man, wenn es sein muss, nicht wenn es rechnerisch passt.
Genau so stand es im Code, in der Funktion für den Schlusspfeil, mit Begründung im Kommentar darüber. Das Bullseye kostet dort 50, das teuerste Doppel 18.
Für die beiden Pfeile davor galt sie nicht
Die Kosten der Vorbereitungspfeile kannten diese Aussage nicht. Dort kostete ein Bull neun, ungefähr so viel wie eine T13, und war damit der billigste Weg zu einem Rest von 40. Das Ergebnis sieht man erst, wenn man es sich ansieht:
| Rest | Vorgeschlagen | Gleich lang, ohne Bull |
|---|---|---|
| 141 | T17 BULL D20 | T20 T19 D12 |
| 90 | BULL D20 | T18 D18 |
| 33 | 25 D4 | S17 D8 |
Keiner dieser Wege steht in einer gedruckten Tafel. Wer BULL D20 auf 90 liest, glaubt der App die nächste Zahl auch nicht mehr.
Warum kein Test anschlug
Es gab einen Test für die Sache, und er war kein schlechter. Er lief über alle Reste von 2 bis 170, holte alle Wege, und prüfte: Endet der Vorschlag auf dem Bull, obwohl ein gleich langer Weg auf einem Doppel endet? Das ist eine Eigenschaft und kein Einzelfall, und trotzdem lief er durch.
Aufgefallen ist es beim Durchsehen der Tafel, von Hand, Rest für Rest. Das ist die unangenehme Antwort: Gefunden hat es kein Werkzeug, sondern der Blick auf das Erzeugnis.
Der Fix ist keine größere Strafe
Der naheliegende Weg wäre, dem Bull im Aufbau höhere Kosten zu geben. Er ist falsch. Der Wert müsste größer sein als der größte Abstand zweier Doppel-Ränge, und er müsste bei jeder Änderung an den Gewichten mitwachsen. Eine Zahl, die von drei anderen Zahlen abhängt, ist keine Regel, sondern eine Kalibrierung, die beim nächsten Mal wieder kippt.
Die Aussage gehört deshalb in die Sortierung, vor die Kosten: Gibt es einen gleich langen Weg ohne Bull im Aufbau, kommt er zuerst. Gibt es keinen, bleibt der Weg über das Bull stehen, denn ein Vorschlag ist besser als keiner. Kein Gewicht kann diese Aussage mehr überstimmen.
bewertet.sort((a, b) => {
if (a.weg.length !== b.weg.length)
return a.weg.length - b.weg.length;
// Vor den Kosten, damit kein Gewicht diese Aussage überstimmen kann.
if (a.bull !== b.bull) return a.bull - b.bull;
if (a.kosten !== b.kosten) return a.kosten - b.kosten;
return a.text.localeCompare(b.text);
});Was ich daraus mitnehme
Prüft eine Eigenschaft eine Folge von Schritten, dann prüft man sie an jedem Schritt und nicht am letzten. Der neue Test tut genau das: Er sieht auf wuerfe.slice(0, -1), läuft über alle drei Aus-Modi und meldet jeden Rest, bei dem ein Weg ohne Bull dieselbe Länge hätte. Gegengeprobt mit dem alten Stand meldet er die achtzehn.
Und die zweite Lehre ist die ältere: Ein grüner Testlauf ist kein Beleg dafür, dass das Ergebnis stimmt. Er ist ein Beleg dafür, dass die Annahmen im Test zu den Annahmen im Code passen.
Die Tafel liegt inzwischen als eigenes Paket öffentlich, mit beiden Eigenschaftstests: darts-checkout, TypeScript, ohne Abhängigkeiten.
Belege
- Dartile-Repo, packages/kern/src/checkout.ts: bullImAufbau und die Sortierung in checkoutWege
- Test: packages/kern/src/checkout.test.ts, „nimmt das Bull nicht als Vorbereitungspfeil“
- Gezählt über alle Reste von 2 bis 170 in den Modi doppel, master und straight
- Die Tafel liegt öffentlich als darts-checkout