diff --git a/src/AiPlayer.java b/src/AiPlayer.java index f3ed2aa..0333225 100644 --- a/src/AiPlayer.java +++ b/src/AiPlayer.java @@ -1,3 +1,8 @@ +/** + * Die Klasse AiPlayer ist die Basis für alle Ki Spieler und jede Spezifische Ki erweitert diese Klasse. + * @author Florian und Florian + * */ + import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -6,19 +11,34 @@ import java.util.Random; public abstract class AiPlayer extends LocalPlayer implements Runnable { + /** + * Liste aller erstellten ShootThreads + */ List shootThreads; + /** + * Konstruktor + */ public AiPlayer() { this.setName("AI Player"); this.shootThreads = new ArrayList<>(); } + + /** + * Gibt einen zufälligen Punkt im Spielfeld zurück. + * @return Ein zufälliger Punkt + */ public Point RandomPoint() { Random random = new Random(); // Pseudo Random für zufallszahlen int posx = random.nextInt(super.board.getSize()); // Generiert 0 - 13 int posy = random.nextInt(super.board.getSize()); // return new Point(posx,posy); } - + + /** + * Initialisiert das Board. + * @param size größe des Boards + */ @Override public void createBoard(int size) { super.createBoard(size); @@ -26,31 +46,39 @@ public abstract class AiPlayer extends LocalPlayer implements Runnable { this.ready(); } + /** + * Ki Methode zum zufälligen Setzten der Schiffe + */ public void aiSetShips() { for(int i = 0; i < super.board.getShips().size(); i++) { // Interiert durch alle Shiffe //TODO: set horizontal while(!super.board.getShips().get(i).setPosition(RandomPoint(), true, super.board.getShips(), super.board.getSize())) {} - } // Versucht das Aktuelle Shiff zu setzten und wiederholt solange bis es funktioniert + } // Versucht das aktuelle Schiff zu setzten und wiederholt solange bis es funktioniert return; } - + /** + * Ki Methode zum zufälligen Schießen auf das gegnerische Board. + */ public void aiShoot() { if (!this.myTurn) return; this.enemy.receiveShoot(RandomPoint()); return; } + @Override public synchronized void receiveHit(HitResponse hitResponse) { + // Eltern-Klasse LocalPlayer updatet myTurn super.receiveHit(hitResponse); if (this.myTurn) { + // Neuer Schuss wird erstellt und gestartet. Thread t = new Thread(this); t.start(); this.shootThreads.add(t); } } - + @Override public synchronized void receiveShoot(Point point) { super.receiveShoot(point); @@ -60,8 +88,11 @@ public abstract class AiPlayer extends LocalPlayer implements Runnable { this.shootThreads.add(t); } } - - + + /** + * Wird aufgerufen, wenn in determineCoinToss festgestellt wurde das die Ki anfängt. + * Erster Schuss wird gestartet. + */ @Override public void beginTurn() { Thread t = new Thread(this); @@ -90,7 +121,7 @@ public abstract class AiPlayer extends LocalPlayer implements Runnable { } try { - Thread.sleep(300); + Thread.sleep(250); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); @@ -118,4 +149,4 @@ public abstract class AiPlayer extends LocalPlayer implements Runnable { } } } -} \ No newline at end of file +} diff --git a/src/SpecificAiPlayerEasy.java b/src/SpecificAiPlayerEasy.java index 4825ec5..a499e81 100644 --- a/src/SpecificAiPlayerEasy.java +++ b/src/SpecificAiPlayerEasy.java @@ -1,6 +1,13 @@ - +/** + * Diese Klasse implementiert den Einfachsten Ki Spieler. + * @author Florian und Florian + * */ public class SpecificAiPlayerEasy extends AiPlayer{ + /** + * Bein einfachen Ki Spieler wird nur der AiPlayer initialisiert und der Name gesetzt, + * da in der Eltern-Klasse AiPlayer eine default implementierung für alle Methoden existieren. + */ public SpecificAiPlayerEasy() { super(); this.setName("AI Player Easy"); diff --git a/src/SpecificAiPlayerHard.java b/src/SpecificAiPlayerHard.java index 079279f..067af48 100644 --- a/src/SpecificAiPlayerHard.java +++ b/src/SpecificAiPlayerHard.java @@ -1,3 +1,7 @@ +/** + * Diese Klasse implementiert den Harten Ki Spieler. + * @author Florian und Florian + * */ import java.util.ArrayList; import java.util.Random; @@ -10,6 +14,17 @@ public class SpecificAiPlayerHard extends AiPlayer{ private int nextChessRow; private int nextChessCol; + // Enum für die Orientierung + private enum Orientierung { + UNBEKANNT, + HORIZONTAL, + VERTIKAL + } + private Orientierung orientierung; + private Point firstHit; // Speichert den ersten Treffer zur Bestimmung der Orientierung + + + public SpecificAiPlayerHard() { super(); this.setName("AI Player Hard"); @@ -20,6 +35,8 @@ public class SpecificAiPlayerHard extends AiPlayer{ this.random = new Random(); this.nextChessRow = 0; this.nextChessCol = 0; + this.orientierung = Orientierung.UNBEKANNT; + this.firstHit = null; } // Checks if a position has already been shot at @@ -33,16 +50,16 @@ public class SpecificAiPlayerHard extends AiPlayer{ this.gridSize = super.board.getSize(); this.shotsFired = new boolean[gridSize][gridSize]; } - // If there are hits to process, prioritize those + // Wenn wir noch Treffer in der Queue haben, diese priorisieren while (!hitQueue.isEmpty()) { - Point target = hitQueue.remove(0); - + Point target = hitQueue.remove(0); if (!alreadyShot(target)) { + shotsFired[target.getX()][target.getY()] = true; return target; } } - // Otherwise, use chess pattern targeting + // Ansonsten weiterhin "Schachbrettmuster" int row = nextChessRow; int col = nextChessCol; while (alreadyShot(new Point(row, col))) { @@ -55,16 +72,113 @@ public class SpecificAiPlayerHard extends AiPlayer{ advanceChessPattern(); return new Point(row, col); } - + @Override public synchronized void receiveHit(HitResponse hitResponse) { super.receiveHit(hitResponse); - // If it's a hit or sunk, add adjacent cells to the hitsQueue + + // Wenn es ein Treffer ist, Adjacent-Punkte hinzufügen if (hitResponse.getHitResponse() == HitResponseType.HIT) { - addAdjacentPoints(hitResponse.getPoint()); + Point hitPoint = hitResponse.getPoint(); + + // Wenn wir noch keinen ersten Treffer haben, speicher ihn + if (firstHit == null) { + firstHit = hitPoint; + // Orientierung noch unbekannt: alle möglichen Richtungen hinzufügen + addAdjacentPoints(hitPoint); + } else { + // Wenn Orientierung noch nicht bestimmt, jetzt prüfen + if (this.orientierung == Orientierung.UNBEKANNT) { + // Prüfen, ob der zweite Treffer horizontal oder vertikal liegt + if (firstHit.getY() == hitPoint.getY()) { + this.orientierung = Orientierung.VERTIKAL; + } else if (firstHit.getX() == hitPoint.getX()) { + this.orientierung = Orientierung.HORIZONTAL; + } + + // Sobald die Orientierung erkannt wurde, entferne alle „unpassenden“ Punkte + if (this.orientierung != Orientierung.UNBEKANNT) { + cleanUpHitQueue(); + } + } + // Für den aktuellen Treffer nur in passender Orientierung erweitern + addPointsByOrientation(hitPoint); + } + } else if (hitResponse.getHitResponse() == HitResponseType.SUNK) { + this.orientierung = Orientierung.UNBEKANNT; + firstHit = null; + hitQueue.clear(); } } - + + /** + * Entfernt aus der hitQueue alle Punkte, die nicht der erkannten Orientierung entsprechen. + */ + private void cleanUpHitQueue() { + if (firstHit == null || this.orientierung == Orientierung.UNBEKANNT) { + return; + } + ArrayList toRemove = new ArrayList<>(); + for (Point p : hitQueue) { + if (this.orientierung == Orientierung.HORIZONTAL) { + // HORIZONTAL => gleiche Zeile wie firstHit + if (p.getX() != firstHit.getX()) { + toRemove.add(p); + } + } else if (this.orientierung == Orientierung.VERTIKAL) { + // VERTICAL => gleiche Spalte wie firstHit + if (p.getY() != firstHit.getY()) { + toRemove.add(p); + } + } + } + hitQueue.removeAll(toRemove); + } + + /** + * Fügt benachbarte Felder in der **erkannten Orientierung** hinzu. + * - Ist die Orientierung HORIZONTAL, so werden nur links/rechts hinzugefügt. + * - Ist sie VERTICAL, so werden nur oben/unten hinzugefügt. + */ + private void addPointsByOrientation(Point point) { + if (this.orientierung == Orientierung.UNBEKANNT) { + // Fallback: füge alle benachbarten Punkte hinzu + addAdjacentPoints(point); + return; + } + + int x = point.getX(); + int y = point.getY(); + if (this.orientierung == Orientierung.HORIZONTAL) { + // Gleiche Zeile => links und rechts vom Point + Point left = new Point(x, y - 1); + Point right = new Point(x, y + 1); + + if (isValidPoint(left) && !alreadyShot(left) && !hitQueue.contains(left)) { + hitQueue.add(left); + } + if (isValidPoint(right) && !alreadyShot(right) && !hitQueue.contains(right)) { + hitQueue.add(right); + } + } else if (this.orientierung == Orientierung.VERTIKAL) { + // Gleiche Spalte => oben und unten + Point up = new Point(x - 1, y); + Point down = new Point(x + 1, y); + + if (isValidPoint(up) && !alreadyShot(up) && !hitQueue.contains(up)) { + hitQueue.add(up); + } + if (isValidPoint(down) && !alreadyShot(down) && !hitQueue.contains(down)) { + hitQueue.add(down); + } + } + } + + + /** + * Diese Methode erweitert die hitsQueue um die umliegenden Punkte die Schiffe seien könnten. + * @param point + */ private void addAdjacentPoints(Point point) { int x = point.getX(); int y = point.getY(); @@ -83,12 +197,18 @@ public class SpecificAiPlayerHard extends AiPlayer{ } } } - + /** + * Die Methode gibt zurück, ob eine Position auf dem Board ist. (Boolean) + * @param point Punkt der geprüft werden soll + * @return Ist auf dem Board oder nicht. + */ private boolean isValidPoint(Point point) { return point.getX() >= 0 && point.getX() < gridSize && point.getY() >= 0 && point.getY() < gridSize; } - + /** + * Ki Methode um zu schießen. + */ @Override public void aiShoot() { this.enemy.receiveShoot(getNextMove()); diff --git a/src/SpecificAiPlayerMedium.java b/src/SpecificAiPlayerMedium.java index 0562b3a..a27390a 100644 --- a/src/SpecificAiPlayerMedium.java +++ b/src/SpecificAiPlayerMedium.java @@ -1,8 +1,14 @@ +/** + * Diese Klasse implementiert den Medium Ki Spieler. + * @author Florian und Florian + * */ import java.util.ArrayList; import java.util.List; public class SpecificAiPlayerMedium extends AiPlayer{ - + /** + * Liste an Punkten die beschossen werden sollen. (Mögliche weitere Segmente vom schiff) + */ private List hitsQueue = new ArrayList<>(); public SpecificAiPlayerMedium() { @@ -10,6 +16,9 @@ public class SpecificAiPlayerMedium extends AiPlayer{ this.setName("AI Player Medium"); } + /** + * Ki Methode um zu schießen. + */ @Override public void aiShoot() { Point nextShot = ComputeNextShot(); @@ -26,6 +35,10 @@ public class SpecificAiPlayerMedium extends AiPlayer{ } } + /** + * Die Methode bestimmt welche Position als nächstes beschossen werden soll. + * @return + */ public Point ComputeNextShot() { Point nextShot; @@ -45,6 +58,10 @@ public class SpecificAiPlayerMedium extends AiPlayer{ return nextShot; } + /** + * Diese Methode erweitert die hitsQueue um die umliegenden Punkte die Schiffe seien könnten. + * @param point + */ private void addAdjacentPoints(Point point) { int x = point.getX(); int y = point.getY(); @@ -64,12 +81,22 @@ public class SpecificAiPlayerMedium extends AiPlayer{ } } + /** + * Diese Methode gibt zurück, ob eine Position schon beschossen wurde. (Boolean) + * @param p Punkt der geprüft werden soll + * @return wurde schon beschossen oder nicht. + */ private boolean alreadyShot(Point p) { return this.enemy.board.getHitResponseOnPoint(p) != null; } + /** + * Die Methode gibt zurück, ob eine Position auf dem Board ist. (Boolean) + * @param point Punkt der geprüft werden soll + * @return Ist auf dem Board oder nicht. + */ private boolean isValidPoint(Point point) { return point.getX() >= 0 && point.getX() < board.getSize() && point.getY() >= 0 && point.getY() < board.getSize();