Merge pull request 'AiPlayer Kommentare hinzugefügt' (#21) from FH_Fixes into main

Reviewed-on: #21
Reviewed-by: lgc <main@lugico.de>
This commit is contained in:
flo 2024-12-22 22:36:39 +00:00
commit 29d4118d8f
4 changed files with 209 additions and 20 deletions

View File

@ -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<Thread> 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,30 +46,38 @@ 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() {
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);
@ -59,15 +87,22 @@ 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);
t.start();
this.shootThreads.add(t);
}
/**
* Die Methode wird immer aufgerufen, wenn ein Schuss abgefeuert werden soll.
* Es wird erst ein Schuss abgefeuert, wenn der vorherige schuss abgeschlossen wurde.
*/
public void run() {
Iterator<Thread> i = this.shootThreads.iterator();
while(i.hasNext()) {
@ -85,7 +120,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();

View File

@ -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");

View File

@ -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<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();
@ -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());

View File

@ -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<Point> 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();