{
"llm" : {
"feedback" : "# Exercise: fractal\n\n### Correctness\n- Boxes: Für `n == 0` wird aktuell gar nichts gezeichnet, weil deine Rekursion nur in den Fall `n != 0` geht und keinen Basisfall hat, der tatsächlich ein Quadrat zeichnet.\n- Boxes: Du zeichnest bei jedem Rekursionsschritt immer auch das aktuelle (große) Quadrat (`g.drawRect(...)`), nicht nur in der Abbruchbedingung. Dadurch entsteht ein anderes Bild als gefordert (die Rekursion sollte die Struktur über die Teilquadrate erzeugen, nicht jedes Level zusätzlich den Rahmen malen).\n- Boxes: Es fehlt der vierte Teil (unten links). Du rufst nur drei rekursive Teilquadrate auf (oben links, oben rechts, unten rechts).\n- Triangles: Für `n == 0` wird bei dir nichts gezeichnet, weil du nur bei `n > 0` zeichnest. Der Basisfall sollte das kleinste Dreieck zeichnen.\n- Triangles: Du zeichnest die Dreieckslinien in jedem Rekursionsschritt, nicht nur im Basisfall. Das führt zu vielen zusätzlichen Linien und nicht zur gewünschten rekursiven Dreiecks-Aufteilung.\n\n### Suggestion\n- Boxes: Überlege dir einen klaren Basisfall: Bei welchem `n` soll wirklich ein Quadrat gezeichnet werden? Und was soll passieren, wenn `n` größer ist?\n- Boxes: Prüfe, ob du das Quadrat wirklich auf jedem Level zeichnen willst, oder ob das Zeichnen eher in den Abbruchfall gehört.\n- Boxes: Wenn du ein Quadrat in vier gleich große Quadranten teilst: Welche `(x,y)`-Offsets ergeben sich für alle vier Positionen (inkl. unten links)?\n- Triangles: Setze einen Basisfall, in dem du genau ein Dreieck (3 Linien) zeichnest, und rufe sonst nur rekursiv die kleineren Dreiecke auf.\n- Triangles: Berechne zuerst sauber die drei Mittelpunkte der Dreiecksseiten (jeweils Mittelwert der Endpunkte) und verwende diese Punkte dann als Ecken der 3 Kind-Dreiecke.\n\n### Code Style\n- Triangles: Der Kommentar „use g.drawLine...“ steht unten, obwohl du schon Linien zeichnest; entweder entfernen oder passend platzieren, damit klar ist, was noch zu tun ist.\n- Triangles: Die Punktberechnungen in den rekursiven Aufrufen sind schwer lesbar; eigene Variablen für Mittelpunkte (z.B. `mx01`, `my01` etc.) würden die Verständlichkeit deutlich verbessern.\n\n\n# Exercise: knapsack\n\n### Correctness\n- Deine `KnapsackN.main()`-Methode hat nicht die erwartete Signatur (in Java wird für den Programmeinstieg normalerweise `main(String[] args)` verwendet); so werden die mitgelieferten Tests/Runner die Methode ggf. nicht aufrufen.\n- In `pack(...)` lädst du ein Item bis zu `maxN`-mal in einer `while`-Schleife, aber danach entlädst du es immer genau `maxN`-mal (per `for (j=1..maxN)`), unabhängig davon, wie oft du es in diesem Pfad tatsächlich geladen hast. Das kann dazu führen, dass `currWeight`/`currValue` zu weit zurückgesetzt werden (bis hin zu negativen Werten) und verfälschte Maxima entstehen.\n- Durch das bedingungslose Entladen nach der Schleife wird auch der Fall “Item 0-mal genommen” nicht sauber behandelt, weil du trotzdem entlädst, obwohl du in diesem Zweig evtl. gar nichts geladen hast.\n\n### Suggestion\n- Schau dir an, wie viele `loadItem(i)`-Aufrufe in einem konkreten Durchlauf tatsächlich passieren, und stelle sicher, dass exakt dieselbe Anzahl an passenden `unloadItem(i)`-Aufrufen erfolgt (gleiche “Push/Pop”-Balance pro Rekursionszweig).\n- Statt “am Ende pauschal `maxN`-mal entladen” könntest du das Entladen an den tatsächlichen Zustand koppeln (z.B. nur so oft entladen, wie du in diesem Zweig geladen hast).\n- Prüfe die Signatur deiner `main`-Methode gegen das, was Java-Runner und Unit-Tests typischerweise erwarten.\n\n### Code Style\n- Die Parameter `n` und `maxN` in `pack(int i, int n, int maxN)` sind redundant/irreführend: `maxN` existiert bereits als Feld, und `n` wird nur lokal als Zähler benutzt. Das macht die Rekursion schwerer lesbar.\n- In `pack(...)` ist `while (n <= maxN)` weniger klar als eine einfache Zählschleife (lesbarer, weil Start/Ende auf einen Blick ersichtlich).\n- `unloadItem(i)` in einer separaten `for`-Schleife am Ende ist fehleranfällig, weil die “Aufräumlogik” nicht direkt neben der Stelle sitzt, wo geladen wird.\n\n\n# Exercise: queens\n\n### Correctness\n- `Queens.main()` hat nicht die erwartete Java-Signatur als Einstiegspunkt (`static void main(String[] args)`); so startet das Programm typischerweise nicht.\n- Deine `solve()`-Methode hat kein korrektes Backtracking über die Zeilen: Bei der Rekursion startest du jedes Mal wieder mit `i = 0, j = 0` und suchst erneut von oben links, statt die nächste Zeile (oder einen klaren Fortschrittsparameter) zu lösen. Dadurch findest du i.d.R. keine gültige vollständige Belegung bzw. kannst in fehlerhafte/unnötige Rekursion laufen.\n- In `solve()` ist die Abbruchbedingung `if (i == board.length) return true;` logisch falsch für “gelöst”: `i` wird bei dir nicht als “aktuelle Zeile, die gerade erfolgreich platziert wurde” geführt, sondern nur als Laufvariable beim Suchen; außerdem kann `i` bei dir auch steigen, weil irgendwo schon eine Dame steht, nicht weil alle Zeilen korrekt gesetzt wurden.\n- `count()` zählt die Lösungen nicht korrekt: Auch hier fehlt ein sauberer Backtracking-Ansatz pro Zeile. Zusätzlich brichst du über `solved = true` ab, sobald `j >= board.length` in *diesem* Frame erreicht ist, wodurch du nicht alle Suchzweige vollständig durchläufst.\n- Sowohl `solve()` als auch `count()` verwenden den aktuellen Zustand des Boards (mit evtl. bereits gesetzten Damen) als Grundlage, aber ohne eine definierte Regel „genau eine Dame pro Zeile“ beim Aufbau sicherzustellen. Das kollidiert mit der Aufgabenidee und auch mit dem `BoardChecker.isValid`-Konzept (eine Dame pro Zeile).\n\n### Suggestion\n- Gib der Rekursion einen klaren Fortschrittsparameter mit (z.B. „welche Zeile wird als nächstes befüllt?“). Dann platzierst du in dieser Zeile testweise in jeder Spalte eine Dame und gehst rekursiv zur nächsten Zeile weiter.\n- Formuliere eindeutige Basisfälle: „alle Zeilen bearbeitet“ bedeutet gelöst (bei `solve`) bzw. zählt als 1 Lösung (bei `count`); „keine Spalte in dieser Zeile funktioniert“ bedeutet zurück (Backtracking).\n- Achte darauf, dass du nach einem erfolglosen rekursiven Versuch die gesetzte Dame wieder entfernst (Undo-Schritt), bevor du die nächste Spalte probierst.\n- In `count()`: Vermeide ein “solved”-Flag zum Abbrechen; stattdessen solltest du wirklich alle Spalten-Optionen der aktuellen Zeile durchprobieren und die Rückgabewerte aufsummieren.\n- Für den Programmeinstieg: Passe die `main`-Methode an die übliche Java-Signatur an, damit du dein Ergebnis überhaupt zuverlässig testen kannst.\n\n### Code Style\n- `solve()` und `count()` enthalten sehr ähnliche Such-/Schleifenlogik (viel Duplikation). Wenn du beim Backtracking-Ansatz bleibst, lässt sich die Struktur oft klarer und kürzer ausdrücken (und ggf. in Hilfsmethoden auslagern).\n- Variablennamen wie `i` und `j` sind in Backtracking-Lösungen schnell verwirrend, wenn sie nicht eindeutig “row/col” bedeuten und auch so verwendet werden. Klarere Namen reduzieren Logikfehler.\n- `getBoard()` gibt `this.board` zurück; das `this.` ist hier unnötig (kein Fehler, nur Stil).\n\n\n# Exercise: sudoku\n\n### Correctness\n- In `Sudoku.java` ist `main()` nicht als `public static void main(String[] args)` deklariert; so startet das Programm (und damit die GUI) typischerweise nicht über den Java-Entry-Point.\n- `solved(SudokuModel model)` erfüllt die Spezifikation „false: no solution found, model was reset to its initial state“ nicht: Bei `false` bleiben ggf. während des Suchlaufs veränderte Felder im Model stehen (vordefinierte bleiben, aber gesetzte/geratene Werte in anderen Feldern können übrig bleiben).\n- `nofSolutions(SudokuModel model)` erfüllt die Spezifikation „The model was reset to its initial state.“ nicht: Nach dem Zählen können gesetzte Werte im Model verbleiben (insbesondere bei früher Rückgabe, aber auch wenn ein rekursiver Pfad abbricht, ohne die auf tieferen Ebenen gemachten Sets wieder zu löschen).\n\n### Suggestion\n- Vergleiche die Signatur deiner `main`-Methode mit dem, was die JVM als Einstiegspunkt erwartet (Stichworte: `public`, `static`, Parameter-Array).\n- Überlege dir beim Backtracking: Welche Felder hast du selbst gesetzt, und wann musst du sie wieder löschen, damit beim „Scheitern“ wirklich der Ausgangszustand wiederhergestellt ist? Das gilt nicht nur für das aktuell betrachtete Feld, sondern auch für Felder, die in tieferen Rekursionsstufen gesetzt wurden.\n- Für `nofSolutions`: Du brauchst eine Strategie, damit nach jedem rekursiven Versuch (egal ob er Lösungen findet, `max` erreicht, oder 0 liefert) alle von diesem Versuch gesetzten Felder wieder rückgängig gemacht werden, bevor du den nächsten Guess probierst oder zurückkehrst. Achte besonders auf Codepfade mit `return` innerhalb der Schleife (früher Abbruch).\n\n### Code Style\n- Du verwendest in `solved`/`nofSolutions` mehrfach „magische Zahlen“ (`80`, `9`). Nutze stattdessen `model.size()` (und daraus `size*size - 1` bzw. `size*size`) – das macht den Code korrekt für andere Sudoku-Größen und besser lesbar.\n- Die Abbruchbedingung `fieldNr > 80` ist weniger klar als eine Formulierung über `size*size`; das verbessert auch die Verständlichkeit der Rekursionsanker.\n- In `nofSolutions` gibt es mehrere `return`-Stellen mitten im Suchprozess; das macht es schwer sicherzustellen, dass Cleanup (clears) immer passiert. Struktur (z.B. Rückgabe erst nach dem Aufräumen) hilft, solche Fehler zu vermeiden.\n",
"status" : "SUCCESS"
},
"unitTest" : {
"tests" : [ {
"name" : "allZero()",
"status" : "PASSED",
"message" : null
}, {
"name" : "uselessStuff()",
"status" : "PASSED",
"message" : null
}, {
"name" : "random1()",
"status" : "PASSED",
"message" : null
}, {
"name" : "random2()",
"status" : "PASSED",
"message" : null
}, {
"name" : "random3()",
"status" : "PASSED",
"message" : null
}, {
"name" : "boardIsValidAfterSolveN4()",
"status" : "PASSED",
"message" : null
}, {
"name" : "boardIsValidAfterSolveN5()",
"status" : "PASSED",
"message" : null
}, {
"name" : "boardIsValidAfterSolveN8()",
"status" : "PASSED",
"message" : null
}, {
"name" : "boardSizeIsCorrectForN4()",
"status" : "PASSED",
"message" : null
}, {
"name" : "boardSizeIsCorrectForN8()",
"status" : "PASSED",
"message" : null
}, {
"name" : "countN1()",
"status" : "PASSED",
"message" : null
}, {
"name" : "countN2()",
"status" : "PASSED",
"message" : null
}, {
"name" : "countN3()",
"status" : "PASSED",
"message" : null
}, {
"name" : "countN4()",
"status" : "FAILED",
"message" : "N=4 has exactly 2 solutions ==> expected: <2> but was: <0>"
}, {
"name" : "countN5()",
"status" : "FAILED",
"message" : "N=5 has exactly 10 solutions ==> expected: <10> but was: <0>"
}, {
"name" : "countN6()",
"status" : "FAILED",
"message" : "N=6 has exactly 4 solutions ==> expected: <4> but was: <0>"
}, {
"name" : "countN8()",
"status" : "FAILED",
"message" : "N=8 has exactly 92 solutions ==> expected: <92> but was: <0>"
}, {
"name" : "solveReturnsTrueForN1()",
"status" : "PASSED",
"message" : null
}, {
"name" : "solveReturnsTrueForN4()",
"status" : "PASSED",
"message" : null
}, {
"name" : "solveReturnsTrueForN8()",
"status" : "PASSED",
"message" : null
}, {
"name" : "solveReturnsFalseForN2()",
"status" : "PASSED",
"message" : null
}, {
"name" : "solveReturnsFalseForN3()",
"status" : "PASSED",
"message" : null
} ]
}
}