MoinMoin,
da ich nicht so genau weiß, wonach ich suchen müsste, versuche ich
einfach mal mein Problem zu beschreiben, es wird daher etwas länger
werden, dafür entschuldige ich mich schonmal im voraus.
Ich befasse mich seit Ewigkeiten mal wieder ein wenig mit dem
Programmieren auf dem PC, sonst hab ich immer nur Atmegas programmiert.
Das Ganze passiert in C mit dem Onlinegdbcompiler.
Ich versuche ein Game of Life zu schreiben, das "mehr Möglichkeiten" als
Conways bietet, und erhoffe mir dadurch mehr Komplexität und
Lebendigkeit reinzukriegen. Alles passiert auf einem 2D-Spielfeld,
welches aus Feldern mit einer X und Y Position besteht mit verbundenen
Rändern besteht, also ein Torus. Jedes Feld soll Rasen haben können, auf
der Welt laufen Hasen rum, die den Rasen fressen wollen, und es laufen
Füchse rum, die die Hasen fressen wollen.
Als erstes erzeuge ich ein struct Feld
printf("Feld %d %d hat Rasen %d \n",x,y,Spielfeld[x][y].Rasen);
11
x++;
12
}
13
;
Nebenbei noch die Definition für ein struct Hasen und ein Fuchs, die
dann Eigenschaften wie Alter Geschlecht usw enthalten.
Nun hänge ich daran fest, dass so einen "Initialisierungsdurchlauf" auch
für die Hasen und Füchse machen möchte. Aus Gründen die mir gerade
entfallen sind, war irgendwas unpraktikabel daran, in das Feld-Struct
mitaufzunehmen ob Fuchs und Hase auf dem Feld vorhanden sind. Wobei ich
glaube, dass war, weil die Hasen und Füchse auch mehrere Eigenschaften
haben sollten.
Die X und Y-Schleifen wie oben, und dann sollte sowas wie
1
rand()%100;
2
ifrand>90erzeugeHasen
kommen. Aber Variablen (ein Struct ist doch nichts anderes als ne
Variable?) kann man doch nur Eingangs der Funktion erzeugen? Soll ich
also nun für jedes Feld nen toten Hasen und Fuchs vorhalten, der dann
mit ner 10% Wahrscheinlichkeit lebendig wird?
Oder kann ein Struct auch andere Structs als Member haben? Wobei dass
dann ja aufs selbe hinaus liefe, wie für jedes Feld nen toten Hasen zu
erzeugen....
Ich hoffe, es schimmert wenigstens ansatzweise durch, was mein Problem
ist.
MfG Chaos
In dem Beispiel von denen soll eine Usereingabe entscheiden, wie groß
das Array ist. In meinem Fall will ich ja
1
structHase
2
{
3
intAlter;
4
intGeschlecht;
5
intHunger;
6
intPosX;
7
intPosY;
8
};
9
intHasenzaehler
10
11
structHase*Hasenpointer;//????
12
intsize=sizeof(structHase);//????
13
14
while(x<xsize)
15
{
16
while(y<ysize)
17
{
18
Zufall=rand()%100;
19
if(rand>=90)
20
{
21
Hasenzaehler++;
22
Hasenpointer=(int*)malloc(size);
23
}
24
}
25
}
Wow, das hab ich mir grad ausgedacht und es kompiliert tatsächlich. Wie
greife ich dann auf meine einzelnen Hasen zu? Ich muss mir die Adresse
vom ersten merken und kann von da aus mit sizeof(struct Hase)
hochmultiplizieren, um die einzelnen anzusprechen? Oder muss ich davon
ausgehen, dass das "Hasenmachen" von nem anderen Prozess unterbrochen
wird, der möglicherweise auch malloc von verwendet, und mir dann
zwischen meine Hasen anderen Krams schmeißt?
J. T. schrieb:> Wow, das hab ich mir grad ausgedacht und es kompiliert tatsächlich. Wie> greife ich dann auf meine einzelnen Hasen zu?
Wenn Du vorher noch nie was mit malloc gemacht hast, dann mache das
Spiel zuerst mit einer gedachten Maximalwelt (keine Ahnung, 32000x32000
Hasen, Felder und Füchsen, egal). Wenn es zuviel Speicher ist oder zu
lange rechnet, dann nimm 2 Nullen weg.
Erst wenn das Spiel rund läuft, überlege Dir, ob es Sinn macht,
dynamisch zu werden. Und wenn die Antwort "Ja" ist, dann befasse Dich
damit.
Grundsätzlich hab ich schon einige solche Spielchen programmiert und hab
da immer um das Problem herumgeschifft. Ich finde, "jetzt" ist ein guter
Zeitpunkt, sich mit malloc zu befassen.
Kann wer dazu
J. T. schrieb:> Oder muss ich davon> ausgehen, dass das "Hasenmachen" von nem anderen Prozess unterbrochen> wird, der möglicherweise auch malloc von verwendet, und mir dann> zwischen meine Hasen anderen Krams schmeißt?
etwas sagen?
Auf einem halbwegs aktuelllen Betriebssystem wie Linux oder Windows hat
jeder Prozeß seinen eigenen virtuellen Speicher, den sie sich nicht
gegenseitig verpfuschen können (außer daß natürlich nicht jeder
gleichzeitig beliebig viel nehmen kann).
Ansonsten sind da noch ein paar Grundlagen zu Speicher und
Zeigerarithmetik nützlich, die vielleicht den Rahmen von ein paar
Beiträgen eher übersteigen.
Z.B. der gute alte Kernighan und Ritchie, oder eines der vielen anderen
C-Bücher.
J. T. schrieb:> Ich muss mir die Adresse> vom ersten merken und kann von da aus mit sizeof(struct Hase)> hochmultiplizieren, um die einzelnen anzusprechen?
Nein, kannst Du nicht.
Du kannst überhaupt keine Annahmen vom Speicher treffen. Weder, dass Du
welchen bekommst, noch dass Du auch nur ein Byte darüber hinaus
verwenden oder gar ahnen kannst.
Jeder malloc-Aufruf generiert Dir Speicher irgendwo oder halt nicht.
die Beschreibung von malloc in einer C-Referenz Deiner Wahl ist ein
guter Startpunkt. Zumindest, wenn Dein Programm ohne Malloc
funktioniert.
A. S. schrieb:> Nein, kannst Du nicht.
das allein könnte man als konkrete Antwort stehen lassen.
A. S. schrieb:> Weder, dass Du> welchen bekommst, noch dass Du auch nur ein Byte darüber hinaus> verwenden
warum sollte ich überhaupt auf den Gedanken kommen, mehr Speicher nutzen
zu wollen, als ich hab?
A. S. schrieb:> oder gar ahnen kannst.
Deine Hochnäsigkeit kannst du dir gerne sparen
A. S. schrieb:> Jeder malloc-Aufruf generiert Dir Speicher irgendwo oder halt nicht.
Sagt dir aber freundlicherweise bescheid, ob er es getan hat, oder halt
nicht.
A. S. schrieb:> die Beschreibung von malloc in einer C-Referenz Deiner Wahl ist ein> guter Startpunkt. Zumindest, wenn Dein Programm ohne Malloc> funktioniert.
Der Hinweis auf malloc wurde doch schon als aller erster Beitrag
genannt. Mit deiner negativen Art kannst du dich gerne von meinen
Threads fernhalten.
Gerne darfst du mir vorher noch erklären, warum das hier:
1
//Hasen erzeugen
2
while(x<SpielfeldgroesseX)
3
{
4
while(y<SpielfeldgroesseY)
5
{
6
Zufall=rand()%100;
7
if(Zufall>=99)
8
{
9
10
11
Hasenpointer=malloc(size_sHase);
12
Hasenpointer->Geschlecht=rand()%2;
13
Hasenpointer->Alter=rand()%20;
14
Hasenpointer->Hunger=rand()%100;
15
Hasenpointer->PositionY=y;
16
Hasenpointer->PositionX=x;
17
18
Hasenzaehler++;
19
20
if(ersterHaseDa_Flag==0)
21
{
22
ersterHaseDa_Flag=1;
23
ErsterHase_Pointer=Hasenpointer;
24
}
25
26
printf("Hase %d ist ",Hasenzaehler);
27
if((Hasenpointer->Geschlecht)==0)
28
{
29
printf("weiblich ");
30
}
31
else
32
{
33
printf("männlich ");
34
}
35
printf("und %d Jahre alt.",Hasenpointer->Alter);
36
//printf("Feld %d %d hat einen Hasen \n", x, y);
37
}
38
y++;
39
}
40
y=0;
41
x++;
42
}
43
x=0;
exakt so wie in meinen Überlegungen weiter oben dargelegt funktioniert,
obwohl es ja nicht geht, wie du sagst.
J. T. schrieb:> Hasenpointer += sizeof(sHase);
Ein pointer++ reicht um auf das nächste Element zu zeigen, die
Elementgröße berücksichtigt der Compiler schon. Habe jetzt aber nicht
alles im Detail verfolgt.
J. T. schrieb:> warum sollte ich überhaupt auf den Gedanken kommen, mehr Speicher nutzen> zu wollen, als ich hab?
Weil Du a) faktisch genau das wolltest und tatest: Annahmen über
Speicher außerhalb des zuerst angeforderten.
und es b) womöglich funktioniert, wenn Du es ausprobierst. Darum schrieb
ich
> A. S. schrieb:>> oder gar ahnen kannst.
Wenn es für Dich nach
> Hochnäsigkeit
klingt, tut es mir Leid.
> Sagt dir aber freundlicherweise bescheid, ob er es getan hat, oder halt> nicht.
Ja. Sonst wäre es nicht nutzbar.
J. T. schrieb:> Der Hinweis auf malloc wurde doch schon als aller erster Beitrag> genannt. Mit deiner negativen Art kannst du dich gerne von meinen> Threads fernhalten.
Du hast explizit nochmal nachgefragt. Sorry, dass ich Deine Beiträge
lese.
> Gerne darfst du mir vorher noch erklären, warum das hier:
dort sehe ich nichts, was irgendwie mit +=sizeof oder ptr++ zu tun hat.
Dass Du im nächsten Post ptr++ nehmen müsstest, geschenkt, hat Johannes
schon gesagt. Dein Konstrukt mit +=sizeof() gibt es genauso, allerdings
dann mit Byte-Pointern (uint8_t* oder char* z.B.).
Mit den üblichen Implementierungen von Malloc funktioniert beides
meistens *nicht*, da malloc seine Kontroll-Informationen an den Block
(bzw. davor) klatscht UND oft die nächst größere "glatte" Blockgröße
nimmt. Und wenn es bei Dir funktioniert, ist es trotzdem Unsinn darüber
nachzudenken.
Gruß
achim
(Und ja, es gibt sogar einen Befehl, mit dem Du genau das machen
könntest. Aber auch der führt ins Chaos im jetzigen Projekt, also
sprechen wir nicht drüber)
J. T. schrieb:> Wobei das hier:> [...]> nur noch für den ersten Hasen klappt.
Das ist genau das Problem, von dem A.S. spricht. malloc liefert dir
einen Pointer auf Speicher zurück, in den du deinen Hasen stecken kannst
(sofern es erfolgreich war). Mehr Annahmen darfst du über diesen Pointer
nicht treffen. Insbesondere darfst du nicht davon ausgehen, dass mehrere
aufeinander folgende malloc-Aufrufe dir die Speicherbereiche an
aufeinanderfolgenden Adressen gibt. Jeder malloc-Aufruf gibt dir einen
Speicher-Block, der zu den anderen in keinerlei Beziehung steht.
Es kann zwar je nach System sogar funktionieren, aber das ist dann
reines Glück (oder Pech, da man einen Bug produziert hat, den man in dem
Moment nicht erkennt).
J. T. schrieb:> nur noch für den ersten Hasen klappt.
Der Speicherbereich, den Dir malloc() reserviert, ist (praktisch
gesehen) rein zufällig. D.h. wenn Du drei mal hintereinander malloc()
aufrufst, bekommst Du insgesamt drei Zeiger zurück, die irgendwo hin
zeigen. Die Speicherbereiche sind nicht hintereinander.
D.h. wenn Du so mit malloc() arbeitest, um für jeden Hasen ein neuen
Speicherbereich dir geben lässt, hast Du erst den halben Weg geschafft.
Denn für jeden Hasen-Speicherbereich musst Du Dir jetzt noch den Zeiger
auf diesen Hasen-Speicher merken.
Du hast das Problem nicht gelöst, sondern nur "verschoben". Also:
Wenn Du 5 Hasen mit malloc() besorgt hast, hast Du 5 Speicherblöcke und
zusätzlich 5 unterschiedliche Zeiger auf diese Speicherblöcke. Diese
Zeiger musst Du irgendwo speichern. Am besten in einer Tabelle.
Da Du aber nicht weist, wie groß Deine Tabelle sein soll, denn es kommen
ja gelegentlich neue Hasen hinzu, stehst Du wieder vor dem gleichen
Problem: Du besorgst Dir Speicher für eine Tabelle, die Platz für 5
Zeiger auf Hasen-Speicherbereiche hat. Aber mit dem 6. Hasen, geht's
nicht weiter. Du brauchst eine größere Tabelle. Dafür gibt es die
Funktion realloc(). Die vergrößert (oder verkleinert) einen
Speicherbereich, der zuvor mit malloc() besorgt wurde. Aber Achtung:
Nach dem vergrößern oder verkleinern hat sich die Adresse des
Speicherbereichs geändert!
A. S. schrieb:> Wenn es für Dich nach>> Hochnäsigkeit>> klingt, tut es mir Leid.
Tat es allerdings tatsächlich. Aber nach deinem Nachschub und einer
Erklärung, sehe ich, dass es nicht so gemeint war und möchte mich für
meine Ungehaltenheit entschuldigen. Scheinbar hat die Miesepeterigkeit
mich auch schon ein wenig angefallen.
Experte schrieb:> Dafür gibt es die Funktion realloc().
Auch dir vielen Dank für deine Ausführungen. Realloc hab ich inzwischen
auch entdeckt gehabt. Bin aber genau auf die von dir erwähnten Probleme
gestoßen.
Naja ich werd mal etwas weitertüfteln. Evtl fallen mir ja noch die
richtigen Kniffe ein
Jedes der Elemente ist vom Typ deiner struct.
Man allokiert jetzt nicht jeweils Platz für eine struct, sondern gleich
für alle.
1
typedefstruct{...}feld_t;
2
size_tnFeld=0;// Anzahl in die eine Richtung (Zeilenlänge)
3
size_tmFeld=0;// Anzahl in die andere Richtung (Spaltenlänge, Anzahl Zeilen)
4
feld_t*feld=NULL;
5
...
6
// irgendwie Größe ermitteln:
7
nFeld=...;
8
mFeld=...;
9
// Speicher holen für alle Elemente:
10
feld=(feld_t*)malloc(nFeld*mFeld*sizeof(feld_t));
11
if(!feld)...Fehlerbehandlung...;
12
// jetzt alle Elemente initialisieren...
Jetzt liegen die Elemente einfach hintereinander im Speicher, üblich ist
in C zeilenweise Organisation. Also feld[0] ist gedacht feld_0,0,
feld[1] ist feld_0,1 etc. bis feld[nFeld\*mFeld-1] ist
feld_mFeld-1,nFeld-1.
feld_ij wäre also feld[i*nFeld+j].
Mit Makros oder Funktionen könnte man die Indices umrechnen, damit man
es nicht dauernd verwechselt.
Vorschlag 2:
Man macht für jede Zeile des Feldes ein malloc(nFeld*sizeof(feld_t)) und
speichert die Adresse dieser Zeile in einem Feld von Zeigern (das
ebenfalls allokiert wird)
// jetzt alle Elemente der Zeile initialisieren...
14
}
Das sieht erstmal umständlicher aus.
Hat aber einen charmanten Vorteil: man kann auf die Elemente einfach mit
feld[i][j] zugreifen.
Oder eine von vielen anderen Möglichkeiten...
Wer das zu umständlich findet, ist ein Kandidat für C++ (std::vector<
std::vector< feld_t > > ...).
Aber zum Üben darf man sich auch gerne mal mit C durchquälen, dann weiß
man etwas moderneres zu schätzen.
WOW! Ich bin begeistert, vielen Dank für die sehr konkreten Vorschläge.
Ich glaube, meine letzten Grübeleien gingen sehr in Richtung von
Vorschlag2, waren aber leider noch nicht ausgegoren, als ich nochmal
guckte, ob wer geschrieben hat :D
Ich werd erstmal meinen Ansatz weiterfolgen, und wenn er fertig oder
unfunktionell ist, mich mal deinem Vorschlag widmen.
printf(", %d Tage alt und auf Position %d X und %d Y.\n\n",(Hasenliste_Pointer+(Hasenzaehler*size_sHase))->Alter,(Hasenliste_Pointer+(Hasenzaehler*size_sHase))->PositionX,(Hasenliste_Pointer+(Hasenzaehler*size_sHase))->PositionY);
printf("und %d Tage alt \n\n",(Hasenliste_Pointer+(Hasenzaehler*size_sHase))->Alter);
123
Hasenzaehler++;
124
}
125
126
127
return0;
P.S.
ich seh grad, ich hab beim realloc wieder die Fehlerbehandlung
vergessen.
P.P.S.
Und so muss ich die toten Hasen mit rumschleppen, damit die Liste
konsistent bleibt... auch noch suboptimal
Ich hab jetzt mal versucht, das Hasenerzeugen in eine Funktion
auszulagern. Damit hatte ich irgendwie schon immer Probleme. Ich
übergebe der Funktion den Hasenlistenpointer und nen Pointer auf die
Hasenherdengröße, ansonsten ist die Hasenerzeugung, die schon
funktioniert, eins zu eins rüberkopiert.
Wenn ich den Funktionsaufruf auskommentiere und die "zu Fuß" Variante in
der main nutze, bekomme ich meine ersten 25 Hasen aufgelistet.
Kommentiere ich die "zu Fuß" Variante aus und nutze den
Funktionsaufrauf, wird ohne Warnungen/Fehler kompiliert, das Programm
läuft und gibt 0 zurück, aber es gibt nur die ersten drei printf aus.
Die Fehler/Go-Behandlung vom malloc erscheint gar nicht mehr.
Was läuft da falsch?
Hier nochmal der jetzige Code:
printf(", %d Tage alt und auf Position %d X und %d Y.\n\n",(Hasenliste_Pointer+(Hasenzaehler*size_sHase))->Alter,(Hasenliste_Pointer+(Hasenzaehler*size_sHase))->PositionX,(Hasenliste_Pointer+(Hasenzaehler*size_sHase))->PositionY);
Wenn ich die Spielfeldgröße auf 25² statt 256² stelle, dann geht es auch
als Funktionsaufruf....
aber ab Hase 6 haben alle Werte jeweils den Wert 0.....
Wenn ich mit dem Debugger durchsteppe, kommt irgendwann in der
"Hasenerzeugenschleife" ein Segmentationerror. Als ich auf 25²
Spielfeldgröße runtergestellt hat, hat meine Geduld gelangt, 25 mal den
Brakepoint in der äusseren Schleife anzuspringen und ich stellte mit
erstaunen fest, dass es plötzlich geht.
Auf 256 fehlt mir die Geduld. Und irgendwie weiß ich nicht, wie ich den
debugger dazu bekomme, mir den Durchlauf bei dem der Segmentationserror
kommt, auszugeben.
P.S.
Es ist noch wirrer. Wenn ich die Größe auf 25² stelle, dann haben auch
in der zu Fuß Variante ALLE Hasen die Werte 0.
Wenn ich wieder groß Stelle haben die Hasen plausible Werte.
J. T. schrieb:> ansonsten ist die Hasenerzeugung, die schon> funktioniert, eins zu eins rüberkopiert.
Nein. Du hast da die +sizeof-Version behalten.
Zu realloc habe ich ja schon Alles gesagt.
A. S. schrieb:> Nein. Du hast da die +sizeof-Version behalten.
Sorry, ich versteh nicht, worauf du hinauswillst?
In der Funktion erzeuge ich mir nur die Variablen die aus der main
fehlen, und dann kommt doch genau das gleiche, wie in der Main-schleife?
oder bin ich nu völlig betriebsblind? Dann übergebe ich der Funktion den
Pointer aus der main, der wegen dem malloc wohin auch immer zeigt.
Zusätzlich noch den Pointer auf die Herdengröße die ich wissen will.
Ansonsten muss in der Schleife doch das +sizeof? sein
J. T. schrieb:> (Hasenliste_Pointer+(Hasenzaehler*size_sHase))->Geschlecht = rand()%2;> //Geschlecht 0 = männlich 1 = weiblich
meinst du überhaupt diese Stelle?
J. T. schrieb:> J. T. schrieb:>> (Hasenliste_Pointer+(Hasenzaehler*size_sHase))->Geschlecht = rand()%2;>> //Geschlecht 0 = männlich 1 = weiblich>> meinst du überhaupt diese Stelle?
ja. Im Original (und richtig) ist es
J. T. schrieb:> (Hasenliste_Pointer+Hasenzaehler)->Geschlecht => rand()%2;
Genauso gut ginge auch
Ein paar Anmerkungen:
- Teile und herrsche: Kleine Funktionen die einen Teil erledigen.
- Wenn ein Fehler auftritt, Programm sofort beenden ist einfach.
Viel Spaß:
1
#include<stdlib.h>
2
#include<stdio.h>
3
4
structHase
5
{
6
intich_bin_ein_hase;
7
};
8
9
structHasenArray
10
{
11
size_tplatz;
12
size_tanzahl;
13
structHase**hasen;
14
};
15
16
constintINIT_HASEN_ARRAY_PLATZ=10;
17
18
voidexit_with_error(constchar*msg)
19
{
20
puts(msg);
21
exit(1);
22
}
23
24
voidinit_array(structHasenArray*ha)
25
{
26
if(!ha)
27
exit_with_error("init_array() mit NULL-Zeiger aufgerufen");
28
29
ha->anzahl=0;
30
ha->platz=INIT_HASEN_ARRAY_PLATZ;
31
ha->hasen=malloc(ha->platz*sizeof(structHase*));
32
33
if(!ha->hasen)
34
exit_with_error("Kein Speicher für init_array()");
35
}
36
37
voidresize_array(structHasenArray*ha)
38
{
39
if(!ha)
40
exit_with_error("resize_array() mit NULL-Zeiger aufgerufen");
Ich blicke noch nicht ganz, wie das Ding arbeiten soll.
Aber so geht es sicher nicht...
Erstens solltest du mit möglichst vielen Warnungen kompilieren und die
auch beachten.
Bspw. gibst du mit falschen Formatanweisungen in printf() aus (%d für
unsigned), das kann man vermeiden.
Zumindest -Wall -Wextra.
Zweitens wäre ein ordentlich formatierter besser lesbar.
Drittens müsstest du wirklich mal Grundlagen lernen.
Eine der Grundlagen in C ist Zeigerarithmetik.
printf("und %d Tage alt \n\n",(Hasenliste_Pointer+(Hasenzaehler*size_sHase))->Alter);
5
...
Wenn du in C zu einem Zeiger etwas addierst, dann wird das implizit mit
der Größe des Typs multipliziert, auf das der Zeiger zeigt gemäß seiner
Deklaration.
Deine Multiplikation mit der Größe der Rammler ist also falsch.
Weiterhin reicht es nicht, einfach nur etwas auszugeben und einfach
weiter zu machen, wenn realloc() fehlschlägt.
Auch sehe ich nicht, wieso du die Hasen bis 25 benutzt, ohne zu wissen
wieviele es sind. Aber wie gesagt wirklich verstanden habe ich das
Programm nicht...
Aber daß es so nicht klappt, ist schon zu sehen.
Ich hab das ganze jetzt erst mal ohne malloc/realloc gemacht.
Klaus W. schrieb:> Auch sehe ich nicht, wieso du die Hasen bis 25 benutzt, ohne zu wissen> wieviele es sind.
Das hatte ich gemacht, um zu sehen, ob überhaupt mehr als ein Hase
erzeugt und gespeichert wird, ganz zu Anfang hatte ich mir nur den
ersten Hasen ausgeben lassen, und das klappte. Aber trotzdem ging es
nicht. Also hab ich mir mehr Hasen ausgeben lassen...
Klaus W. schrieb:> Aber wie gesagt wirklich verstanden habe ich das> Programm nicht...
Das ist, so wie es ist, auch noch nicht zu verstehen, da völlig
unfertig. Das ist noch nicht einmal die Initialsierung der Welt.
Da ich die Initialisierung der Welt nun aber zum laufen bekomme
habe(noch ohne malloc/realloc), und gerne "etwas sehen" würde, hab ich
mich dunkel an Allegro erinnert. Flugs CodeBlocks runtergeladen und
Allegro5. CodeBlocks installiert, mich soweit durch die ganzen
Einstellungen gefriemelt, bis er endlich nicht mehr gemeckert hat, es
gäbe keine "allegro.h".
Aber dann scheitert er schon daran, die Funktion al_init() zu finden
beim compilieren, obwohl mir die Funktion explizit vorgeschlagen wurde,
vom Autofill.
Hier und da etwas gelesen, DevC++ mit seinem PacketManager wurde öfters
lobend erwähnt, also flugs mal den installiert. Der PacketManager hat
dann auch Allegro installiert. Aber DevC++ findet dann wieder nicht
einmal die allegro.h....
Naja ich denke ich werd dafür mal nen neuen Thread aufmachen, ist ja
doch recht OT
So ich hatte mal wieder ein wenig Zeit, weiter zu tüfteln.
Wen das Spiel nicht interessiert, die eigentlich Frage kommt ca in der
Mitte vom Post.
Im Anhang mal 2 "Mitschnitte" von Runden als .cvs-Datei. Der erste Wert
steht jeweils für die Anzahl an Hasen, der zweite Wert steht für die
Anzahl an Füchsen und der dritte Wert ist der Rundenzähler. Leider hab
ich keinen Durchschnittswert der Grashöhe (quasi wieviel Futter den
Hasen auf dem jeweiligen Feld zur Verfügung steht) mit dazugetan, das
ist in der grafischen Ausgabe ganz hübsch anzusehen.
Die spannende Runde war in so fern spannend, dass um Runde 4250 herum
nur noch ein Fuchs am Leben war, zum Glück, oder Pech für die Hasen, war
es ein trächtiges Weibchen. Und es hat wohl nen Männchen bekommen,
ziemlich inzestuöser Haufen, diese Füchse :D.
Die andere Runde hab ich mal ohne Grafikausgabe laufen lassen. (Ich hab
das Allegro immer noch nur in ner alten Version und ziemlich rudimentär
zum laufen bekommen. Daher muss ich jeden Pixel einzeln zeichnen,
irgendwie funktionieren die Grafikprimitive nicht. Mit grafischer
Ausgabe komm ich auf ca eine zehntel Sekunde pro Runde, und ca 20%
CPU-Last. Ohne waren die 50000 Runden bis die Füchse tot waren so
schnell rum, dass ich nur kurz weg war, 100000 Runden um waren als ich
wiederkam, nach ca 10 sek oder so. Der Taskmanager sagte 7.4% CPU-Last.)
Nachdem nach den ca 50000 Runden dann keine Füchse mehr da waren,
schwankte die Hasenzahl um die 800 herum.
Zur Zeit ist das ganze noch recht primitiv, die Hasen fressen bis das
Feld leer ist, gucken sich dann im Radius X um welches Feld das höchste
Gras hat und gehen da hin. 50 Ticks nach dem letzten angefressen worden
wächst das Gras um jeweils 1 nach, bis es die maximale Länge (z Zt 10)
erreicht hat.Hat der Hase ne Zeit lang nichts gefressen, steigt der
Hunger, ist der Hunger zu groß, wird der Hase schwächer. Ist der Hase zu
schwach, ist der Hase tot. Ist der Hase älter als x Ticks, ist der Hase
tot. Wird der Hase gefressen, ist der Hase tot. Hat der Hase keinen
Hunger und ist stark genug, schaut er sich im Radius Y nach ner
nicht-trächtigen Häsin um, und macht sie trächtig. Die gute alte
Steinzeit mit der Keule-Methode :D.
Die Paarung bei den Füchsen läuft nach dem selben Schema ab, auch das
Fressen ist ähnlich, nur dass sie sich nach nem Hasen umsehen, und
diesen dann mit der Chance NochnX erledigen.
Bei der aktuellen Spielfeldgröße von 60 mal 80 Feldern haben sich
20Prozent als ganz gut erhausgestellt. Damit kommt es zu den sich
gegenseitig bedingenden Minima und Maxima, die mich auf die Idee
brachten, das ganze zu programmieren.
Auch die "Grafik" ist aufgrund der Einschränkungen mit den Pixeln noch
maximal primitiv. Ein Feld ist ein Rechteck, in Abhängikeit von der
Grashöhe unterschiedlich stark grün ist. Ein Hase ist ein stärkeabhängig
blaues Rechteck im oberen linken Quadranten vom Feld auf dem Spielfeld,
ein Fuchs ein stärkeabhängig blaues Rechteck, um unteren rechten
Quadranten. Man hätte aufgrund der Quadranten noch die anderen beiden
Farbkanäle für weitere Information, aber Farbenunterscheiden ist keine,
zumindest meiner, Stärken.
Gerne würde ich nun eine Art Genetik mit in die Paarungsgeschichten
eingehen lassen, auch weitere Ideen was man passieren lassen könnte,
sind herzlich willkommen.
Über Hinweise auf eine einfach zu installierende/nutzende Grafiklibrary
würde ich mich auch freuen.
Und hauptsächlich wollte ich nun, nachdem ich das Programm erstmal rund
laufend habe, wie empfohlen wurde, und nach einigen Stolperfallen aus
denen ich aber noch selbst herausfand, gerne wissen, wie das nun mit dem
malloc/realloc funktioniert, bzw warum es bei mir nicht funktionierte.
Experte schrieb:> Du hast das Problem nicht gelöst, sondern nur "verschoben". Also:>> Wenn Du 5 Hasen mit malloc() besorgt hast, hast Du 5 Speicherblöcke und> zusätzlich 5 unterschiedliche Zeiger auf diese Speicherblöcke. Diese> Zeiger musst Du irgendwo speichern. Am besten in einer Tabelle.
Ich weiß inzwischen, bei malloc soll man keine Annahmen machen. Ich mach
aber mal folgende: Es hat sich gezeigt, dass das Spielfeld nicht mehr
als einen Hasen pro Feld versorgen kann, wenn keine Füchse da sind. Um
auf Nummer Sicher zu gehen, rechnet man mit 2 Hasen pro Feld.
1
typedefstruct
2
{
3
unsignedbli;
4
unsignedbla;
5
unsignedblub;
6
}sHase;
7
8
sHaseHasenliste[2xFeldXxFeldY]
Man macht ein (statisches) Array von Pointern auf Hasen. Ein Pointer auf
ein struct ist doch immer 8byte groß, egal wie groß das struct ist?
Das wäre also ein statischer Speicherverbrauch von 8 x 2 x FeldX x FeldY
byte? Dazu käme dann dynamisch: Anzahl lebende Hasen x Größe vom
Hasenstruct.
Die Liste brauch ich in meiner statischen Version auch, aber dazu kommt
noch noch 8 x 2 x FeldX x FeldY x Größe vom Hasenstruct.
Ob sich das lohnt? Ich weiß es nicht.
Zumindest verstehe ich nicht, warum es mit malloc/realloc nicht
funktioniert hat. Bekomme aber irgendwie auch nicht so recht den Punkt
gefasst, wo es mit dem Verständnis hapert. Darum ist es wohl irgendwie
auch ziemlich viel Text geworden, ich hab irgendwie mal die Gedanken
fließen lassen, ich hoffe die Sprünge sind nicht allzu
unnachvollziehbar. Es soll ja noch Leute geben, die gerne lesen ;-)
MfG
Chaos
> von J. T. (chaoskind)
Kleiner Tipp zur Forumsformatierung: Nimm mal den C-Code Tag statt des
reinen Code-Tags, dann wird der Code auch richtig dargestellt.
J. T. schrieb:> Als erstes erzeuge ich ein struct Feld...
Tja, deine Herangehensweise hat sich nicht geändert. Zuerst etwas machen
und dann erst sich Gedanken um die Folgen machen. Ich würde dir eher
vorschlagen, zuerst dein Spielfeld zu konstruieren. Das hat einzelne
Felder, wo Gras wachsen kann, entweder grad abgefressen oder mehr oder
weniger nachgewachsen. Es mag auch sein, daß in so einem Feld ein Hase
sich befindet, lebendig oder tot, Hase oder Häsin, bei Häsin entweder
schwanger oder nicht und wenn, wie lange schon. Obendrein kann sich in
einem Feld ein Fuchs befinden, hungrig oder nicht.
Das sind alles mögliche Eigenschaften eines Feldes und sie benötigen
kein Reservieren oder Zurückgeben von Platz auf dem Heap. Das Einzige,
was du allerdings nur einmal zu machen brauchst, ist die Konstruktion
des Spielfeldes. Dessen Felder sollen ja wohl nicht zu klein, aber auch
nicht zu groß sein, sondern nur so groß, daß man darauf erkennen kann,
was für einen Zustand das Feld hat: abgefressen oder nachgewachsen,
Hase/Fuchs drauf usw.
Wenn du schon ein Spiel basteln willst, dann kläre zu allererst die
generellen Dinge ab. Die Realisierung der Details kommt viel später.
W.S.
W.S. schrieb:> Als erstes erzeuge ich ein struct Feld...
Das waren doch nur Gedankenstümpfe wieder im Zusammenhang mit malloc.
W.S. schrieb:> Das hat einzelne Felder, wo Gras wachsen kann, entweder grad abgefressen> oder mehr oder weniger nachgewachse...
Genauso mache ich es doch. Schau dir den angehängten Quelltext an.
J. T. schrieb:> Zumindest verstehe ich nicht, warum es mit malloc/realloc nicht> funktioniert hat. Bekomme aber irgendwie auch nicht so recht den Punkt> gefasst, wo es mit dem Verständnis hapert.
Vergiss realloc. Du verbeisst Dich da, wie schon gesagt:
A. S. schrieb:> (Und ja, es gibt sogar einen Befehl, mit dem Du genau das machen> könntest. Aber auch der führt ins Chaos im jetzigen Projekt, also> sprechen wir nicht drüber)
Dein Problem mit Malloc: Malloc ist dafür da, den konkreten Speicher für
einen (oder n) Hasen in einem Feld zu erzeugen. Das macht nur Sinn, wenn
Du z.B. in den Feldern n Pointer für Hasen vorhälst, z.B. 5 oder 10. Die
sind dann 0 (kein Hase) oder zeigen zu einem mit malloc reservierten und
später mit free gekillten Hasen.
Also nochmal: Malloc ist dafür da, einzelne Hasen zu erzeugen, die nicht
in einer globalen Liste stehen! Sondern je per ptr referenziert werden.
Generell erzeugt man mit Malloc eigentlich einzelne Instanzen, die über
"in der Instanz bereitgehaltene" Ptr verkettet sind. Sogenannte linked
lists oder verkettete Listen. Also der "Next"-Ptr in der Instanz zeigt
auf den nächsten. Man hat dann nur einen Ptr "ErsteInstanz", die 0 ist
solange keine existiert. Und wenn die erste Erzeut wird, wird deren
Adresse ErsteInstanz zugewiesen. Und wenn die zweite erzeugt wird, dann
ErsteInstanz->Next. Und dann ErsteInstanz->Next->Next usw. Aber
natürlich iterativ.
Natürlich kannst Du auch mit malloc und realloc eine Gesamtliste
pflegen, doch dann kannst Du besser (wie empfohlen und umgesetzt)
statisch agieren.
Und natürlich kannst Du eine Gesamtliste der erzeugten Hasen pflegen,
(z.B. wenn Dein Spielfeld größtenteils leer ist), aber dann z.B. mit
einer doppelt verketten List.
Das Problem ist, dass a) nicht klar wird, was Du schon kennst und Du b)
nicht bereit bist, die Basics (Ptr-Handling, Malloc) an einfachen
Beispielen zu erforschen.
W.S. schrieb:> Das sind alles mögliche Eigenschaften eines Feldes und sie benötigen> kein Reservieren oder Zurückgeben von Platz auf dem Heap. Das Einzige,> was du allerdings nur einmal zu machen brauchst, ist die Konstruktion> des Spielfeldes.
Naja, es gibt halt (mindestens) zwei Blickwinkel auf das Spiel:
a.) In jeder Parzelle ist Platz für einen möglichen Hasen. Dann müssen
alle Hasen auf dem Spielfeld bei Bedarf zusammengesucht werden.
b.) Es gibt eine Liste mit Hasen, und bei jedem Hase in dieser Liste
steht, auf welcher Position (Parzelle) des Spielfelds er gerade
steht.
Es kommt halt auf das ursprüngliche Problem drauf an, welche
Organisation am Besten ist. Die Zwischenform(en), dass in jeder Parzelle
zwischen 0 und N Hasen gespeichert werden können, gibt's ja auch noch...
Ein Klassiker: Welche Datenstruktur wähle ich für das Problem.
Die passende Wahl einer Datenstruktur für ein Problem, ist meistens über
50% der Lösung.
Ein paar Tips mit denen du die Lesbarkeit deines Codes stark verbessern
könntests
1. mehr Funktionen - auch für sowas triviales wie
rand()%10
rand()%2
und ( (unsigned)(rand()%10 - 5) )
du verwendet sie mehrfach mit der gleichen Bedeutung - steckt die in
Einzeiler-Funktionen mit sinnvollem Namen - z.B. Geburtenrate oder
Grashoehe usw.
2. lass das mit den while-Schleifen und diesem ++x,y=0 irgendwo
Kilometer weiter drunter - das ist total unleserlich - nutze
for-Schleifen - Bitte!!!
Deins:
if(HList[i].Hunger==0)//Wenn kein Hunger mehr, Stärke erhöhen
74
{
75
HList[i].Staerke++;
76
if(HList[i].Staerke>=10)
77
{
78
HList[i].Staerke=10;
79
}
80
}
81
}
Besser - viel geringere visuelle komplexität:
1
...
2
sFeld*aktFeld=&SF[x][y];
3
sHase*aktHase=&HList[i];
4
5
if(aktFeld->GH>0)//Wenn Gras da ist, fressen,
6
{
7
aktFeld->GH--;
8
aktFeld->ZSA=0;
9
if(aktHase->Hunger>0)
10
{
11
aktHase->Hunger--;
12
}
13
aktHase->ZoF=0;
14
15
if(aktHase->Hunger==0)//Wenn kein Hunger mehr, Stärke erhöhen
16
{
17
aktHase->Staerke++;
18
if(aktHase->Staerke>10)
19
{
20
aktHase->Staerke=10;
21
}
22
}
23
}
das würde schon viel ausmachen
5. einen Header machen in dem deine Konstante und ALLE Funktionen drin
sind
ist nur dann sinnvoll wenn du eine Library daraus machst und auch alles
öffentlich Verwendung findet - ansonsten
so viel Implementierungsdetails in der C-Datei verstecken wie möglich -
alles andere wirkt so als glaube man das wäre professionell
diese #define im Header macht nur ganz ganz ganz alte Hasen - aber ich
verstehe warum das die Einsteiger so gerne kopieren - die Bücher und
Tutorial sind voll mit solchen Beispielen
die Kritik der anderen verstehe ich - aber das wird schon über die Zeit
besser
cppbert schrieb:> ansonsten> so viel Implementierungsdetails in der C-Datei verstecken wie möglich -> alles andere wirkt so als glaube man das wäre professionell
Die 5 Punkte kann ich alle unterschreiben! (Den 4. durch Deduktion)
cppbert schrieb:> mehr Funktionen - auch für sowas triviales wie
Das hab ich mir auch schon überlegt und für die nächste Session
vorgenommen, wenn ich mal wieder ein-zwei zusammenhängende Stunden
finde.
cppbert schrieb:> lass das mit den while-Schleifen und diesem ++x,y=0 irgendwo Kilometer> weiter drunter - das ist total unleserlich - nutze for-Schleifen -> Bitte!!!
Das wird mir schwer fallen, das mach ich schon immer so, das steckt tief
drin. Und ich finde es ehrlichgesagt irgendwie logischer. Erst das
abhandeln, bei dem der Zähler auf Null ist, dann den Zähler erhöhen.
Eingeschlichen hat sich das ganze übrigens weil ich irgendwann mal das
Gefühl hatte, die for-schleife würde erst den Zähler erhöhen. und dann
den Krams abarbeiten. Das fand ich doof, es brannte sich ein, der Fehler
lag dann aber doch woanders.
In deiner Version zu 2 scheint sich aber ein Fehler eingeschlichen zu
haben? Du schreibst while und in die Klammer dann for-Syntax. Soll dass
so, oder hattest du vorher nur zu oft ans while denken müssen, weil du
es mir austreiben willst? :D
Dein 3tens gefällt, das werde ich versuchen mir anzugewöhnen. Wobei ich
nicht genau weiß, was du mit den Reindizierungen meinst.
cppbert schrieb:> diese #define im Header macht nur ganz ganz ganz alte Hasen -
Also meinen ersten Kontakte mit C waren irgendwann Mitte Ende der
neunziger. Aber dann gab es auch immer wieder laaange Strecken, in
denen ich nichts gemacht hab.
Irgendwie sehe ich mich auch nicht wirklich als den großen
Programmierer, der seinen Krams irgendwann veröffentlichen will, sondern
schreib das ursprünglich nur für mich. Denke daher nur wenig an
Konventionen, und mach viel nach "Irgendwie geht's schon".
Ist dann natürlich doof, wenn man mal Hilfe braucht. :D
Ich danke dir recht herzlich, für deinen durchdachten und
wohlformulierten Beitrag, der ganz ohne Gehässigkeiten und Beleidigungen
auskommt. Davon sollten sich einige Teilnehmer dieses Forums mal eine
Scheibe abschneiden
J. T. schrieb:> W.S. schrieb:>> Das hat einzelne Felder, wo Gras wachsen kann, entweder grad abgefressen>> oder mehr oder weniger nachgewachse...>> Genauso mache ich es doch. Schau dir den angehängten Quelltext an.
Und wozu dann die ganze Herumeierei mit Pointern, Heap, Allocation usw.
?
Nein, aus deinem Quelltext geht nicht hervor, wie du dein Vorhaben
eigentlich anpacken willst. Und dort lange herumzusuchen ist mir zu
mühsam.
W.S.
W.S. schrieb:> Und wozu dann die ganze Herumeierei mit Pointern, Heap, Allocation usw.> ?> Nein, aus deinem Quelltext geht nicht hervor, wie du dein Vorhaben> eigentlich anpacken willst. Und dort lange herumzusuchen ist mir zu> mühsam.
er hat in der Folge der Kommunikation ja gesagt das er auf malloc
erstmal verzichtet weil ihm geraten wurde das erstmal ohne zu
implementieren
W.S. schrieb:> Nein, aus deinem Quelltext geht nicht hervor, wie du dein Vorhaben> eigentlich anpacken willst.
Da das Programm genau tut, was ich mir gedacht hab, und es lustigerweise
auch so im Quelltext steht, würde dass schon daraus hervorgehen, wenn du
dich damit befassen würdest. Möchtest du nicht, ist ja auch ok, aber
dann halte dich doch bitte mit Behauptungen zurück.
cppbert schrieb:> er hat in der Folge der Kommunikation ja gesagt das er auf malloc> erstmal verzichtet weil ihm geraten wurde das erstmal ohne zu> implementieren
Danke
J. T. schrieb:> cppbert schrieb:>> er hat in der Folge der Kommunikation ja gesagt das er auf malloc>> erstmal verzichtet weil ihm geraten wurde das erstmal ohne zu>> implementieren>> Danke
Du musst dann aber auch dabei sagen, dass Du Dich bisher strikt
geweigert hast, Dich mit den Konzepten von malloc und Zeigern
auseinander zu setzen.
Du hattest (und hast?) Dein Bild von einer Liste, deren Größe Du mit
Malloc erweiterst bzw. mit Realloc vergrößerst.
Das ist so ungefähr so sinnvoll, wie eine for-Schleife durch while
nachzubauen, nur weil Syntax und Funktion von for zu komplex ist.
Zudem hast Du mehrfach Pointer und Datenfeld (Also Referenz und Inhalt)
verwechselt bzw. einfache Ptr-Operationen (wie ptr++ statt
ptr+=sizeof(*ptr) ) noch nicht verinnerlicht. Das wäre nicht schlimm,
wenn Du das dann nicht in ein komplexes System einflechten möchtest und
am Ende fragst: Warum geht das nur manchmal?
J. T. schrieb:> Ich versuche ein Game of Life zu schreiben, das "mehr Möglichkeiten" als> Conways bietet, und erhoffe mir dadurch mehr Komplexität und> Lebendigkeit reinzukriegen.
Nur als zusätzliche Info und vielleicht Hilfe für andere:
Du implementierst hier Wator (https://de.wikipedia.org/wiki/Wator) nur
mit Hasen / Füchsen anstatt Fische \ Haie.
Tobias .. schrieb:> Nur als zusätzliche Info und vielleicht Hilfe für andere:> Du implementierst hier Wator (https://de.wikipedia.org/wiki/Wator) nur> mit Hasen / Füchsen anstatt Fische \ Haie
Oh, danke für den Hinweis, das kannte ich noch nicht.
Es fallen mir aber auch gleich ein paar Unterschiede auf. Bei Wator ist
die Nahrungsquelle für die Herbivoren unbegrenzt, bei mir wächst auch
das Gras begrenzt nach. Und die haben es so implementiert, das auf einem
Feld nur ein Viech sein kann, das Feld sich also quasi merkt, ob es nen
Viech auf sich hat oder nicht. Bei mir ist es genau andersrum, das Viech
merkt sich auf welchem Feld es ist. Davon ab sind sich die beiden schon
sehr ähnlich.
Warum fallen mir eigentlich immer nur Dinge ein, die es schon gibt :D
J. T. schrieb:> Es fallen mir aber auch gleich ein paar Unterschiede auf.
Ich muss gestehen, ich hab den Thread hier nur schnell überflogen ob so
ein Hinweis schon gab.
Dein Eingangsposting hatte schon sehr Ähnlichkeiten damit, daher die
Verlinkung. ;)
Tobias .. schrieb:> Dein Eingangsposting hatte schon sehr Ähnlichkeiten damit, daher die> Verlinkung. ;)
Jo, sagte ich dann ja auch, die beiden sind sich schon sehr ähnlich, und
mein Einwand mit Erwähnung der Unterschiede war auch keinesfalls als
Abwehr oder so gedacht. Im Gegenteil, ich freue mich immer über solche
Hinweise, vor allem, wenn sie wie von dir einfach als Einwurf gegeben
werden. Und nicht ein implizites "du bescheuerter Idiot" mitschwingt.
Daher alles gut =)
J. T. schrieb:> Und die haben es so implementiert, das auf einem> Feld nur ein Viech sein kann, das Feld sich also quasi merkt, ob es nen> Viech auf sich hat oder nicht. Bei mir ist es genau andersrum, das Viech> merkt sich auf welchem Feld es ist.
Eben weil du mit dem Kopf durch die Wand willst, indem du nicht die
Karnickel als einfach zu managendes Feld der Gras-Parzellen deines
Spielfeldes betrachten willst, sondern umgekehrt.
So ein Hase kann sich vermehren oder auch gefressen werden, die
Gras-Parzelle kann schlimmstenfalls leergefressen sein und braucht Zeit
um nachzuwachsen, also ist die Parzelle etwas, das du statisch anlegen
kannst und nicht im Spielverlauf löschen oder neu kreieren mußt, bei den
Hasen und Füchsen hingegen ist das allzeit erforderlich.
W.S.