Zeilen und Spaltenweise einlesen und als Bruch darstellen(Zweidimensionales Array)

Sekiro24

Mitglied
Hallo zusammen,
ich stehe erneut vor einem Problem. Undzwar soll ich ein Spiel Namens Domino erstellen, der die Felder des Arrays mit den gleichen Augenzahlen aneinander legen und als Bruch darstellen soll.
Zuerst wollte ich mal das einlesen thematisieren. Dies soll über eine zusätzliche Funktion Namens "einlesen" passieren.

Das Problem hierbei ist, dass nach 7 Zeilen Iteration, ich immer noch etwas eingeben kann obwohl ja nach 7 Zeilen die Schleife aufhören soll.
Vielleicht kann mir jemand dabei helfen auch die Ausgabe als Bruch vorerst darzustellen, ohne die Sortierung.

C:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

int Steine[7][2];
int i_zeile, i_spalte, k;
    
main(){
    printf("Bitte geben Sie nun 7 Dominosteine ein\n");
    einlesen();
    for(k = 0; k < 7; k++)printf("%d. %d\n", (k+1) ,Steine[k][k]); /*Hier soll die Ausgabe als Bruch dargestellt werden*/
}

einlesen(){
    /*Einlesen der Zahlen, Zeilen- und Spaltenweise*/
    for(i_zeile = 0; i_zeile < 7; i_zeile){
        for(i_spalte = 0; i_spalte < 2; i_spalte){
            scanf("%d", &Steine[i_zeile][i_spalte]);
        }
    }
    
}
 

cwriter

Erfahrenes Mitglied
Das Problem hierbei ist, dass nach 7 Zeilen Iteration, ich immer noch etwas eingeben kann obwohl ja nach 7 Zeilen die Schleife aufhören soll.
Was sollte im letzten Feld eines for-Loops stehen?
for(Start; Condition; Statement). Du hast im Statement eine reine Expression (keine Veränderung des Zustands).


Vielleicht kann mir jemand dabei helfen auch die Ausgabe als Bruch vorerst darzustellen, ohne die Sortierung.
Ehrlich gesagt kann ich mir darunter nichts vorstellen.
Meinst du
Code:
Zahl1 / Zahl2

oder

Zahl1 | Zahl2

oder

Zahl1
---------
Zahl2

cwriter
 

Sekiro24

Mitglied
Was sollte im letzten Feld eines for-Loops stehen?
for(Start; Condition; Statement). Du hast im Statement eine reine Expression (keine Veränderung des Zustands).



Ehrlich gesagt kann ich mir darunter nichts vorstellen.
Meinst du
Code:
Zahl1 / Zahl2

oder

Zahl1 | Zahl2

oder

Zahl1
---------
Zahl2

cwriter
Genau Zahl1 / Zahl2 soll ausgegeben werden.
Nur wie mache ich das so, dass es mit einem zweidimensionalen Array funktioniert.
Zuerst natürlich, sollte die Eingabe erfolgen.
:)
 

Technipion

Erfahrenes Mitglied
Ehrlichgesagt sehe ich noch einige andere Problemchen im Code.
Deine main() und die Funktion einlesen() scheinen keinen Typ zu haben?

Außerdem ein kleiner Tipp direkt am Anfang: Die Variablen Steine[][] und i_zeile, i_spalte und k sind sogenannte globale Variablen, weil sie von überall (in deiner Quellcode-Datei) sichtbar sind. Eigentlich versucht man immer so gut es geht globale Variablen zu vermeiden, in seltenen Fällen (oder als Anfänger) kann man sie jedoch benutzen. Allerdings ist es dann immer schön sie als global zu kennzeichnen. Ich z.B. beginne ihre Namen dann immer mit "g_". Also aus int Steine[7][2]; würde dann int g_Steine[7][2];. Nur ein kleiner Tipp, damit erinnert man sich immer daran, dass diese Variablen global sind.

Zur for-Schleife: Hast du verstanden was cwriter gemeint hat? Falls ja poste mal deinen aktualisierten Code. Falls nein: Wo genau liegt das Problem?

Gruß Technipion
 

cwriter

Erfahrenes Mitglied
Zuerst natürlich, sollte die Eingabe erfolgen.
Du inkrementierst die for-loop-Iterationsvariable nicht.
Nur wie mache ich das so, dass es mit einem zweidimensionalen Array funktioniert.
?
C:
printf("%d / %d", Steine[i][0], Steine[i][1]);
Es fällt mir ein bisschen schwer, deinen Kenntnisstand zu lesen. Die logische Herangehensweise bei diesen Dingen geht über die Typen.

Du hast einen Array von einem Array von ints namens Steine. Wir schreiben den Typ mal als [[int]]. Wenn du einmal dereferenzierst, bekommst du den Typ [int]. Das passt aber noch nicht für printf. Also musst du nochmals dereferenzieren. Das tatest du ja schon
Nun ist der 1x dereferenzierte Typ aber int[2]. D.h. du kannst auf 0 und 1 als Index zugreifen.
Vielleicht erklärt das die Herangehensweise ja etwas.

Deine main() und die Funktion einlesen() scheinen keinen Typ zu haben?
Sekiro nutzt C89, wo alles implizit int ist. Nicht mein Stil, aber naja...
Übrigens ist selbst in C11 noch implizit int - da aber mit einer Warnung. Aber anständige Menschen tun das nicht, da hast du schon recht ;)

Eigentlich versucht man immer so gut es geht globale Variablen zu vermeiden, in seltenen Fällen (oder als Anfänger) kann man sie jedoch benutzen. Allerdings ist es dann immer schön sie als global zu kennzeichnen. Ich z.B. beginne ihre Namen dann immer mit "g_". Also aus int Steine[7][2]; würde dann int g_Steine[7][2];. Nur ein kleiner Tipp, damit erinnert man sich immer daran, dass diese Variablen global sind.
Alles völlig richtig, aber für diese Aufgabenprogramme sollte das schon reichen - korrekterweise müsste man es lokal halten und per Pointer übergeben, aber es ist sowieso C89 - und damit ist die Chance, dass dieser Code irgendwo produktiv eingesetzt wird, nahezu 0 ;)

Gruss
cwriter
 
Zuletzt bearbeitet:

Sekiro24

Mitglied
Du inkrementierst die for-loop-Iterationsvariable nicht.
?
Jo total übersehen. Vielen Dank.

C:
printf("%d / %d", Steine[i][0], Steine[i][1]);
Es fällt mir ein bisschen schwer, deinen Kenntnisstand zu lesen. Die logische Herangehensweise bei diesen Dingen geht über die Typen.

Du hast einen Array von einem Array von ints namens Steine. Wir schreiben den Typ mal als [[int]]. Wenn du einmal dereferenzierst, bekommst du den Typ [int]. Das passt aber noch nicht für printf. Also musst du nochmals dereferenzieren. Das tatest du ja schon
Nun ist der 1x dereferenzierte Typ aber int[2]. D.h. du kannst auf 0 und 1 als Index zugreifen.
Vielleicht erklärt das die Herangehensweise ja etwas.


Sekiro nutzt C89, wo alles implizit int ist. Nicht mein Stil, aber naja...
Übrigens ist selbst in C11 noch implizit int - da aber mit einer Warnung. Aber anständige Menschen tun das nicht, da hast du schon recht ;)


Alles völlig richtig, aber für diese Aufgabenprogramme sollte das schon reichen - korrekterweise müsste man es lokal halten und per Pointer übergeben, aber es ist sowieso C89 - und damit ist die Chance, dass dieser Code irgendwo produktiv eingesetzt wird, nahezu 0 ;)

Gruss
cwriter[/QUOTE]
Mir ist schon bewusst das man eher zu lokalen Variablen neigt als zu globalen und diese dann an die jeweiligen Funktionen, die man verwenden möchte weitergibt. Die Aufgabe ist es vom Dozenten das Verständnis hinter globalen und lokalen Variablen klar zu machen. Nachher kommt noch eine weitere Funktion, der man dann Laufvariablen i und j übergeben soll. Diese Funktion soll berechnen heißen und die Zahlen der Dominosteine die gleich sind aneinander legen/sortieren.
Beispielsweise:
1. 5/2
2. 6/3
3. 1/4
4. 6/2
unsortiert

1. 5/2
2. 2/6
3. 6/3
4. 1/4
sortiert

Also 2 an 2 und 6 an 6
Dabei kann der Stein auch gedreht werden.
 
Zuletzt bearbeitet:

Sekiro24

Mitglied
?
Jo total übersehen. Vielen Dank.

C:
printf("%d / %d", Steine[i][0], Steine[i][1]);
Es fällt mir ein bisschen schwer, deinen Kenntnisstand zu lesen. Die logische Herangehensweise bei diesen Dingen geht über die Typen.

Du hast einen Array von einem Array von ints namens Steine. Wir schreiben den Typ mal als [[int]]. Wenn du einmal dereferenzierst, bekommst du den Typ [int]. Das passt aber noch nicht für printf. Also musst du nochmals dereferenzieren. Das tatest du ja schon
Nun ist der 1x dereferenzierte Typ aber int[2]. D.h. du kannst auf 0 und 1 als Index zugreifen.
Vielleicht erklärt das die Herangehensweise ja etwas.


Sekiro nutzt C89, wo alles implizit int ist. Nicht mein Stil, aber naja...
Übrigens ist selbst in C11 noch implizit int - da aber mit einer Warnung. Aber anständige Menschen tun das nicht, da hast du schon recht ;)


Alles völlig richtig, aber für diese Aufgabenprogramme sollte das schon reichen - korrekterweise müsste man es lokal halten und per Pointer übergeben, aber es ist sowieso C89 - und damit ist die Chance, dass dieser Code irgendwo produktiv eingesetzt wird, nahezu 0 ;)

Gruss
cwriter
Mir ist schon bewusst das man eher zu lokalen Variablen neigt als zu globalen und diese dann an die jeweiligen Funktionen, die man verwenden möchte weitergibt. Die Aufgabe ist es vom Dozenten das Verständnis hinter globalen und lokalen Variablen klar zu machen. Nachher kommt noch eine weitere Funktion, der man dann Laufvariablen i und j übergeben soll. Diese Funktion soll berechnen heißen und die Zahlen der Dominosteine die gleich sind aneinander legen/sortieren.
Beispielsweise:
1. 5/2
2. 6/3
3. 1/4
4. 6/2
unsortiert

1. 5/2
2. 2/6
3. 6/3
4. 1/4
sortiert

Also 2 an 2 und 6 an 6
Dabei kann der Stein auch gedreht werden.[/QUOTE]

Ich benutze momentan den Code Blocks Editor. Wenn ich den Code compilieren möchte, [\QUOTE]
Kann ich keine zweite Eingabe machen. Wieso ? mit Cygwin hatte es irgendwie geklappt.
Der Code sieht bis hierhin wie folgt aus:
C:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

int Steine[7][2];
int i_zeile, i_spalte, j, k;

main(){
    printf("Bitte geben Sie nun 7 Dominosteine ein\n");
    einlesen();
    for(k = 0; k < 7; k++)printf("%d. %d/%d\n", (k+1) ,Steine[k][0], Steine[k][1]);
}

einlesen(){
    for(i_zeile = 0; i_zeile < 7; i_zeile++){
        for(i_spalte = 0; i_spalte < 2; i_spalte++){
            scanf("%d. %d", i_zeile, &Steine[i_zeile][i_spalte]);
        }
    }
}
[\CODE]
 
Zuletzt bearbeitet:

cwriter

Erfahrenes Mitglied
Ich benutze momentan den Code Blocks Editor. Wenn ich den Code compilieren möchte,
Kann ich keine zweite Eingabe machen. Wieso ? mit Cygwin hatte es irgendwie geklappt.
Der Code sieht bis hierhin wie folgt aus:
Naja, was macht denn scanf?
Wenn du "%d. %d" als scanf format string hast, dann wird nach i_zeile gelesen - bzw. auch nicht, das Programm sollte abstürzen.
Nimm das "%d." und die 'i_zeile' auis dem Scanf raus, dann solltest du alle Zahlen eingeben können.

Gruss
cwriter
 

Sekiro24

Mitglied
Habs denke ich mal verstanden.
Jetzt soll ich noch das mit dem berechnen machen. Ich hab da mal was geschrieben, jedoch blicke ich da noch nicht richtig durch, wie ich das realisieren soll, dass der überübernächste kontrolliert werden soll. Das ganze soll rekursiv erfolgen. Also das sich die Funktion berechnen selbst aufruft.
Ich dachte da an eine geschachtelte for in for Schleife aber dann ist es doch nicht mehr rekursiv oder ?
C:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

int Steine[7][2];
int berechnen(int, int);
int i, j;
int main(){


    printf("Bitte geben Sie nun 7 Dominosteine ein\n");
    einlesen();
    for(i = 0; i < 7; i++)printf("%d. %d/%d\n", (i+1) ,Steine[i][0], Steine[i][1]);
    i = 0; j = 0;
    berechnen(i,j);
    return 0;
}

void einlesen(){
    for(i = 0; i < 7; i++){
        printf("%d.\n", (i+1));
        for(j = 0; j < 2; j++)
          scanf("%d", &Steine[i][j]);
    }
}

/*Überprüfung, ob der aktuelle Stein durch Anlegen mit dem nächsten Stein identisch ist bsp: 1.)3/4,4/2,2/1*/
/*Auch durch drehen*/
int berechnen(int i, int j) {
    int temp1, temp2;
    if(Steine[i][0] == Steine[j][0])
    {
        i++;
        j++;
        brechnen(i,j);
    }
    else
        if(Steine[i][0] == Steine[j][1])
        {
            temp1 = Steine[j][0];
            temp2 = Steine[j][1];
            Steine[j][0] = temp2;
            Steine[j][1] = temp1;
        }
    else{
        /*Wenn der aktuelle Stein nicht passt soll der übernächste Stein überprüft werden. Wenn dieser nicht passt
        soll der überübernächste Stein überprüft werden*/
    }

}
 

cwriter

Erfahrenes Mitglied
Jetzt soll ich noch das mit dem berechnen machen. Ich hab da mal was geschrieben, jedoch blicke ich da noch nicht richtig durch, wie ich das realisieren soll, dass der überübernächste kontrolliert werden soll.
Da sind wir ja schon zwei.


Ich dachte da an eine geschachtelte for in for Schleife aber dann ist es doch nicht mehr rekursiv oder ?
Naja - auch mit 2 Schleifen kann sich eine Funktion noch selbst aufrufen...


Da ich dir nicht die Arbeit abnehmen will, aber dennoch einen guten Rat geben kann, habe ich mal ein Beispiel in C++ implementiert. Das ist ähnlich genug, um den Algorithmus zu zeigen, aber weit genug entfernt, dass man nicht mit "1 zu 1" umschreiben davonkommt. Am besten lässt du das Beispiel durch einen C++ Compiler, um die Resultate zu sehen:
C++:
#include <iostream>
#include <time.h>
#include <vector>
#include <string>

constexpr size_t blockcount = 10;

struct Stein {
    Stein(int a, int b)
        : v1(a), v2(b) {

    }

    std::string format(bool normal = true) const {
        auto a = normal ? v1 : v2;
        auto b = normal ? v2 : v1;
        return "(" + std::to_string(a) + "|" + std::to_string(b) + ")";
    }

    std::string format(int expect) const {
        return format(expect == v1);
    }
    int v1;
    int v2;
};

std::vector<int> berechne(std::vector<int> used, const std::vector<Stein>& all, int i, int orientation)
{
    // Copy the existing path as a reference return value
    decltype(used) maxpath = used;

    // Check all bricks
    for (size_t j = 0; j < all.size(); ++j)
    {
        // Check if the current brick being checked is not already used
        bool not_this = false;
        for (size_t f = 0; f < used.size(); ++f) {
            if (used[f] == j) {
                not_this = true;
                continue;
            }
        }

        if (not_this) continue; // Skip if already used

        // Copy the orientation
        decltype(orientation) new_orientation = orientation;
        // If not the first block
        if (i != -1)
        {
            // Set the orientations of the next blocks
            if (orientation == 1 || orientation == 0) {
                // "Normal"
                if (all[i].v2 == all[j].v1) {
                    // orientation stays the same
                    new_orientation = 1;
                }
                else if (all[i].v2 == all[j].v2) {
                    // swap
                    new_orientation = -1;
                }
                else continue;
            }
            else if (orientation == -1 || orientation == 0) {
                // Inverted
                if (all[i].v1 == all[j].v1) {
                    // swap
                    new_orientation = 1;
                }
                else if (all[i].v1 == all[j].v2) {
                    // still -1
                    new_orientation = -1;
                }
                else continue;
            }
            else {
                // No match in any orientation, continue
                continue;
            }
        }
        // Copy the current state
        auto cpy = used;
        // Add the selected value to the copied state
        cpy.push_back(j); // Add to list
        // Recurse
        auto r = berechne(cpy, all, j, new_orientation);
        // Assign the maxpath if the path was deeper (As this is DFS)
        if (maxpath.size() < r.size())
            maxpath = r;
    }

    // Return the found path
    return maxpath;

}

int main(int argc, char* argv[])
{
    // Randomize and fill
    srand(time(NULL));

    auto rf = []() { return (rand() % 10) + 1; };

    std::vector<Stein> steine;

    for (size_t i = 0; i < blockcount; ++i) {
        steine.push_back(Stein(rf(), rf()));
    }

    // Start by setting any start block (-1) and a fixed orientation
    std::vector<int> used;
    auto ret = berechne(used, steine, -1, 1);

    // Dump info for all bricks
    std::cout << "All bricks: " << std::endl;
    for (const auto& x : steine) {
        std::cout << x.format() << " => ";
    }

    // Dump info about the sorting
    std::cout << std::endl << "Sorted bricks: " << std::endl;
    int lastval = -1;
    for (const auto& x : ret) {
        if (lastval == -1) {
            // We know that we started with orientation = 1
            lastval = steine[x].v2;

            std::cout << steine[x].format(true) << " => ";
        }
        else {
            std::cout << steine[x].format(lastval) << " => ";
            lastval = steine[x].v1 == lastval ? steine[x].v2 : steine[x].v1;
        }
    }
    std::cout << "\n (missing " << steine.size() - ret.size() << " brick(s))" << std::endl;

    return 0;
}

Kurze Theorie dazu: Das ist ein DFS (depth first search) basierter Ansatz, der sehr teuer ist, was Stack / Speicher angeht, aber einfach zu verstehen.
Im Prinzip wird in jeder Tiefe geschaut, welche Steine passen. Für jeden der passenden Steine wird das Problem verkleinert (minus den Stein, der gerade genommen wurde) und wieder gelöst.

Ich habe keine Ahnung, wie es mit nur den 2 Parametern i und j gehen soll, und es macht auch keinen Sinn, Rekursion und Iteration (mit Speicher ausserhalb) zu mischen.

Dieser Code findet immer einen Optimalen Pfad. Als Übung kannst du den Beweis dazu schreiben :)
Vielleicht hilft das ja als Ansatz.

Tipps für C++ => C:
1) push_back fügt ein Element ans Ende des Arrays an. In C macht man das normalerweise, indem man einen genügend grossen Array reserviert und eine Iterationsvariable hat, an deren Position eingefügt wird, und dann wird diese Variable um 1 erhöht. Es gibt hier aber auch elegantere Methoden (da ohnehin kopiert wird) mit memcpy.
2) decltype übernimmt den Typ einer Variable.
3) auto übernimmt den Typ der zugewiesenen Variable
4) In diesem Beispiel werden die Vektoren by value übergeben. C-Arrays müssen zuerst kopiert werden!
5) std::cout kann man mit printf() ersetzen, std::string mit const char*. Aber: Scopes im Auge behalten! (Compilerwarnungen lesen, -Wall -Wextra)
6) Stein ist ein Struct mit einem Konstruktor. Die Memberfunktionen kannst du ebenfalls schnell umschreiben.

Gruss
cwriter
 

Neue Beiträge