/** * Diese Klasse implementiert den Harten Ki Spieler. * @author Florian und Florian * */ import java.util.ArrayList; import java.util.Random; public class SpecificAiPlayerHard extends AiPlayer{ private int gridSize; private boolean[][] shotsFired; private final ArrayList hitQueue; private final Random random; 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"); /*this.gridSize = super.board.getSize(); this.shotsFired = new boolean[gridSize][gridSize];*/ this.gridSize = 0; this.hitQueue = new ArrayList<>(); 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 public boolean alreadyShot(Point p) { return shotsFired[p.getX()][p.getY()]; } // Generates the next move for the AI public Point getNextMove() { if(gridSize == 0) { this.gridSize = super.board.getSize(); this.shotsFired = new boolean[gridSize][gridSize]; } // Wenn wir noch Treffer in der Queue haben, diese priorisieren while (!hitQueue.isEmpty()) { Point target = hitQueue.remove(0); if (!alreadyShot(target)) { shotsFired[target.getX()][target.getY()] = true; return target; } } // Ansonsten weiterhin "Schachbrettmuster" int row = nextChessRow; int col = nextChessCol; while (alreadyShot(new Point(row, col))) { advanceChessPattern(); row = nextChessRow; col = nextChessCol; } shotsFired[row][col] = true; advanceChessPattern(); return new Point(row, col); } @Override public synchronized void receiveHit(HitResponse hitResponse) { super.receiveHit(hitResponse); // Wenn es ein Treffer ist, Adjacent-Punkte hinzufügen if (hitResponse.getHitResponse() == HitResponseType.HIT) { 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(); // Possible adjacent positions (up, down, left, right) Point[] adjacentPoints = { new Point(x, y - 1), new Point(x, y + 1), new Point(x - 1, y), new Point(x + 1, y) }; for (Point p : adjacentPoints) { if (isValidPoint(p) && !alreadyShot(p) && !hitQueue.contains(p)) { hitQueue.add(p); } } } /** * 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()); } // Advances the chess pattern to the next cell private void advanceChessPattern() { nextChessCol += 2; if (nextChessCol >= gridSize) { nextChessRow += 1; nextChessCol = (nextChessRow % 2 == 0) ? 0 : 1; // Alternate starting points for chess pattern } if (nextChessRow >= gridSize) { nextChessRow = 0; nextChessCol = 0; // Reset if pattern wraps around } } // Adds adjacent cells to the hit queue /* public void processHit(int row, int col) { if (row > 0 && !alreadyShot(row - 1, col)) { hitQueue.add(new int[]{row - 1, col}); } if (row < gridSize - 1 && !alreadyShot(row + 1, col)) { hitQueue.add(new int[]{row + 1, col}); } if (col > 0 && !alreadyShot(row, col - 1)) { hitQueue.add(new int[]{row, col - 1}); } if (col < gridSize - 1 && !alreadyShot(row, col + 1)) { hitQueue.add(new int[]{row, col + 1}); } }*/ }