Kollisionen von Fischen in simuliertem Aquarium behandeln

YangLee

Mitglied
Dies ist ein Aquarium, in dem bestimmte Fischarten „schwimmen“ können. Sie bewegen sich horizontal und vertikal (die vertikale Bewegung ist zufällig). Ein Aspekt ist jedoch noch nicht abgedeckt. Nämlich der, wenn zwei Fische kollidieren. Ich möchte diesen Fall in der Methode moveFish oder in einer separaten Methode abdecken, Die Implementierung soll folgendermaßen aussehen, der aktuelle Fisch soll vorübergehend verschwinden solange bis die beiden fische wieder unterschiedliche Positionen haben.

Java:
import java.util.Random;


public class Aquarium {

    public int tankHeight;
    public int tankWidth;
    public Fisch[][] fische;

    public Aquarium(int tankHeight, int tankWidth) {
        this.tankHeight = tankHeight;
        this.tankWidth = tankWidth;
        this.fische = new Fisch[this.tankHeight][this.tankWidth];
    }

    public int getTankHeight() {
        return this.tankHeight;
    }

    public int getTankWidth() {
        return this.tankWidth;
    }

    public void setFisch(Fisch fisch) {
        this.fische[fisch.getSwimHeight()][fisch.getSwimWidth()] = fisch;
    }

    public void printFishTank() {

        System.out.println();

        for (int i = 0; i < this.tankHeight; i++) {
            System.out.print("|");
            for (int j = 0; j < this.tankWidth; j++) {
                if (fische[i][j] != null) {
                    System.out.print(fische[i][j].getAussehen());
                    j += fische[i][j].getAussehen().length() - 1;
                } else {
                    System.out.print(" ");
                }
            }
            System.out.println("|");
        }
        System.out.print("+");
        for (int i = 0; i < this.tankWidth; i++) {
            System.out.print("-");
        }
        System.out.println("+");
        System.out.println();
    }

    public void moveFische() {
        
        randomVertikal();

        Fisch[][] updatedFish = new Fisch[this.tankHeight][this.tankWidth];

        for (int i = 0; i < this.tankHeight; i++) {
            for (int j = 0; j < this.tankWidth; j++) {

                if (this.fische[i][j] != null) {
                    if (this.fische[i][j].getDirection() == true)
                    {
                        if (this.fische[i][j].getSwimWidth() + this.fische[i][j].getAussehen().length() < this.tankWidth)
                        {
                            this.fische[i][j].setSwimWidth(j+1);
                        }
                        else
                        {
                            this.fische[i][j].setDirection(false);
                            this.fische[i][j].turnFish(); // Drehe den Fisch
                        }
                    }
                    else
                    {
                        if (this.fische[i][j].getSwimWidth() - 1 >= 0)
                        {
                            this.fische[i][j].setSwimWidth(j-1);
                        }
                        else
                        {
                            this.fische[i][j].setDirection(true);
                            this.fische[i][j].turnFish(); // Drehe den Fisch
                        }
                    }

                    int newHeight = fische[i][j].getSwimHeight();
                    int newWidth = fische[i][j].getSwimWidth();
                    
                    if (this.fische[newHeight][newWidth] != null) {
                       handleCollision(this.fische[newHeight][newWidth]);
                    }

                    updatedFish[newHeight][newWidth] = fische[i][j];

                }
            }
        }
        fische = updatedFish;

    }
    
    public void handleCollision(Fisch fisch) {
        
    
    }
  
    public void randomVertikal() {
        Random rand = new Random();
        Fisch[][] updatedFish = new Fisch[this.tankHeight][this.tankWidth];

        for (int i = 0; i < this.tankHeight; i++) {
            for (int j = 0; j < this.tankWidth; j++) {
                if (this.fische[i][j] != null) {
                    Fisch currentFish = this.fische[i][j];
                    int newSwimHeight = currentFish.getSwimHeight();

                    if (currentFish instanceof Fisch.Shark && rand.nextInt(4) == 0) {
                        if (newSwimHeight > 0 && newSwimHeight < this.tankHeight - 1) {
                            newSwimHeight += rand.nextBoolean() ? 1 : -1;
                        } else if (newSwimHeight <= 0) {
                            newSwimHeight += rand.nextBoolean() ? 1 : 0;
                        } else if (newSwimHeight >= this.tankHeight - 1) {
                            newSwimHeight += rand.nextBoolean() ? 0 : -1;
                        }
                    }
                    if (currentFish instanceof Fisch.SwordFish && rand.nextInt(5) == 0) {
                        if (newSwimHeight > 0 && newSwimHeight < this.tankHeight - 1) {
                            newSwimHeight += rand.nextBoolean() ? 1 : -1;
                        } else if (newSwimHeight <= 0) {
                            newSwimHeight += rand.nextBoolean() ? 1 : 0;
                        } else if (newSwimHeight >= this.tankHeight - 1) {
                            newSwimHeight += rand.nextBoolean() ? 0 : -1;
                        }
                    }
                    if (currentFish instanceof Fisch.PufferFish && rand.nextInt(10) == 0) {
                        if (newSwimHeight > 0 && newSwimHeight < this.tankHeight - 1) {
                            newSwimHeight += rand.nextBoolean() ? 1 : -1;
                        } else if (newSwimHeight <= 0) {
                            newSwimHeight += rand.nextBoolean() ? 1 : 0;
                        } else if (newSwimHeight >= this.tankHeight - 1) {
                            newSwimHeight += rand.nextBoolean() ? 0 : -1;
                        }
                    }
  
                    currentFish.setSwimHeight(newSwimHeight);
                    

                    int newHeight = currentFish.getSwimHeight();
                    int newWidth = currentFish.getSwimWidth();
                    updatedFish[newHeight][newWidth] = currentFish;
                }
            }
        }
        fische = updatedFish;
    }

    public static void main(String[] args) throws InterruptedException {

        Fisch.SwordFish fish1 = new Fisch.SwordFish(1, 3, false);
        Fisch.NormalFish fish2 = new Fisch.NormalFish(1, 9, false);
        Fisch.PufferFish fish3 = new Fisch.PufferFish(3, 9, false);
        Fisch.Shark fish4 = new Fisch.Shark(8, 9, false);
        Fisch.NormalFish fish5 = new Fisch.NormalFish(5, 9, false);

        Aquarium a = new Aquarium(9, 22);


        a.setFisch(fish1);
        a.setFisch(fish2);
        a.setFisch(fish3);
        a.setFisch(fish4);
        a.setFisch(fish5);

        for (int i = 0; i < 100; i++) {
            a.printFishTank();
            a.moveFische();
            
            Thread.sleep(800);

        }
    }

}

public class Fisch {

    public String aussehen;
    public int swimHeight;
    public int swimWidth;
    public boolean direction;

    public Fisch(int swimHeight, int swimWidth, boolean direction) {
        this.swimHeight = swimHeight;
        this.swimWidth = swimWidth;
        this.direction = direction;
    }

    public String getAussehen() {
        return this.aussehen;
    }

    public int getSwimHeight() {
        return this.swimHeight;
    }

    public int getSwimWidth() {
        return this.swimWidth;
    }

    public boolean getDirection() {
        return this.direction;
    }
    public boolean setDirection(boolean direction) {
        return this.direction = direction;
    }

    public int setSwimHeight(int swimHeight) {
        return this.swimHeight = swimHeight;
    }

    public int setSwimWidth(int swimWidth) {
        return this.swimWidth = swimWidth;
    }


    public void turnFish() {
        String newaussehen = "";

        for (int i = this.aussehen.length(); i >= 0; i--) {
            if (i >= 0 && i < this.aussehen.length()) {
                char currentChar = this.aussehen.charAt(i);

                if (currentChar == '>') {
                    newaussehen += '<';
                } else if (currentChar == '<') {
                    newaussehen += '>';
                } else if (currentChar == ')') {
                    newaussehen += '(';
                } else if (currentChar == '(') {
                    newaussehen += ')';
                    
                } else {
                    newaussehen += currentChar;
                }
            }

        }
        this.aussehen = newaussehen;
    }
    public static class NormalFish extends Fisch {
        public NormalFish(int swimHeight, int swimWidth, boolean direction) {
            super(swimHeight, swimWidth, direction);
            this.aussehen = "<><";
        }
    }

    public static class Shark extends Fisch {
        public Shark(int swimHeight, int swimWidth, boolean direction) {
            super(swimHeight, swimWidth, direction);
            this.aussehen = "<///====><";
        }
    }

    public static class PufferFish extends Fisch {
        public PufferFish(int swimHeight, int swimWidth, boolean direction) {
            super(swimHeight, swimWidth, direction);
            this.aussehen = "<()><";
        }
    }

    public static class SwordFish extends Fisch {
        public SwordFish(int swimHeight, int swimWidth, boolean direction) {
            super(swimHeight, swimWidth, direction);
            this.aussehen = "-<><";
        }
    }
}
 

KonradN

Super-Moderator
Mitarbeiter
Ich bin mir nicht sicher, ob ich Dich richtig verstanden habe, aber das klingt so, dass Du eigentlich keinerlei Kollisionen haben willst. Die Fische bewegen sich und wenn Du an einer Position mehrere Fische hast, dann wird nur einer angezeigt.

Und wie immer, kannst Du Dir überlegen, wie Du sowas abbilden möchtest:
a) Wenn du das 2D Feld behalten willst, dann kannst Du statt Fisch eine List<Fisch> verwenden oder so. Dann kannst Du die Fische normal bewegen. Das kann auch relativ performant laufen, in dem Du das Array nicht immer neu erstellst sondern wenn Du die Fische bewegst, dann entfernst Du diese aus der ursprünglichen Liste und dann hast Du nach dem Move ein 2D Array von Listen, die alle leer sind. Das würde funktionieren, aber das ist alles relativ umständlich....

b) Eine Alternative ist, dass Du den Fischen eine Position verpasst. Dann kannst Du einfach eine List<Fisch> pflegen. Die Fische bewegen sich und das verändert nur die Fische und die Liste kann dann so konstant bleiben. Zur Anzeige schaust du halt, ob es zu einer Position auch (mind.) einen Fisch gibt und stellst diesen dann da.
Wenn das nicht optimal genug ist, könntest Du auch eine Map<Position, Fisch> vorhalten. Jeden Fisch, den du bewegst, müsstest Du dann nur aus der Map nehmen, bewegen und dann wieder hinzu fügen. Das würde bei sehr vielen Fischen / möglichen Positionen die Geschwindigkeit erhöhen.
Eine andere Alternative wäre, die Liste der Fische einfach zu sortieren - dann kannst Du bei der Darstellung entsprechend der Reihenfolge vorgehen.

Generell ist halt die Empfehlen hier, das man diverse Aufgaben sauber trennt (Separation of concerns). Du hast Fische und die bewegen sich. Das bildest Du einfach erst einmal ab. Das Andere ist ja - so ich Dich richtig verstanden habe - ein Problem der Anzeige. Das löst Du dann separat - das hat mit den Fischen und ihren Bewegungen nichts zu tun.
 

YangLee

Mitglied
Ich bin mir nicht sicher, ob ich Dich richtig verstanden habe, aber das klingt so, dass Du eigentlich keinerlei Kollisionen haben willst. Die Fische bewegen sich und wenn Du an einer Position mehrere Fische hast, dann wird nur einer angezeigt.

Und wie immer, kannst Du Dir überlegen, wie Du sowas abbilden möchtest:
a) Wenn du das 2D Feld behalten willst, dann kannst Du statt Fisch eine List<Fisch> verwenden oder so. Dann kannst Du die Fische normal bewegen. Das kann auch relativ performant laufen, in dem Du das Array nicht immer neu erstellst sondern wenn Du die Fische bewegst, dann entfernst Du diese aus der ursprünglichen Liste und dann hast Du nach dem Move ein 2D Array von Listen, die alle leer sind. Das würde funktionieren, aber das ist alles relativ umständlich....

b) Eine Alternative ist, dass Du den Fischen eine Position verpasst. Dann kannst Du einfach eine List<Fisch> pflegen. Die Fische bewegen sich und das verändert nur die Fische und die Liste kann dann so konstant bleiben. Zur Anzeige schaust du halt, ob es zu einer Position auch (mind.) einen Fisch gibt und stellst diesen dann da.
Wenn das nicht optimal genug ist, könntest Du auch eine Map<Position, Fisch> vorhalten. Jeden Fisch, den du bewegst, müsstest Du dann nur aus der Map nehmen, bewegen und dann wieder hinzu fügen. Das würde bei sehr vielen Fischen / möglichen Positionen die Geschwindigkeit erhöhen.
Eine andere Alternative wäre, die Liste der Fische einfach zu sortieren - dann kannst Du bei der Darstellung entsprechend der Reihenfolge vorgehen.

Generell ist halt die Empfehlen hier, das man diverse Aufgaben sauber trennt (Separation of concerns). Du hast Fische und die bewegen sich. Das bildest Du einfach erst einmal ab. Das Andere ist ja - so ich Dich richtig verstanden habe - ein Problem der Anzeige. Das löst Du dann separat - das hat mit den Fischen und ihren Bewegungen nichts zu tun.
Vielen Dank für die ausführliche Antwort. Gäbe es auch eine Möglichkeit das Problem ohne eine List<> zu behandeln, da ich aktuell noch nicht über das Wissen, wie diese funktionieren, verfüge und es gerne mit meinen bereits bekannten Mittlen lösen würde.
 

KonradN

Super-Moderator
Mitarbeiter
Ja klar, du kannst statt einer List auch einfach ein Array nehmen. Wenn Du also weisst, wie viele Fische du hast, dann kannst Du ein entsprechendes Array erzeugen. Oder Du erzeugst einfach ein Array, dass groß genug ist und dann füllst Du dies mit Fischen. Du kannst sowas aber auch dynamisch halten, in dem Du - wenn das Array voll ist und ein weiterer Fisch benötigt wird - ein größeres Array erzeugst und die Elemente kopierst. (Das ist auch unter dem Strich, was java.util.ArrayList macht!)

Wichtig ist halt der Unterschied bei der Datenhaltung. Du hast nicht mehr das Feld von möglichen Feldern sondern du hast nur noch die Fische, die dann halt eine Position haben.
 

White_Fox

Top Contributor
Vielen Dank für die ausführliche Antwort. Gäbe es auch eine Möglichkeit das Problem ohne eine List<> zu behandeln, da ich aktuell noch nicht über das Wissen, wie diese funktionieren, verfüge und es gerne mit meinen bereits bekannten Mittlen lösen würde.
ArrayList funktioniert genauso wie ein Array. Die wichtigsten Zugriffsmethoden sind set() und get(), aber die meisten anderen dürften auch kein Rätsel sein bzw. solltest du schnell herausgefunden haben wie die funktionieren.

Sieht als Anfänger erstmal komplizierter aus, tatsächlich ersparst du dir damit eine ganze Menge Arbeit, da du z.B. nicht auf eine fixe Länge beschränkt bist. Gewöhne dir die Arbeit mit Arrays besser gar nicht erst an. Es genügt wenn du weißt, wie sie funktionieren, wenn das Programmieren Spaß machen soll nimmt man was besseres.

Bedenke nur, daß du in einer ArrayList den Typ spezifizieren mußt, was für Objekte da reinkommen sollen. Z.B.
Java:
var fishList = new ArrayList<Fish>(); // In den <> wird angegeben, welche Objekte die ArrayList aufnehmen und ausgeben soll.

Wenn du es zweidimensional haben willst, bauchst du eine ArrayList, die wiederum lauter ArrayListobjekte enthalten soll. Diese "inneren" Listen enthalten dann die eigentlichen Fish-Objekte:

Java:
//Beachte die Klammersetzung im Code <> als auch im Kommentar ().

var fishTable = new ArrayList<ArrayList<Fish>>(); //Eine ArrayList, die lauter Objekte vom Typ ArrayList<Fish> (also eine ArrayList, die lauter Objekte vom Typ Fish enthält) enthält.

Initialisieren kannst du das dann so:
Java:
int cols = 10, rows = 15;

//Äußere ArrayList deklarieren, die die inneren ArrayListobjekte hält:
var fishTable = new ArrayList<ArrayList<Fish>>(rows);

//Neue ArrayListobjekte erzeugen, die Fishobjekte halten und in die äußere Liste einsetzen:
for(int i = 0; i < rows; i++){
    var fishCol = new ArrayList<Fish>(cols);
    fishTable.set(i, fishCol);
}

//Zugriff auf ein Fishobjekt:
int row = 2, col = 3;

//Haarklein aufgedröselt:
ArrayList<Fish> selectedRow = fishTable.get(row);
Fish selectedFish = selectedRow.get(col);

//Oder, kürzer, deinem Arrayzugriff ähnlicher:
Fish selectedFish = fishTable.get(row).get(col);
 

Ähnliche Java Themen

Neue Themen


Oben