Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Jaymz

Frischling

  • »Jaymz« ist der Autor dieses Themas

Beiträge: 16

Wohnort: Steiermark / Österreich

  • Private Nachricht senden

1

11.09.2010, 00:49

[gelöst] Schiffe versenken (mit zufälliger Schiffsposition)

Hallo erstmal,

ich bin zwar schon ein paar Tage hier im Forum angemeldet, aber ich hab mich bisher aufs lesen beschränkt. :)

Ich versuche mich nun schon seid ein paar Wochen an C++, und bin nun an einem Punkt angekommen wo ich nicht mehr alles verstehe.

Genauer gesagt versuche ich gerade mir die Klassen ein bischen näher zu bringen ^^

Daher dachte ich es ist eine gute Idee mal ein Schiffe versenken in der Konsole zu programmieren. Leider läuft das nicht ganz so wie ich das gerne hätte :D

Mein Problem besteht darin das ich Versuche mit rand() meine "Schiffe" zufällig zu setzen. Soweit funktioniert das ganze ja, aber die Anzahl der Schiffe ist leider nicht immer die selbe. Manchmal sinds 3, manchmal 4.

um mal zu zeigen wo ich den Fehler vermute hab ich mal ein Stück Code:

Meine Klasse (die sicher nicht perfekt ist aber wie gesagt ich übe noch^^):

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class drawField
{
    private:
    char field_[5][10];
    char control_field_[5][10];


    public:
    drawField();
    ~drawField();

    void showField();
    void setShips();
    void setField(int row, int column);
};


und die dazugehörige definition/deklaration? ich verwechsel das immer noch :huh:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void drawField::setShips()
{

int row = 0;
int column = 0;
for(int ships = 0; ships < 5; ships++)
{
    // Zufallszahl erstellen
    srand(time(NULL));
    row = rand() % 5 + 1;
    column = rand() % 10 + 1;

    //Überprüfung ob die das Feld bereits belegt ist
    if (control_field_[row][column] == 'X')
    {
        --ships;
    }
    else
    {
        control_field_[row][column] = 'X';
    }
};


ich hätte gerne 5 schiffe die jeweils ein feld in einem char array [5][10] belegen aber irgendwie werden es immer weniger als 5.

könnte mir da jemand einen tip geben wie man das macht? ich würde nur darum bitte wenn es lösungen gibt (muss natürlich kein code sein, eine erklärung wäre mir sogar lieber, da ich ja was lernen will) diese möglichst "anfängerfreundlich" zu präsentieren. :D

danke schonmal

Jaymz

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Jaymz« (11.09.2010, 01:26)


BurningWave

Alter Hase

Beiträge: 1 106

Wohnort: Filderstadt/Konstanz

Beruf: Student

  • Private Nachricht senden

2

11.09.2010, 00:58

Also mir fallen 2 Sachen auf:
1. srand(time(NULL)); ruft man eigentlich nur ein einziges Mal auf, das reiht, um den Zufallszahlengenerator zu initialisieren. Am besten du setzt den Aufruf in eine Init()-Funktion, falls du sowas hast, sonst eben an den Anfang von main().
2. In deinem Code haben sich 2 weitere sehr widerliche Fehler versteckt: Du schreibst an 2 Stellen über das Ende deines Arrays heraus, wenn rand() den entsprechenden Wert liefert. Überdenke noch mal, wie Arrays inidziert sind. Wenn du nicht auf die Lösung kommst, dann sag ich dir auch woran es genau liegt ;)

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

3

11.09.2010, 01:02

Ich hab's nur eben überflogen, allerdings sehe ich soweit, dass deine set Methode eventuell besser ausgelegt wäre, wenn es nicht 5 mal statisch durchlaufen wird, sondern stattdessen solange, bis 5 Schiffe gesetzt sind (Stichwort while Schleife).
Also würde ich unsigned int ships = 0; anlegen und die while Schleife solange durchlaufen lassen, bis diese Variable 5, oder je nachdem welche Anzahl du möchtest, erreicht hat.
Innerhalb der Schleife kannst du dann kontrollieren, ob das Feld bereits besetzt ist und es noch einmal probieren.
Beispiel:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
unsigned int ships = 0;
while (ships < 5) {
    // Zufallszahl erstellen
    srand(time(NULL));
    row = rand() % 5 + 1;
    column = rand() % 10 + 1;

    //Überprüfung ob die das Feld bereits belegt ist
    if (control_field_[row][column] != 'X')
    {
        control_field_[row][column] = 'X';

        ++ships;
    }
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

Jaymz

Frischling

  • »Jaymz« ist der Autor dieses Themas

Beiträge: 16

Wohnort: Steiermark / Österreich

  • Private Nachricht senden

4

11.09.2010, 01:17

danke schonmal für die schnelle hilfe.

Zitat

1. srand(time(NULL)); ruft man eigentlich nur ein einziges Mal auf, das reiht, um den Zufallszahlengenerator zu initialisieren. Am besten du setzt den Aufruf in eine Init()-Funktion, falls du sowas hast, sonst eben an den Anfang von main().
danke schonmal für diese info. ich hab den befehl schnell "zusammengegoogled" und dabei wohl was wichtiges übersehen. :D

Zitat

2. In deinem Code haben sich 2 weitere sehr widerliche Fehler versteckt: Du schreibst an 2 Stellen über das Ende deines Arrays heraus, wenn rand() den entsprechenden Wert liefert. Überdenke noch mal, wie Arrays inidziert sind. Wenn du nicht auf die Lösung kommst, dann sag ich dir auch woran es genau liegt ;)
hmm. ich vermute mal das ich bei der rand() nummer bis 5/10 geh aber der compiler mit 0 beginnt. also ist das array bei 4/9 zu ende? wäre das einzige was mir im mom einfallen würde. :)

Zitat

Ich hab's nur eben überflogen, allerdings sehe ich soweit, dass deine set Methode eventuell besser ausgelegt wäre, wenn es nicht 5 mal statisch durchlaufen wird, sondern stattdessen solange, bis 5 Schiffe gesetzt sind (Stichwort while Schleife).
Also würde ich unsigned int ships = 0; anlegen und die while Schleife solange durchlaufen lassen, bis diese Variable 5, oder je nachdem welche Anzahl du möchtest, erreicht hat.
Innerhalb der Schleife kannst du dann kontrollieren, ob das Feld bereits besetzt ist und es noch einmal probieren.
ups. ich dachte wenn ich in der for schleife mittels ships-- zähl hab ich auch den effekt das die schleife solange läuft bis alle felder belegt sind. aber ich werds mal anpassen.


danke nochmals

Jaymz

BurningWave

Alter Hase

Beiträge: 1 106

Wohnort: Filderstadt/Konstanz

Beruf: Student

  • Private Nachricht senden

5

11.09.2010, 01:22

Ja, die Zufallszahlen gehen von 0 bis %X und das Erste Element eines Arrays hat immer den Index 0.
Also lass einfach + 1 weg:
row = rand() % 5;
column = rand() % 10;

Das mit der Schleife kannst du so lassen, es solte funktionieren, die Schiffe wurden nur nicht alle angezeigt, weil manche außerhalb der Arraygrenzen lagen.

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

6

11.09.2010, 01:28



Das mit der Schleife kannst du so lassen, es solte funktionieren, die Schiffe wurden nur nicht alle angezeigt, weil manche außerhalb der Arraygrenzen lagen.

Sicher funktioniert es so, aber weswegen die Mühe (ja ich finde eine for Schleife "aufwändiger") wenn eine while Schleife dafür einfach besser geeignet ist.
Das mit den Array Grenzen ist mir erst auf den zweiten Blick aufgefallen, ich sollte ins Bett gehen ^^
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

Jaymz

Frischling

  • »Jaymz« ist der Autor dieses Themas

Beiträge: 16

Wohnort: Steiermark / Österreich

  • Private Nachricht senden

7

11.09.2010, 01:30

dankeschön.

ich bin schon seid stunden auf fehlersuche aber auf die idee das ich das + weglass bin ich nicht gekommen. :lol:

irgendwie hab ich das mit dem von 0 anfangen nicht wirklich drinn.(ich weis es zwar aber es ist nicht immer abrufbar).

danke auf jeden fall. und ich bin mir sicher das ich euch noch mit weiteren fragen nerfen werde.

also bis dann :D

edit: @architekt: mir gefällt deine lösung auch besser als meine, und ich werde sie auch so umsetzen. ich finde auch das es sich einfach "besser" liest. ist bei meinem kleinen programm sicher nicht wichtig aber wenn ich mal weitweitweit nach vorne denk (2D Grafik Spiel), sicher nützlich. :D

NachoMan

Community-Fossil

Beiträge: 3 885

Wohnort: Berlin

Beruf: (Nachhilfe)Lehrer (Mathematik, C++, Java, C#)

  • Private Nachricht senden

8

11.09.2010, 12:24

willkommen im forum.
das mit dem null anfangen hat man spätestens nach drei solcher fehler drin ;)

als ich gelesen hab, dass du die schiffe zufällig positionieren willst hab ich schon nen schreck bekommen. aber zum glück haben deine schiffe keine länge^^
kleiner tipp für die namensgebung: deine klasse heißt drawField, sie zeichnet das feld aber nicht nur(klingt eher nach einer methode und das würde mich verwirren). Field wäre vielleicht passender.
übrigens bekommst du ne endlosschleife wenn alle felder besetzt sind ;) denk dran wenn sich dein programm mal aufhängen sollte^^
"Der erste Trunk aus dem Becher der Erkenntnis macht einem zum Atheist, doch auf dem Grund des Bechers wartet Gott." - Werner Heisenberg
Biete Privatunterricht in Berlin und Online.
Kommt jemand mit Nach oMan?

9

11.09.2010, 12:55

Hey noch ne Kleinigkeit, das de es vom Anfang an korrekt machst:
(Man sieht in deinem Code jetzt nicht ob du using namespace std; nutzt, d.h. folgendes)

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Field
{
//    private: brauchst du nicht, ist bei class so gegeben ;) sonst nimm struct
    char m_field[5][10];
    char m_control_field[5][10];

public:
    Field();
    // ~drawField(); brauchst du den wirklich?

public:
    void draw() const; // nja bist in class Field und const, da du beim zeichnen wohl nix änderst ;)
    void setRandomShips(); // wer soll sonst wissen was da für schiffe gesetzt werden ;)
    void setField(const unsigned int, const unsigned int); // const da du nix dran ändern wirst/sollst, unsigned weil dein array von 0 bis ... geht ;)
};

void Field::setRandomShips()
{
    unsigned int row(0);
    unsigned int column(0);

    for (unsigned int ships(0); ships < 5;)
    {
         row = std::rand() % 5;
         column = std::rand() % 10;

        // Überprüfung ob die das Feld bereits belegt ist
        if (m_control_field[row][column] != 'X')
        {
            m_control_field[row][column] = 'X';
            ++ships;
        }
    }
}

Warum for? Naja ships brauchst du nur in den Scope und im Prinzip willst du ja auch einfach hochzählen. Is aber nicht zwingend ;)
Ich geh mal davon aus das du <stdlib.h> genommen hast. Heißt aber <cstdlib> solange du bei C++ bist und da befinden sich alle Funktionen aus dem Header im Namensraum std ;)
Devil Entertainment :: Your education is our inspiration
Der Spieleprogrammierer :: Community Magazin
Merlin - A Legend awakes :: You are a dedicated C++ (DirectX) programmer and you have ability to work in a team? Contact us!
Siedler II.5 RttR :: The old settlers-style is comming back!

Also known as (D)Evil

Jaymz

Frischling

  • »Jaymz« ist der Autor dieses Themas

Beiträge: 16

Wohnort: Steiermark / Österreich

  • Private Nachricht senden

10

11.09.2010, 14:46

@NachoMan: hast recht. ich bin mir nicht mehr sicher wie ich auf den namen gekommen bin aber er ist auf alle fälle etwas irreführend. (hab das ganze zig mal neu angefangen weil ich immer irgendwie nen fehler oder ein problem gehabt hab, wo ich nicht weiter gekommen bin. da kanns sein das ich das vorher mal mit ner funktion gemacht hab und daher der name noch übrig ist :rolleyes: )

und längere schiffe hab ich eigentlich schon geplant aber ich wills zuerst mal grob funktionsfähig machen und dann erweitern. mal schauen wie erfolgreich diese erweiterungen dann sind :D

@Deviloper: das mit private: und dem destruktor hab ich so aus einem buch übernommen in dem geraten wurde das man diese sachen trotzdem schreiben soll, ums übersichtlicher zu machen. seid dem mach ich das immer auch wenn ichs nicht brauch.da ich noch zu wenig praxiserfahrung hab, kann ich noch nicht sagen wieviel sinn das macht. :D

const bei draw() verstehe ich, aber bei setField() tue ich mir da schon schwerer. ich will da ja immer einen anderen wert übergeben den der spieler eingibt. da kann ich dann kein const machen oder? ?( werds auf alle fälle mal ausprobieren.

das mit der for schleife ist schon behoben. ich nehm eigentlich fast immer for schleifen. aber ich werd versuchen in zukunft genauer zu überdenken welche schleife sinn macht. :)

meine includes sind folgende: (die sind in einer header datei die dann wiederum in den cpp dateien eingebunden wird)

C-/C++-Quelltext

1
2
3
4
5
6
7
#include <iostream>
#include <windows.h>
#include <time.h>

using std::cout;
using std::cin;
using std::endl;

mit <cstdlib> hab ich bisher kaum gearbeitet. bisher bin ich fast immer mit <iostream> ausgekommen. <windows.h> nehm ich nur wegen system("cls") und <time.h> wegen srand().
also dann

vielen Dank nochmal.

Jaymz

Werbeanzeige