Schwierigkeiten mit Umsetzung - 2Dim Arrays

Shizmo

Aktives Mitglied
Hallo, ich habe eine Idee, weiß aber nicht genau wie ich das umsetzen kann, vielleicht hat von euch einer einen Tipp.

Also es geht um Schiffe versenken, ich habe ein boolean 2Dim Array namens "ships", Beispiel-Code:
Java:
ships[0][0] = true;
ships[1][0] = true;
ships[2][0] = true;
ships[3][0] = true;

D.h. auf einem 10x10 Feld, sind auf diesen Feldern Schiffe.
Ich habe auch ein boolean 2Dim Array für die Treffer, damit ich auch Punkte vergeben kann.
Es funktioniert auch soweit, wenn ich auf das Feld ships[1][0] klicke, wird ein Treffer gezählt usw.
Ich will aber auch markieren, wenn ein Schiff gesunken ist, also zB. der obere Beispiel-Code ist ein Schiff (zB ein Kreuzer). D.h. ich brauche irgendwas, dass den
Java:
ships[0][0] = true;
ships[1][0] = true;
ships[2][0] = true;
ships[3][0] = true;
übergeordnet ist, also wenn alle 4 dieser Array-Felder mit einem Treffer markiert sind, dass sie als gesunken markiert werden.

Ich hoffe das ist einigermaßen verständlich :D
Freue mich auf jeden Tipp!
 

Thallius

Top Contributor
Du erstellst eine Klasse Ship. In dieser befindet sich das Array. Dann kannst du einfach bei einem Treffer den EIntrag im Array auf false seitzen und in dem Moment wo alle Einträge in dem Array false sind ist das Dingen weg.

Gruß

Claus
 
K

kneitzel

Gast
Du kannst auch einfach hingehen und den Typ des Arrays verändern. Statt nur true/false schreibst du 0 oder die id des Schiffes. Dann kannst Du entweder Schiffe verwalten (Dann musst Du die bei einem Treffer einfach entsprechend verändern) oder Du schaust, ob Du auf der Karte noch Nicht Beschossene Felder hast, auf der das Schiff noch steht.

Also per for Schleife(n) über alle Felder gehen und prüfen auf ships[zeile][spalte] == id && !shooted[zeile][spalte] oder so ...

Wobei Du das generell Objektorientierter machen könntest. Also Du hast ein Spielfeld, welches aus einzelnen Feldern besteht. Ein Feld hat einen marker, ob das Feld schon beschossen wurde und ggf. eine Referenz auf ein Schiff. Ein Schiff hat eine Größe und Referenzen auf Felder, auf denen es ist.
Dann ist es einfach zu prüfen, ob ein Schiff versenkt ist:
Versenkt ist es, wenn die Größe == Anzahl Treffer ist.
Anzahl Treffer ermittelst Du einfach, indem Du alle Felder des Schiffes zählst, die beschossen wurden.
==> So kannst Du viele kleine Funktionen machen, die jede für sich relativ einfach und leicht zu verstehen ist (Und die sic ggf. gegenseitig nutzen).
 

Shizmo

Aktives Mitglied
Hmmm, vielen Dank für eure Antworten, aber ich finde momentan immer noch keine Lösung.
Ich habe eine Methode, die verschiedene Schifftypen erstellt zB shipbuilder(5); erstellt eine Schiff mit 5 Feldern, etc. Zwischen den Schiffen ist immer eine Lücke.
Ich habe auch ein 2Dim Array "ships" wo alle Schiffe markiert sind und ein 2Dim Array "hits" wo alle Treffer markiert sind. Dadurch dass mein shipbuilder aber verschiedene Typen erstellen kann, weiß ich nicht wie ich das mit dem sinken lösen kann, da ich die Schiffe nicht wirklich auseinander halten kann.
Es würde funktionieren, wenn ich zB destroyerBuilder() und submarineBuilder(), etc. hätte, also für jedes Schiff eine eigene Methode, dann könnte ich für jedes Schiff ein extra 2DimArray anlegen, über das ich dann eine for-Schleife los lass und wenn alle false sind, dann sinkt dieses Schiff.
Aber 5 extra Methoden anstatt 1ner will ich nicht, ist mir zu ineffizient.
Freue mich auf weitere Tipps :)
 

JStein52

Top Contributor
Aber 5 extra Methoden anstatt 1ner
Wieso denn 5 verschiedene ? Die Schiffe unterscheiden sich doch nur in der Grösse oder ? Falls ja machst du wie oben schon mal gesagt eine Klasse Ship der du im Konstruktor die Grösse mitgibst (und die Lage auf dem Spielfeld). Die Ships merkst du dir in einer ArrayList<Ship> und bei jedem Schuss gehst du diese Liste durch und guckst ob und welches Ship getroffen wurde. Wenn in einem Shipp alle Felder getroffen ( = false) sind ist das Dingens weg wie @Thallius so schön gesagt hat.
 

JStein52

Top Contributor
Ja, schon klar. Aber die Ship's unterscheiden sich nur in der Zahl der Felder ?! Dann merkst du dir für jede Instanz von Ship die Koordinaten der Felder (wie du das am geschicktesten machst kann ich dir gerade nicht sagen, aber einfach und brutal wäre es dir für jedes Ship einfach ein komplettes Spielfeld als 2-Dim-Array anzulegen in dem anfangs alle Felder des Ship's auf true sind). Und bei jedem Schuss schaust du dir jedes Ship an und kontrollierst auf Treffer und auf versenkt.

Edit: dann hast du eine einzige Klasse Ship mit (u.a.) den Methoden

public boolean istTreffer(x,y) und
public boolean istVersenkt()
 

Shizmo

Aktives Mitglied
Ja sie unterscheiden sich nur in der Zahl der Felder, aber mein shipBuilder baut Schiffe anhand einer Schleife, also ca. so
Java:
for(int i = 0; i < size; i++){
   ships[x][y+i] = true;
   aShip(size, x, y+i);
}

Also deine Idee wäre jetzt das aShip() dem übergib ich die Größe und die Position, ich kann aber immer nur eine Position mitgeben, da es ja eine Schleife ist.

Die Methode kann ja dann auch immer nur ein Array erstellen, da ich den Namen ja nicht variabel machen kann. Hmmm...
 

JStein52

Top Contributor
for(int i = 0; i < size; i++){
ships[x][y+i] = true;
aShip(size, x, y+i);
}
Na ja, deinen ShipBuilder müsstest du natürlich ein bisschen anpassen. Er würde Instanzen von Ship erzeugen und ihnen die Grösse (ist wohl dein size) übergeben und dann meinetwegen per Setter-Methode die einzelnen Felder setzen. Und jedes Ship wird in eine ArrayList eingefügt. (und jedes Ship hat dann meinetwegen intern ein Spielfeld auf dem seine eigenen Positionen eingetragen sind.
 

JStein52

Top Contributor
Nur mal so als Anstoss:
Eine Klasse Ship

Code:
public class Ship {
   
    boolean[][] board = null;
    boolean     status;
    int         size;
   
    public Ship(int size) {
        board = new boolean[10][10];
        this.size = size;
        this.status = true;   // Schiff nicht versenkt.
    }

    public void setPosition(int x, int y) {
        // TODO: feld auf true setzen, evtl. Plausipruefungen
    }
   
    public boolean istTreffer(int x, int y) {
        // TODO: board durchfieseln ob das ein Treffer ist, falls ja Feld auf false
        //       und true zurückliefern
        return true;
    }
    public boolean istVersenkt() {
        // TODO: board durchfieseln ob alle Felder auf false sind
        return true;
    }
}

So in etwa wie oben gesagt. Alles natürlich rudimentär und müsste ausgebaut werden.

Dann eine Klasse Player von denen du zwei Instanzen erzeugst, spielerA und spielerB.
Jedes Player-Object verwaltet eine Liste von Ships.

Code:
import java.util.ArrayList;
import java.util.List;

/**
*
* @author Juergen
*/
public class Player {
   
    List<Ship> myShips = null;
    public Player() {
        //TODO: ....
        myShips = new ArrayList<Ship>();
        // ....
       
    }
   
    public void neuesSpiel() {
        myShips.clear();
        // TODO:  alle Schiffe neu anlegen
        Ship ship = new Ship(4);
        ship.setPosition(3, 2);
        // usw.
        myShips.add(ship);
       
        // nächstes Ship, usw.
    }

    public boolean testeTreffer(int x, int y) {
        for(Ship ship: myShips) {
            if (ship.istTreffer(x, y)) {
                if (ship.istVersenkt()) {
                    myShips.remove(ship);
                    return true;
                }
            }
            else {
                // nur Treffer, nicht versenkt, irgendwas tun
                return true;
            }
        }
        return false;
    }
}

Und dann machst du dir eine Klasse Game oder Board oder so die die main-Methode enthält, die Player anlegt und das Schiessen managt.
 

Joose

Top Contributor
(und jedes Ship hat dann meinetwegen intern ein Spielfeld auf dem seine eigenen Positionen eingetragen sind.

Es würde reichen die Position von Bug oder Heck zu speichern (mittels x und y Koordinate) und die Ausrichtung des Schiffes zu speichern (rauf, rechts, runter, links). Alles von Punkt ausgehend in die richtige Richtung (mit bestimmter Länge) ist Teil des Schiffes
Oder natürlich die Positionen von Bug UND Heck speichern. -> alles dazwischen ist Teil des Schiffes
 

JStein52

Top Contributor
Es würde reichen die Position von Bug oder Heck zu speichern
Ja ist schon klar. Aber irgendwo muss man auch die Treffer speichern bzw. prüfen. Man kann das natürlich auf einem einzigen Spielfeld machen und immer wieder für jedes Schiff nachrechnen ob es versenkt ist. Aber da das Problem ja begrenzt ist und der Speicherplatz dabei keine Rolle spielt würde ich mir der Einfachheit halber pro Schiff einfach ein eigenes Spielfeld machen. Aber das andere ist natürlich auch kein Problem war ja aber genau die Frage des TE wie er es am geschicktesten macht. Und das wäre halt mein Vorschlag. Ob er das jetzt mit genau diesen Klassen Ship, Player, Game usw. macht ist ja wurscht. man kann ja genausogut eine ArrayList von diesen 2-dim. boolean Arrays anlegen und die bei jedem Schuss überprüfen. Wir reden hier von 6-8 Shiffen pro Spieler. Das gibt weder ein Performance- noch ein Speicherproblem und geht auch noch auf einem Smartphone.
 

Thallius

Top Contributor
Wenn er es partout ohne Klassen machen will, dann sollte er einfach kein bool Array sondern ein int Array nehmen. In dem Feld steht dann ein Wert für die Schiffsnummer. Also Überall wo schiff 1 drin ist steht eine 1 etc. Wird ein Feld getroffen das nicht 0 ist, ist es ein treffer und wird auf 0 gesetzt. Ist kein Feld mit mit dem Inhalt 1 vorhanden ist das Schiff versenkt. Sind alle Felder 0 hat der Spieler verloren...
 

JStein52

Top Contributor
Wird ein Feld getroffen das nicht 0 ist, ist es ein treffer und wird auf 0
Dann geht aber mit den Treffern die Information verloren welches Schiff das mal war. Ich weiss aber nicht ob er das am Ende noch braucht, z.B. um irgendwelche Punkte für den Sieger auszurechnen ?.

Edit: aber man könnte z.B. aus der +1 bei einem Treffer eine -1 machen usw.
 
K

kneitzel

Gast
Also Vorschläge für ein mögliches Objektorientiertes Design gab es schon einige. Und ich habe das Gefühl, dass man sich etwas im Kreis dreht. Den Vorschlag mit einem int Array hatte ich glaube ich auch schon gemacht, wobei ich die "Schiffid" speichern würde. Getroffene Felder sind dann von mir aus negativ. Damit hat man dann ein 2d array mit allen Daten und man braucht keine Klassen (Was ich aber als Übung für schlecht halte. So ein Projekt sollte man auch objektorientiert machen.). Und wenn man nicht ständig durchzählen will, ob ein Schiff versenkt ist, dann kann man das ja auch noch in einem Array Speichern. Also von mir aus ein int[][] schiffdaten mit erster Dimension die Schiffsnummer und in der Zweiten Dimension speichert man SCHIFFSGROESSE = 0, TREFFERZAHL = 1 und falls man noch mehr braucht, dann kommt das halt dazu.

Um wirklich gut helfen zu können bräuchte man mal genaue Details des Standes. Was wurde wie umgesetzt und was genau geht nicht oder wo genau gibt es noch Probleme.
 

Shizmo

Aktives Mitglied
So vielen vielen Dank für eure Antworten und euren super Tipps, habs jetzt endlich hinbekommen, zumindest fast. Eine Kleinigkeit fehlt noch. Ich will Punkte zählen, wenn ein Treffer gelandet wird, dann +10, wenn daneben geschossen wird dann -10 und wenn ein Schiff sinkt, dann nochmal für jedes dieser Felder, wo das Schiff stand, +10.

Okay das funktioniert auch soweit alles perfekt, jedoch will ich nicht unter 0 kommen. Wenn ich es aber mit einer if-Bedingung abfange, funktioniert es komischerweise nicht, es returned dann zwar 0, zählt aber trotzdem ins minus und beim nächsten treffer passiert dann einfach nichts, erst beim übernächsten wieder, usw.

Und ja, ich würde es auch lieber mit einer Instanzvariable lösen, weil da hab ich es schon gehabt, aber ich muss es mit dieser Methode machen.

Ruft die Methode auf:
Java:
int points = countPoints();

Die Methode selber:
Java:
    private int countPoints() {
        int points = 0;

        for(int x = 0; x < 10; x++){
            for(int y = 0; y < 10; y++){
                if(gui.getBattleshipCell(x,y).isShip() && gui.getBattleshipCell(x,y).isHit()){
                    points = points + 10;
                }
                if(gui.getBattleshipCell(x,y).isSunk()){
                    points = points + 10;
                }
                if(gui.getBattleshipCell(x,y).isHit() && !gui.getBattleshipCell(x,y).isShip()){
                    points = points - 10;
                }
            }
        }
        if(points < 0){
            return 0;
        }
        else{
            return points;
        }
Also wie gesagt, ohne den letzten 5 Zeilen funktionierts perfekt, allerdings komm ich dann ins Minus, was ich nicht will. Es lässt sich irgendwie nicht abfangen :(
 

Shizmo

Aktives Mitglied
Sorry für den Doppelpost, aber ich wollt nur sagen, dass ich es hinbekommen habe (mit einer Hilfsinstanzvariable).

Vielen, vielen Dank nochmal für die zahlreichen Tipps.

Bis bald ;)
LG
 

Neue Themen


Oben