245 lines
8.4 KiB
Java
245 lines
8.4 KiB
Java
/**
|
|
* 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<Point> 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<Point> 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});
|
|
}
|
|
}*/
|
|
} |