Compare commits

...

8 Commits

Author SHA1 Message Date
3210503b64 angaben 2026-05-12 15:55:13 +02:00
3dace33759 2024st 2026-05-12 15:54:36 +02:00
05f883fdf1 2024ft 2026-05-12 15:50:32 +02:00
14702200e9 2024ft 2026-05-12 15:26:05 +02:00
ff3b4c2963 angaben 2026-05-12 15:23:57 +02:00
e576b37e16 2025ft 2026-05-12 15:23:08 +02:00
2369958a2e started 2025ft 2026-05-12 14:29:51 +02:00
9ec4ee6c5b added finished tests to Readme 2026-05-12 12:08:23 +02:00
22 changed files with 887 additions and 1 deletions

View File

@@ -1 +1,5 @@
# OOP Klausuren der vergangenen Jahre
# OOP Klausuren der vergangenen Jahre
- 2025 ST
- 2025 FT
- 2024 ST
- 2024 FT

View File

@@ -0,0 +1,241 @@
package j2024.FT;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Stack;
public class AriadneIterator implements Iterator<Room> {
private final Room entry;
private Stack<Room> ariadnesThread;
private Set<Room> visited;
private State currentState;
public AriadneIterator(Room entry) {
this.entry = entry;
this.ariadnesThread = new Stack<>();
this.visited = new HashSet<>();
this.currentState = new Starting();
}
// ---------------------------------------------------------------
// Iterator interface
// ---------------------------------------------------------------
@Override
public boolean hasNext() {
return currentState.hasNext();
}
/**
* Laut Spezifikation: ruft zuerst step() auf,
* gibt dann die aktuelle Position (letzter Eintrag in ariadnesThread) zurück.
*/
@Override
public Room next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
step();
return ariadnesThread.peek();
}
// ---------------------------------------------------------------
// Private Hilfsmethoden (werden von den Zuständen genutzt)
// ---------------------------------------------------------------
/**
* Delegiert step() an den aktuellen Zustand.
*/
private void step() {
currentState.step();
}
/**
* Theseus betritt das Labyrinth am Eingang:
* Eingang auf den Faden legen und als besucht markieren.
* Achtung: currentState wird hier NICHT verändert das obliegt dem Starting-Zustand.
*/
private void init() {
ariadnesThread.push(entry);
visited.add(entry);
}
/**
* Liefert true, wenn Theseus den Raum mit dem Minotaurus erreicht hat.
*/
private boolean foundMinotaur() {
return ariadnesThread.peek().hasMinotaur();
}
/**
* Tiefensuche (depth-first): Betritt den ersten noch nicht besuchten Nachbarn.
* Gibt es keinen solchen Nachbarn (Sackgasse), wird per Backtracking ein Schritt
* zurückgegangen (pop), ohne einen neuen Raum zu betreten.
*
* Hinweis: next() gibt anschließend ariadnesThread.peek() zurück,
* also entweder den neu betretenen Raum oder den Raum nach dem Backtrack.
*/
private void seekingStep() {
Room current = ariadnesThread.peek();
for (Room neighbor : current.getReachable()) {
if (!visited.contains(neighbor)) {
// Unbesuchten Nachbarn betreten
ariadnesThread.push(neighbor);
visited.add(neighbor);
return;
}
}
// Sackgasse → Backtracking: einen Schritt zurück
retreatingStep();
}
/**
* Theseus geht entlang des Ariadnefadens einen Schritt zurück.
*/
private void retreatingStep() {
ariadnesThread.pop();
}
/**
* Liefert den nächsten noch nicht besuchten Nachbarn des aktuellen Raums,
* oder null wenn keiner existiert.
*/
private Room nextForward() {
Room current = ariadnesThread.peek();
for (Room neighbor : current.getReachable()) {
if (!visited.contains(neighbor)) {
return neighbor;
}
}
return null;
}
// ---------------------------------------------------------------
// State Pattern Interface
// ---------------------------------------------------------------
/**
* Das Interface wird von AriadneIterator intern definiert.
* (Laut Aufgabe muss der Code für das Interface nicht hingeschrieben werden
* hier dennoch vollständig angegeben.)
*/
private interface State {
void step();
boolean hasNext();
}
// ---------------------------------------------------------------
// Konkreter Zustand: Starting
// ---------------------------------------------------------------
/**
* Startzustand: Theseus hat das Labyrinth noch nicht betreten.
* Beim ersten step()-Aufruf wird init() aufgerufen und in den
* nächsten Zustand gewechselt.
*/
private class Starting implements State {
@Override
public void step() {
init(); // Eingang betreten, Faden anbinden
if (foundMinotaur()) {
// Minotaurus steht direkt am Eingang
currentState = new MissionAccomplished();
} else {
currentState = new SeekingMinotaur();
}
}
/**
* Die Iteration hat noch nicht begonnen → es gibt noch Elemente.
*/
@Override
public boolean hasNext() {
return true;
}
}
// ---------------------------------------------------------------
// Konkreter Zustand: SeekingMinotaur
// ---------------------------------------------------------------
/**
* Suchzustand: Theseus durchsucht das Labyrinth per Tiefensuche.
* Dieser Zustand muss laut Aufgabe NICHT vollständig realisiert werden,
* wird hier aber der Vollständigkeit halber angegeben.
*/
private class SeekingMinotaur implements State {
@Override
public void step() {
seekingStep();
// Nach dem Schritt prüfen, ob Minotaurus gefunden wurde
if (!ariadnesThread.empty() && foundMinotaur()) {
currentState = new MissionAccomplished();
}
}
/**
* Solange Theseus sucht, gibt es immer noch weitere Elemente
* (das Labyrinth hat garantiert einen Weg zum Minotaurus).
*/
@Override
public boolean hasNext() {
return true;
}
}
// ---------------------------------------------------------------
// Konkreter Zustand: MissionAccomplished
// ---------------------------------------------------------------
/**
* Rückweg-Zustand: Der Minotaurus wurde besiegt; Theseus folgt dem
* Ariadnefaden zurück zum Eingang.
*
* Übergang zu Finished: Sobald step() den Ariadnefaden auf den alleinigen
* Eingangsraum reduziert hat, wechselt der Zustand auf Finished
* next() gibt dann noch den Eingang zurück (peek), danach ist
* hasNext() == false.
*/
private class MissionAccomplished implements State {
@Override
public void step() {
retreatingStep();
// Sind wir wieder am Eingang angekommen?
if (ariadnesThread.size() == 1 && ariadnesThread.peek() == entry) {
currentState = new Finished();
}
}
@Override
public boolean hasNext() {
return true; // Es gibt noch mindestens den Eingang zurückzugeben
}
}
// ---------------------------------------------------------------
// Konkreter Zustand: Finished
// ---------------------------------------------------------------
/**
* Endzustand: Theseus ist wieder am Eingang, die Iteration ist abgeschlossen.
* hasNext() liefert false; step() ist eine No-Op.
*/
private class Finished implements State {
@Override
public void step() {
// Iteration beendet nichts zu tun
}
@Override
public boolean hasNext() {
return false;
}
}
}

View File

@@ -0,0 +1,24 @@
package j2024.FT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import org.junit.Test;
public class AriadneIteratorTest {
@Test
public void testSmallMaze() {
Room entry = SampleMaze.createSmallMaze();
AriadneIterator iter = new AriadneIterator(entry);
assertEquals("a2", iter.next().getName());
assertEquals("b2", iter.next().getName());
assertEquals("b1", iter.next().getName());
assertEquals("b2", iter.next().getName());
assertEquals("a2", iter.next().getName());
assertFalse(iter.hasNext());
}
}

Binary file not shown.

38
src/j2024/FT/Room.java Normal file
View File

@@ -0,0 +1,38 @@
package j2024.FT;
import java.util.HashSet;
import java.util.Set;
public class Room {
private final String name;
private boolean minotaur;
private Set<Room> reachable;
public Room(String name) {
this.name = name;
minotaur = false;
reachable = new HashSet<>();
}
public void setMinotaur(boolean v) {
minotaur = v;
}
public void addReachable(Room room) {
reachable.add(room);
room.getReachable().add(this);
}
public String getName() {
return name;
}
public boolean hasMinotaur() {
return minotaur;
}
public Set<Room> getReachable() {
return reachable;
}
}

View File

@@ -0,0 +1,19 @@
package j2024.FT;
public class SampleMaze {
public static Room createSmallMaze() {
Room a1 = new Room("a1");
Room b1 = new Room("b1");
Room a2 = new Room("a2");
Room b2 = new Room("b2");
b1.setMinotaur(true);
a2.addReachable(b2);
b2.addReachable(b1);
b1.addReachable(a1);
return a2;
}
}

6
src/j2024/FT/State.java Normal file
View File

@@ -0,0 +1,6 @@
package j2024.FT;
public interface State {
public void step();
public boolean hasNext();
}

View File

@@ -0,0 +1,153 @@
package j2024.ST;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Stack;
public class AriadneIterator implements Iterator<Node> {
private final Node entry;
private Stack<Node> ariadnesThread;
private Set<Node> visited;
private State currentState;
public AriadneIterator(Node entry) {
this.entry = entry;
this.ariadnesThread = new Stack<>();
this.visited = new HashSet<>();
this.currentState = new Starting();
}
@Override
public boolean hasNext() {
return currentState.hasNext();
}
@Override
public Node next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
step();
if (ariadnesThread.empty()) {
throw new NoSuchElementException();
}
return ariadnesThread.peek();
}
private void step() {
currentState.step();
}
private void init() {
ariadnesThread.push(entry);
visited.add(entry);
}
private boolean foundMiraculix() {
return ariadnesThread.peek().hasMiraculix();
}
private Node nextForward() {
Node current = ariadnesThread.peek();
for (Node next : current.getReachable()) {
if (!visited.contains(next)) {
return next;
}
}
return null;
}
private void seekingStep() {
Node next = nextForward();
if (next != null) {
ariadnesThread.push(next);
visited.add(next);
} else {
retreatingStep();
}
}
private void retreatingStep() {
ariadnesThread.pop();
}
private interface State {
void step();
boolean hasNext();
}
private class Starting implements State {
@Override
public void step() {
init();
if (foundMiraculix()) {
currentState = new MissionAccomplished();
} else {
currentState = new SeekingMiraculix();
}
}
@Override
public boolean hasNext() {
return true;
}
}
private class SeekingMiraculix implements State {
@Override
public void step() {
seekingStep();
if (!ariadnesThread.empty() && foundMiraculix()) {
currentState = new MissionAccomplished();
}
}
@Override
public boolean hasNext() {
return true;
}
}
private class MissionAccomplished implements State {
@Override
public void step() {
retreatingStep();
if (ariadnesThread.size() == 1 && ariadnesThread.peek() == entry) {
currentState = new Finished();
}
}
@Override
public boolean hasNext() {
return ariadnesThread.size() > 1;
}
}
private class Finished implements State {
@Override
public void step() {
}
@Override
public boolean hasNext() {
return false;
}
}
}

View File

@@ -0,0 +1,24 @@
package j2024.ST;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import org.junit.Test;
public class AriadneIteratorTest {
@Test
public void testSmallVillage() {
Node entry = SampleVillage.createSmallVillage();
AriadneIterator iter = new AriadneIterator(entry);
assertEquals(1, iter.next().getNumber());
assertEquals(2, iter.next().getNumber());
assertEquals(3, iter.next().getNumber());
assertEquals(2, iter.next().getNumber());
assertEquals(1, iter.next().getNumber());
assertFalse(iter.hasNext());
}
}

Binary file not shown.

38
src/j2024/ST/Node.java Normal file
View File

@@ -0,0 +1,38 @@
package j2024.ST;
import java.util.HashSet;
import java.util.Set;
public class Node {
private final int number;
private boolean miraculix;
private Set<Node> reachable;
public Node(int number) {
this.number = number;
this.miraculix = false;
this.reachable = new HashSet<>();
}
public int getNumber() {
return number;
}
public boolean hasMiraculix() {
return miraculix;
}
public void setMiraculix(boolean miraculix) {
this.miraculix = miraculix;
}
public Set<Node> getReachable() {
return reachable;
}
public void addReachable(Node node) {
reachable.add(node);
node.reachable.add(this);
}
}

View File

@@ -0,0 +1,19 @@
package j2024.ST;
public class SampleVillage {
public static Node createSmallVillage() {
Node n1 = new Node(1);
Node n2 = new Node(2);
Node n3 = new Node(3);
Node n4 = new Node(4);
n3.setMiraculix(true);
n1.addReachable(n2);
n2.addReachable(n3);
n3.addReachable(n4);
return n1;
}
}

9
src/j2025/FT/Die.java Normal file
View File

@@ -0,0 +1,9 @@
package j2025.FT;
public enum Die {
ONE, TWO, THREE, FOUR, FIVE, SIX;
public static Die random() {
return values()[(int) (Math.random() * 6)];
}
}

141
src/j2025/FT/Game.java Normal file
View File

@@ -0,0 +1,141 @@
package j2025.FT;
public class Game {
private final PlayerHandler playerHandler;
private PairOfDice dice;
private PairOfDice announcement;
public Game(PlayerHandler playerHandler) {
this.playerHandler = playerHandler;
}
public void checkValue() {
state.checkValue();
}
public void rollTheDice() {
state.rollTheDice();
}
public void announce(PairOfDice pairOfDice) {
state.announce(pairOfDice);
}
public void callLiar() {
state.callLiar();
}
public PairOfDice getDice() {
return dice;
}
public PairOfDice getAnnouncement() {
return announcement;
}
public PlayerHandler getPlayerHandler() {
return playerHandler;
}
private State startRound = new State() {
@Override
void checkValue() {
System.out.println("Aktueller Spieler sieht:" + getDice().toString());
}
@Override
void rollTheDice() {
dice = PairOfDice.random();
state = initialValue;
}
};
private State initialValue = new State() {
@Override
void checkValue() {
System.out.println("Aktueller Spieler sieht:" + getDice().toString());
}
@Override
void announce(PairOfDice value) {
announcement = value;
playerHandler.nextTurn();
state = choice;
}
};
private State choice = new State() {
@Override
void announce(PairOfDice value) {
if (value.compareTo(getAnnouncement()) > 0) {
announcement = value;
playerHandler.nextTurn();
}
}
@Override
void checkValue() {
System.out.println("Aktueller Spieler sieht:" + getDice().toString());
state = increaseValue;
}
@Override
void callLiar() {
if (getAnnouncement().compareTo(dice) > 0) {
playerHandler.currentPlayerWinsRound();
} else {
playerHandler.previusPlayerWinsRound();
}
if (playerHandler.remaining() > 1) {
state = startRound;
} else {
state = gameOver;
}
}
};
private State increaseValue = new State() {
@Override
void checkValue() {
System.out.println("Aktueller Spieler sieht:" + getDice().toString());
}
@Override
void announce(PairOfDice value) {
if (value.compareTo(announcement) > 0) {
announcement = value;
playerHandler.nextTurn();
state = choice;
}
}
};
private State gameOver = new State() {
// Konstruktor von anonymen Klassen
{
entry();
}
private void entry() {
System.out.println("Spiel vorbei!");
System.out.println("Spieler " + playerHandler.getCurrentPlayer().getName() + " gewinnt!");
for (Player player : playerHandler.getRanking()) {
System.out.println("Platz " + (playerHandler.getRanking().indexOf(player) + 2) + ": " + player.getName());
}
}
};
private State state = startRound;
}

View File

@@ -0,0 +1,7 @@
package j2025.FT;
public class InvalidAction extends RuntimeException{
public InvalidAction(String message) {
super(message);
}
}

Binary file not shown.

View File

@@ -0,0 +1,40 @@
package j2025.FT;
public class PairOfDice implements Comparable<PairOfDice> {
private final Die die1;
private final Die die2;
public PairOfDice(Die die1, Die die2) {
this.die1 = die1;
this.die2 = die2;
}
@Override
public int compareTo(PairOfDice other) {
return Integer.compare(this.getValue(), other.getValue());
}
private int getValue() {
int high = Math.max(die1.ordinal(), die2.ordinal());
int low = Math.min(die1.ordinal(), die2.ordinal());
// Mäxchen (2,1) → höchster Wert
if (high == 2 && low == 1) return Integer.MAX_VALUE;
// Pasch → 100 + Augenzahl (11..66 → 101..106)
if (high == low) return 100 + high;
// Normale Würfe → zweistellige Zahl (z.B. 6,5 → 65)
return high * 10 + low;
}
public static PairOfDice random() {
return new PairOfDice(Die.random(), Die.random());
}
@Override
public String toString() {
return die1.ordinal() + 1 + " " + (die2.ordinal() + 1);
}
}

View File

@@ -0,0 +1,27 @@
package j2025.FT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class PairOfDiceTest {
private final PairOfDice p33 = new PairOfDice(Die.THREE, Die.THREE);
private final PairOfDice p41 = new PairOfDice(Die.FOUR, Die.ONE);
@Test
public void testCompareTo() {
// p33 ist Pasch → Wert 102, p41 → Wert 41: p33 > p41
assertTrue(p33.compareTo(p41) > 0);
assertTrue(p41.compareTo(p33) < 0);
assertEquals(0, p33.compareTo(p33));
assertEquals(0, p41.compareTo(p41));
}
@Test
public void testToString() {
assertEquals("3 3", p33.toString());
assertEquals("4 1", p41.toString());
}
}

24
src/j2025/FT/Player.java Normal file
View File

@@ -0,0 +1,24 @@
package j2025.FT;
public class Player {
private final String name;
private int points = 10;
public Player(String name, int points) {
this.name = name;
this.points = points;
}
public int getPoints() {
return points;
}
public String getName() {
return name;
}
public void losesPoint() {
points--;
}
}

View File

@@ -0,0 +1,55 @@
package j2025.FT;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class PlayerHandler {
private List<Player> players = new LinkedList<>();
private List<Player> ranking = new ArrayList<>();
public void addPlayer(Player player) {
players.add(player);
}
public Player getCurrentPlayer() {
return players.getFirst();
}
public void nextTurn() {
players.add(players.removeFirst());
}
public int remaining() {
return players.size();
}
public void currentPlayerWinsRound() {
Player lastPlayer = players.getLast();
lastPlayer.losesPoint();
removePlayer(lastPlayer);
}
public void previusPlayerWinsRound() {
Player currentPlayer = getCurrentPlayer();
Player lastPlayer = players.getLast();
currentPlayer.losesPoint();
removePlayer(currentPlayer);
while (players.getFirst() != lastPlayer) {
nextTurn();
}
}
private void removePlayer(Player player) {
if (player.getPoints() == 0) {
ranking.add(player);
players.remove(player);
}
}
public List<Player> getRanking() {
return ranking;
}
}

17
src/j2025/FT/State.java Normal file
View File

@@ -0,0 +1,17 @@
package j2025.FT;
abstract class State {
void checkValue() {}
void rollTheDice() {
throw new InvalidAction("You must not roll the dice in state " + this);
}
void announce(PairOfDice value) {
throw new InvalidAction("You must not announce a value in state " + this);
}
void callLiar() {
throw new InvalidAction("You must not call liar in state " + this);
}
}

Binary file not shown.