Potions

Advanced Potion Making

Wir wollen möglichst günstig einen Zaubertrank (potion) brauen. Die Zutaten (ingredients) befinden sich in einer Kammer (pantry). Jedes mal wenn wir eine Zutat aus der Kammer entnehmen bezahlen wir den Preis dieser Zutat. In einem Zauberbuch finden wir Schritte, um aus gegebenen Zutaten eine neue Zutat zu erzeugen. Diese neue Zutat befindet sich dann nicht mehr in der Kammer sondern auf dem Tisch. Wir können sie zur Anwendung weiterer Schritte verwenden (nun ohne Kosten zu bezahlen) und hieraus wieder neue Zutaten auf dem Tisch erzeugen. Das Ende ist erreicht wenn die Zielzutat (der Zaubertrank) erzeugt wurde. Der Zaubertrank selbst ist übrigens zu Beginn nicht in der Kammer vorhanden.

Beispiel

Zauberer Severus möchte den Zaubertrank Liquid Luck brauen. Hierzu benötigt er einmal Pulver zweimal die Tinktur (Regel 4 im Zauberbuch). Weil die Kammer nur eine Tinktur enthält, stellt er sich aus Thymian und Öl eine zweite Tinktur her (3). Dies wäre sogar günstiger (Kosten 10+2=12) als eine fertige Tinktur (Kosten 25) zu verwenden, aber er besitzt kein zweites Öl um eine zweite Tinktur selbst herzustellen. Da er keinen Lorbeer besitzt, stellt er aus zwei Samen einen Lorbeer her (Schritt 1). Hieraus macht er Pulver (2) und verarbeitet Pulver und Tinkturen zu Liquid Luck (4).

Offensichtlich muss sich unser Verfahren immer entscheiden ob wir eine Zutat X direkt verwendet oder X (aus evtl. günstigeren) Zutaten herstellen. Hierbei muss beachtet werden wieviel von welcher Zutat in der Kammer vorhanden ist. Außerdem kann es mehrere mögliche Schritte geben um die gleiche Zutat herzustellen, und die gleiche Zutat ist evtl. in verschiedenen Schritten verwendbar. Sie können davon ausgehen dass Schritte zyklenfrei sind (Es wird aus A nicht B erzeugt und aus B wieder A).

Programmgerüst

In Potions.java finden Sie Basisklassen für Zutaten (Ingridient) mit Kosten und Namen, sowie für Schritte (Step) mit Eingabe- und Ausgabezutaten.

Studieren Sie die Methode brew() in PotionsTest. Diese erhält ein Rezept (d.h. eine Sequenz von Schritten), stellt mit diesem Rezept einen Trank her, und gibt die entstandenen Kosten zurück. Ist das Rezept nicht durchführbar (weil Zutaten fehlen) wird -1 zurückgegeben. Vollziehen Sie den Code in brew() nach, ändern Sie aber nichts.

Greedy-Verfahren

Implementieren Sie ein Greedy-Verfahren, das ein Rezept (d.h. ein Step[]-Array) zur Erzeugung eines Tranks (goal) generiert.

Backtracking-Verfahren