Hallo Liebe Forum-User,
ich bin wiedermal fleissig am programmieren und haenge gerade an einem
eventuell fuer euch banalen Teil fest.
Ich schreibe derzeit Ein Programm namens 'Robots' in dem es darum geht,
dass sich der Spieler mit den Tasten des Num-Blocks (1-9) durch ein
vorgegebenes Spielfeld bewegen kann.
Das Spielfeld wird durch die Methode 'init_world' mit Robotern (R) und
Wänden (X) an zufaelligen Plaetzen besetzt.
Derzeit bin ich dabei, meine Methode 'move_character' zu schreiben und
muss bei der Bewegung des Spielers noch beruecksichtigen, dass diese
Hindernisse eben auch im Weg stehen koennen.
Ich habe diese Abfrage mit einer Switch in einer Switch angefangen,
jedoch kommt mir es etwas unhandlich vor und ich wollte fragen, ob ich
auf dem richtigen Weg bin, oder das total redundanter Code ist.
Der Teil mit typedef struct hat mir eine Freundin gerate, ich weiss aber
persoenlich nicht so wirklich, wie ich das einbauen, verwenden sollte.
Freue mich auf Hilfe von euch.
Liebe Gruesse.
Hier mein derzeit unvollstaendiger Code:
Viel zu kompliziert.
Dein Robot steht an einer Position x bzw. y.
Aus der Eingabe ergibt sich, wohin der Robot gehen möchte. Ist die
Eingabe '6' dann will er nach rechts gehen und demenstprechend würde er
gerne die x Koordinate um 1 erhöhen. Selbiges mit den anderen Eingaben
und versuchten Koordinatenänderungen in x bzw. y
Aber was auch immer die Eingabe war. Heraus kommt eine Koordinate auf
die der RObot gehen möchte. Und in allen Fällen stellt sich dann die
Frage: kann er das denn? Ist das Feld auf das er gehen möchte frei oder
nicht.
D.h. du musst dich nicht getrennt bei jeder Bewegungsrichtung fragen, ob
das geht oder nicht. Du bahndelst erst mal die Eingabe um daraus die
Bewegungsrichtung zu erkennen. Mit der aktuellen Position und der
Bewegungsrichtung ergibt sich eine neue Position auf der der Robot zwar
noch nicht ist, auf die er aber hin möchte. Und egal welche Position das
ist, man kann diese Position testen, ob ein Robot dort hin gehen könnte.
Und zwar ganz unabhängig davon, mit welcher Bewegungsrichtung er das
tat.
Ich hab die Initialisierung des Spielfeldes zuerst gemacht und belegt.
Das funkioniert alles.
Nein, die Methode move_character funktioniert nicht, es ging mir erstmal
auch nur um die Art die Sachen zu prüfen.
> Aus der Eingabe ergibt sich, wohin der Robot gehen möchte. Ist die> Eingabe '6' dann will er nach rechts gehen und demenstprechend würde er> gerne die x Koordinate um 1 erhöhen. Selbiges mit den anderen Eingaben> und versuchten Koordinatenänderungen in x bzw. y
in der Art?
1
//horizontal
2
case'8':(*y)--;break;
3
case'4':(*x)--;break;
4
case'2':(*y)++;break;
5
case'6':(*x)++;break;
6
//diagonal
7
case'7':(*y)--;(*x)--;break;
8
case'9':(*x)++;(*y)--;break;
9
case'1':(*y)++;(*x)--;break;
10
case'3':(*x)++;(*y)++;break;
11
//nicht bewegen
12
case'5':(*y);(*x);break;
> Aber was auch immer die Eingabe war. Heraus kommt eine Koordinate auf> die der RObot gehen möchte. Und in allen Fällen stellt sich dann die> Frage: kann er das denn? Ist das Feld auf das er gehen möchte frei oder> nicht.> D.h. du musst dich nicht getrennt bei jeder Bewegungsrichtung fragen, ob> das geht oder nicht. Du bahndelst erst mal die Eingabe um daraus die> Bewegungsrichtung zu erkennen. Mit der aktuellen Position und der> Bewegungsrichtung ergibt sich eine neue Position auf der der Robot zwar> noch nicht ist, auf die er aber hin möchte. Und egal welche Position das> ist, man kann diese Position testen, ob ein Robot dort hin gehen könnte.> Und zwar ganz unabhängig davon, mit welcher Bewegungsrichtung er das> tat.
Aber mache ich das nicht eigentlich?
> Der Teil mit typedef struct hat mir eine Freundin gerate, ich weiss aber
persoenlich nicht so wirklich, wie ich das einbauen, verwenden sollte.
Der springende Punkt ist, dass du zb hier
eine Kombination aus x und y hast. Du siehst das momentan als 2
Zahlenwerte an. Aber die beiden haben einen Zusammenhang. Das eine
ergibt ohne das andere keinen Sinn. Die beiden gehören zusammen und
bilden das, was man eine Koordinate nennt.
Das ist so, wie ein Datum (Geburtstag, Hochzeitstag, ...) auch immer aus
3 Angaben besteht: Tag, Monat, Jahr. Jede der Komponenten ist für sich
einfach nur eine Zahl, erst zusammen ergeben sie das, was man ein Datum
nennt.
Ein Adresse besteht auch aus Komponenten. Da gibt es den Strassennamen,
die Hausnummer, die Postleitzahl und den Namen der Stadt. Jede
Information ist für sich alleine gesehen sicherlich richtig und
wertvoll. Aber zu einer Adresse wird das ganze nur, wenn man alles
mitsammen betrachtet.
und genau an dieser Stelle kommt das SPrachmittel 'Struktur' ins Spiel.
Eine Struktur erlaubt dir, diesen logischen Zusammenhang auszudrücken.
Da gibt es dann eben ein
1
structDatum
2
{
3
intTag;
4
intMonat;
5
intJahr;
6
};
oder ein
1
structAdresse
2
{
3
charStadt[40];
4
intPostleitzahl;
5
charStrasse[40];
6
intHausnummer;
7
};
oder eben bei dir
1
structKoordinate
2
{
3
intx;
4
inty;
5
};
und enstprechende Variablen von diesem jeweiligen Typ
1
structDatumHochzeitstag;
2
3
Hochzeitstag.Tag=1;
4
Hochzeitstag.Monat=4;
5
Hochzeitstag.Jahr=2014;
sind dann eben nicht einfach nur Zahlen, sondern sind ein Container, in
dem alle zu diesem Strukturtyp gehörenden Informationen beisammen sind.
In der Variablen 'Hochzeitstag' stecken die 3 Einzelinformationen
drinnen und wenn ich die Variable 'Hochzeitstag' benutze, dann habe ich
automatisch alle 3 Komponenten immer mit dabei.
An eine Funktion, die eine Adrdesse ausgibt, wird dann eben nicht mehr
einfach nur ein Strassenname und eine Hasunummer übergeben, sondern eine
komplette Adrdesse
enthält damit dann auch eine komplette Adresse (was immer auch in dieser
Adresse dann enthalten ist. Aber das wiederrum steht ja bei der
Vereinbarung wie ein struct Adresse Objekt auszusehen hat). Und diese
Adresse kann ich daher wiederrum an die oben vorgestellte Funktion
übergeben, die enben weiss, wie eine Adresse auszugeben ist
wäre es ja wohl so, dass die Funktion für eine gegebene Feldbelegung
eine freie Position bestimmen soll, die anhand ihrer KOORDINATE
angesprochen wird.
Du bist also an einer Koordinate interessiert. Das diese Koordinate aus
einem x Wert und einem y Wert besteht, ist ja ganz nett. Trotzdem ist
der Überbegriff, der x und y zu einer Einheit verschweisst, die
Koordinate. Und genau dazu hast du dir ja auch eine entsprechende
Struktur gemacht, die genau das ausdrückt, nämlich dass das Pärchen aus
x Wert und y Wert eine funktionale Einheit bildet. Folgerichtig wäre es
nur logisch, das auch in der Funktion zum Ausdruck zu bringen (der
typedef bewirkt nur, dass du nicht immer und ewig überal 'struct' mit
dazu schreiben musst
Steffen M. schrieb:> Ich hab die Initialisierung des Spielfeldes zuerst gemacht und belegt.> Das funkioniert alles.>> Nein, die Methode move_character funktioniert nicht, es ging mir erstmal> auch nur um die Art die Sachen zu prüfen.>>> Aus der Eingabe ergibt sich, wohin der Robot gehen möchte. Ist die>> Eingabe '6' dann will er nach rechts gehen und demenstprechend würde er>> gerne die x Koordinate um 1 erhöhen. Selbiges mit den anderen Eingaben>> und versuchten Koordinatenänderungen in x bzw. y>> in der Art?
Nein nicht in der Art.
Du musst unterscheiden: Wo steht der Roboter zur Zeit. Der steht auf den
Koordinaten die durch die Variablen x und y gegeben sind.
Und wo will er hin. Das sind neue Koordinaten. Dazu brauchst du auch
neue Variablen.
Warum kannst du nicht die bereits vorhandenen variablen dafür hernehmen?
Weil du deren Werte noch brauchst, wenn du dann den Roboter tatsächlich
den Zug machen lässt.
Dazu musst du wissen: wo war er vorher (weil du ja dort das Feld als
leer markieren musst) und wo ist er nach dem Zug (weil du ja dort das
Feld als belegt markieren musst.
feld[*y][*x]='.';// dort ist der Roboter nicht mehr
18
feld[y_ziel][x_ziel]='@';// dafür ist er jetzt dort
19
}
20
elseif(feld[y_ziel][x_ziel]=='R'){
21
feld[*y][*x]='.';
22
feld[y_ziel][x_ziel]='X';
23
}
der letzte Teil ist völlig unabhängig vom ersten Teil. in x_ziel bzw
y_ziel steht drinnen, wo der Robot hin will. Und je nachdem was in
feld[x_ziel][y_ziel] vorgefunden wird, wird entsprechend agiert. Das ist
aber unabhängig davon, wie genau die Werte in x_ziel bzw. y_ziel
entstanden sind.
>> D.h. du musst dich nicht getrennt bei jeder Bewegungsrichtung fragen, ob>> das geht oder nicht. Du bahndelst erst mal die Eingabe um daraus die>> Bewegungsrichtung zu erkennen. Mit der aktuellen Position und der>> Bewegungsrichtung ergibt sich eine neue Position auf der der Robot zwar>> noch nicht ist, auf die er aber hin möchte. Und egal welche Position das>> ist, man kann diese Position testen, ob ein Robot dort hin gehen könnte.>> Und zwar ganz unabhängig davon, mit welcher Bewegungsrichtung er das>> tat.>> Aber mache ich das nicht eigentlich?
Nicht im Originalcode
Karl Heinz schrieb:> struct Adresse> {> char Stadt[40];> int Postleitzahl; // <--> char Strasse[40];> int Hausnummer; // <--> };
böse Falle! :-)
Steffen M. schrieb:> for(j = 0; j < XRANGE; j++){> feld[0][j] = '#';> feld[YRANGE][j] = '#';}
das feld[YRANGE][j] sieht nach einem "off-by-one" aus
Steffen M. schrieb:> //innen> for(i = 1; i < YRANGE; i++){
genauso hier. hier wird der Rahmen, der vorher versehentlich nicht
gezeichnet wurde nochmal überschrieben.
Steffen M. schrieb:> void compute_random_free_cell(char feld[YRANGE][XRANGE], int *x, int> *y){>> char compare;> int xran;> int yran;> time_t t;>> srand((unsigned) time(&t));>> xran = rand() % XRANGE;> yran = rand() % YRANGE;>> *x = xran;> *y = yran;>> compare = feld[*y][*x];;>> while(compare != '.'){>> xran = rand() % XRANGE;> yran = rand() % YRANGE;>> *x = xran;> *y = yran;>> compare = feld[*y][*x];}> }
srand sollte üblicherweise genau einmal pro programm aufgerufen werden
(bzw dann, wenn man wirklich den Zufallsgenerator neu initialisieren
will)
Außerdem ist deine time-variable uninitialisiert. Für die
Initialisierung eines Zufallsgenerators mag das aber ok sein ;)
und außerdem ist da ne menge code doppelt.
Besser wäre hier eine do-while schleife
Mit dem Code kann ich aber die Diagonalen Spielerläufe nicht abdecken
oder?
1
elseif(Eingabe=='5')
2
y_ziel,x_ziel;
Sowas funktioniert ja nicht.
1
>structAdresse
2
>{
3
>charStadt[40];
4
>intPostleitzahl;// <--
5
>charStrasse[40];
6
>intHausnummer;// <--
7
>};
Was willst du mir damit sagen? Ich glaube ich stehe auf dem Schlauch.
Das mit dem Rahmen, kann sein, dass es irgendwie nicht richtig passt.
War nur froh, dass es am Ende mein Spielfeld ergeben hat und hab es seit
demher nicht angefasst ^^
ich habe gerade noch probiert, den Spieler auch an eine zufaellige
Koordinate zu setzen, jedoch kommt jedes mal beim start:
x = 19 und y = 1
Als Parameter uebergeben. wieso?
Steffen M. schrieb:> Mit dem Code kann ich aber die Diagonalen Spielerläufe nicht abdecken> oder?
ein bisschen mitdenken sollte ein Student schon können -_-
Vlad Tepesch schrieb:> Außerdem ist deine time-variable uninitialisiert. Für die> Initialisierung eines Zufallsgenerators mag das aber ok sein ;)
das ist quatsch. das übergebene ist ja ein output parameter, hatte ich
falsch im Gedächtnis.
Da du aber den Generator immer mit der aktuellen Zeit neu startest,
sollten die meisten Aufrufe innerhalb eines Laufes aber die selben
Zufallswerte erzeugen. eben so lange, bis die nächste Sekunde dran ist.
Warum du über mehrere Läufe immer den selben Wert bekommst, kann ich mir
gerade noch nicht erklären.
Eventuell ist dein aktueller Code auch schon zu weit von dem
ursprünglichen weg.
Steffen M. schrieb:>> if( Eingabe == '6' )>> x_ziel++;>> else if( Eingabe == '4' )>> x_ziel--;>> else if( Eingabe == '2' )>> y_ziel++;>> else if( Eingabe == '8' )>> y_ziel--;>> Mit dem Code kann ich aber die Diagonalen Spielerläufe nicht abdecken> oder?
Dann schreibst du dafür halt vier weitere Vergleiche für 1, 3, 7, und 9.
Man kann sowas (Abbildung einer Menge von Eingangswerten auf eine Menge
von Ausgangswerten, ohne dass es eine geschlossene Formel für die
Abbildung gibt), auch mit einer Lookup-Tabelle machen. Das sieht dann
etwas eleganter aus, aber da du momentan noch Schwierigkeiten mit
einigen grundlegenden Sprachkonstrukten hast würde ich erst mal bei der
einfachen Version bleiben.
Oh, und noch was, gerade als Anfänger sollte man sich nicht von Code wie
>> if( Eingabe == '6' )>> x_ziel++;>> else if( Eingabe == '4' )>> x_ziel--;
verführen lassen, sondern ganz brav und methodisch immer Klammern
setzen:
> ein bisschen mitdenken sollte ein Student schon können -_-
Ich hatte einen Formatfehler, deswegen hatte es nicht geklappt. Deswegen
habe ich mich eben gewundert, man haette damit das ja nicht abdecken
koennen. Habs nochmal neu formatiert:
1
if(Eingabe=='6'){
2
x_ziel++;}
3
elseif(Eingabe=='4'){
4
x_ziel--;}
5
elseif(Eingabe=='2'){
6
y_ziel++;}
7
elseif(Eingabe=='8'){
8
y_ziel--;}
9
elseif(Eingabe=='7'){
10
y_ziel--;
11
x_ziel--;}
12
elseif(Eingabe=='9'){
13
y_ziel--;
14
x_ziel++;}
15
elseif(Eingabe=='3'){
16
y_ziel++;
17
x_ziel++;}
18
elseif(Eingabe=='1'){
19
y_ziel++;
20
x_ziel--;}
21
elseif(Eingabe=='5'){
22
y_ziel;
23
x_ziel;}
Zwecks der Random Belegung der Spielerposition hat sich im Code zum
urspruenglichen kaum was geaendert.
um das jetzt in die Main einzubinden, brauche ich meinen char Eingabe
die ueber scanf ueber die stdin eingelesen wird oder?
Packe ich das dann alles in eine while(1) Schleife?
Steffen M. schrieb:> Ich hatte einen Formatfehler
den hast du immer noch - der Code sieht nämlich Furchtbar aus
1
if(Eingabe=='6'){
2
x_ziel++;
3
}elseif(Eingabe=='4'){
4
x_ziel--;
5
}elseif(Eingabe=='2'){
6
y_ziel++;
7
}//...
die schließende Klammer unter die Anweisung, die den Block öffnet.
Bei längeren Blöcken (zb Funktionen) finde ich es lesbarer, wenn man
auch die öffnende auf eine eigene Zeile stellt, so dass zusammengehörige
Klammern untereinander stehen.
gleichwertige Ausdrücke/Kontrollstrukturen gleicht tief eingerückt und
logisch untergeordnetere Blöcke tiefer einrücken.
zusammengehörende if / else / else if sind gleichrangig und gehören an
eine Linie. die bedingten Anweisungen eine ebene Tiefer
Der Code ist vom Prinzip her ein Baum und genau so kann man ihn auch
formatieren.
> Autor: R. Rebentrost (Gast)> Datum: 17.06.2015 19:53
endlich mal einer der switch ... case nutzt, das ist nicht nur
übersichtlicher, sondern (insbesondere bei noch längeren else if
Konstrukten) wahrscheinlich auch in der Ausführung um ein paar
Millisekunden schneller. Müßte man mal testen.
Steffen M. schrieb:> Schermi schrieb:>> Hi, nur aus Interesse:>> Studierst du zufällig in Ingolstadt? ;-)>> Ja, das tue ich ^^
Ah. So wie Viktor Frankenstein!
Steffen M. schrieb:> Ich schreibe derzeit Ein Programm namens 'Robots'
Dein gesamtes Programm lässt sich wesentlich kürzer fassen, wenn man für
vordefiniertes Tabellen verwendet und das Feld als eine lange
Zeichenkette auffasst, die ausgegeben werden kann.
1
#define XRANGE 22
2
#define LINELEN (XRANGE+1)
3
#define YRANGE 7
4
#define WHOLE (YRANGE*LINELEN)
5
charfeld[WHOLE+1];
6
intspieler;
7
8
intmove_character(intrichtung)// 1..9 für Richtung, 5 und 0 bleibt stehen
9
{
10
staticintbewegung[10]=
11
{0,
12
LINELEN-1,LINELEN,LINELEN+1,
13
-1,0,+1,
14
-LINELEN-1,-LINELEN,-LINELEN+1
15
};
16
intneu=spieler+bewegung[richtung];
17
if(feld[neu]=='R')return0;// verloren
18
if(feld[neu]=='.')
19
{
20
feld[spieler]='.';
21
spieler=neu;
22
feld[spieler]='@';
23
}
24
return1;
25
}
26
27
voidshow_world(void)
28
{
29
puts(feld);
30
}
31
32
intcompute_random_free_cell(void)
33
{
34
intpos;
35
36
do
37
{
38
pos=rand(WHOLE);
39
}
40
while(feld[pos]!='.');
41
returnpos;
42
}
43
44
voidinit_world(intlevel)
45
{
46
staticstruct
47
[
48
introbots,walls,hyper_space;
49
}
50
game[6]=
51
{
52
{2,0,1},
53
{4,0,2},
54
{8,0,3},
55
{4,20,1},
56
{8,40,2},
57
{16,80,3},
58
}
59
for(i=0;i<YRANGE;i++)// # RAHMEN und Zeilenumbruch
60
{
61
char*zeile=feld+i*LINELEN;
62
memset(zeile,'#',XRANGE);
63
if(0<i&&i<YRANGE-1)memset(zeile+1,'.',XRANGE-2);
64
zeile[XRANGE]='\n';
65
}
66
feld[WHOLE]='\0';
67
feld[spieler]='@';
68
for(i=0;i<game[level].robots;i++)
69
{
70
feld[compute_random_free_cell()]='R';
71
}
72
for(i=0;i<game[level].walls;i++)
73
{
74
feld[compute_random_free_cell()]='X';
75
}
76
for(i=0;i<game[level].hyper_space;i++)
77
{
78
feld[compute_random_free_cell()]='@';
79
}
80
}
81
intmain()
82
{
83
time_tt;
84
srand((unsigned)time(&t));
85
printf("-------- R O B O T S -----------");
86
system("cls");// Bildschirm löschen direkt nach dem man ROBOTS geschrieben hat ? Unsinn. system-Aufrufe dafür ebenfalls.
87
printf("Waehlen Sie ein Level [1-6]:");
88
scanf("%d",&level);
89
spieler=LINELEN*6+6;// Startposition 6,6
90
init_world(level-1);
91
show_world();
92
fflush(stdin);
93
while(move_character(eingabe))
94
{
95
}
96
return0;
97
}
Allerdings bist du weit weg von einem funktionierendem Spiel. Du hast
gar nicht überlegt, ob das Spiel lösbar ist. Was willst du da machen:
#######
#.....#
#RXXX.#
#.X@X.#
#######
Was ist überhaupt das Ziel, wenn das Erreichen des R nur verloren
bedeutet ? Also: Spielstrategie nicht überlegt.
MaWin schrieb:> Allerdings bist du weit weg von einem funktionierendem Spiel. Du hast> gar nicht überlegt, ob das Spiel lösbar ist. Was willst du da machen:>> #######> #.....#> #RXXX.#> #.X@X.#> #######>> Was ist überhaupt das Ziel, wenn das Erreichen des R nur verloren> bedeutet ? Also: Spielstrategie nicht überlegt.
Das muss man vermutlich seinen Professor fragen. Das sieht alles sehr
nach Übungsaufgabe aus.
Mir war es nur wichtig, dass ich erstmal meine Figur in meinem Spielfeld
bewegen kann, was leider immer noch nicht funktioniert.
Ziel des Spiels ist es, entweder alle Roboter mit (move_robots)
ineinander laufen zu lassen, so das keine Roboter auf dem Feld sind ->
Spieler gewinnt.
Die Roboter die Position des Spielers erreichen und ihn somit zerstoeren
-> Roboter gewinnen.
Laut Aufgabe brauche ich keine move_character Methode, ich koennte die
Befehlserkennung und Ausführung fuer die Bewegung auch in die Main
schreiben.
R. Rebentrost schrieb:> Ich habe den Thread nicht im Detail verfolgt, aber so wäre es evtl.> übersichtlicher, wenn du (noch) keine Tabelle verwenden willst:
Naja, der TE hatte eigentlich switch/case verwendet, aber Karl Heinz hat
das aus irgendeinem Grund durch eine if/else-Kaskade ersetzt.
Vlad Tepesch schrieb:> Bei längeren Blöcken (zb Funktionen) finde ich es lesbarer, wenn man> auch die öffnende auf eine eigene Zeile stellt, so dass zusammengehörige> Klammern untereinander stehen.
Ich finde es grundsätzlich lesbarer, unabhängig von der Blocklänge. Das
ist sicherlich Ansichtssache, aber wenn die öffnende Klammer nicht auf
der selben Spalte steht wie die schließende und nicht genauso
"alleinstehend" ist wie diese, sieht der Code für mich immer übelst
zerpflückt aus.
> zusammengehörende if / else / else if sind gleichrangig und gehören an> eine Linie. die bedingten Anweisungen eine ebene Tiefer
Wobei "else if" ja in C eigentlich kein eigener Konstrukt ist, sondern
nur ein normales else gefolgt von einem normalen if. Bei den
Einrückungs- und Klammer-Regeln wird es aber meist separat betrachtet,
da eine Kaskade aus if/else dann übersichtlicher wird. Man will ja
schließlich nicht sowas:
1
if(a)
2
{
3
// Code
4
}
5
else
6
{
7
if(b)
8
{
9
// Code
10
}
11
else
12
{
13
if(c)
14
{
15
// Code
16
}
17
}
18
}
Deshalb weicht man an der Stelle von dieser Regel ab:
> Der Code ist vom Prinzip her ein Baum und genau so kann man ihn auch> formatieren.
und schreibt stattdessen:
1
if(a)
2
{
3
// Code
4
}
5
elseif(b)
6
{
7
// Code
8
}
9
elseif(c)
10
{
11
// Code
12
}
Am Rande noch...
Das hier ist in C verboten:
Steffen M. schrieb:> fflush(stdin);
Ich habe so was ähnliches auch schon mal gemacht (mind. 30 Jahre her),
allerdings die Spielfelder nicht zufällig generiert sondern selbst
entworfen und nur 4 Richtungen (ohne Diagonal).
Ich habe damals jedem Feld (Koordinate) mitgegeben wo es Wände hat und
ob da noch was besonderes ist oder nicht (z.B. Fallen). Jede mögliche
Wand war 1 Bit das gesetzt war (Wand da) oder nicht (Wand weg). Damit
gingen dann auch Einbahnstraßen und so. Das hat den Vorteil das man die
Spielfigur und die anderen Dinge (Bei Dir Roboter) nie in eine Wand
setzen kann da alle Felder ja begehbar sind Rundherum Wände und eine
Einbahnstraße da rein ist übrigens eine tolle Falle (Game over).
Bei der Bewegung (also Spieler drückt links) habe ich dann einfach im
aktuellen Feld geprüft ob der Spieler da überhaupt links kann (Bit für
linke Wand = 0). Wenn ja, x=x-1, Feld auf Gegner/Überaschungen prüfen
(z.B. auch Teleport an eine andere Stelle) und wieder warten was der
Spieler macht.
Bei den Diagonalen wären das dann pro feld 8 Bit, die Roboter und
sonstigen Dinge müssten dann in ein weiteres Array.
z.B. für die Wände
Bit 0 = oben
Bit 1 = rechts oben
Bit 2 = rechts
Bit 3 = rechts unten
Bit 4 = unten
Bit 5 = unten links
Bit 6 = links
Bit 7 = links oben
Übrigens, Es gab in grauer Vorzeit sogar mal ein Elektor Projekt das das
so in Hardware mit einem Labyrinth in einem EPROM gemacht hat. Der
Spieler hat da nur das aktuelle Feld mit den Wänden gesehen (4 LEDs) und
musste sich da durcharbeiten.
stirb auf einem anderen Planeten schrieb:> wahrscheinlich auch in der Ausführung um ein paar> Millisekunden schneller. Müßte man mal testen.
Ich weiss nicht in welcher Welt du lebst. Aber mit Millisekunden bist du
um Größenordnungen daneben. Hast du eine Ahnung, wass ein PC in 1
Millisekunde alles machen kann?
Steffen M. schrieb:> Mir war es nur wichtig, dass ich erstmal meine Figur in meinem Spielfeld> bewegen kann, was leider immer noch nicht funktioniert.
Dann zeig deinen jetzigen Code.
Im übrigen gibt es auf jedem Entwicklungssystem auf dem PC ein Werkzeug
namens Debugger. Damit kann man den Programmfluss verfolgen und sich
auch Variablen ansehen.
Besser du lernst frühzeitig damit umzugehen, denn du wirst noch viel
Zeit mit dem Debugger verbringen.
Und dann gibt es auch noch die gute alte Technik, sich einfach mit ein
paar printf im Code einen Überblick darüber zu verschaffen, was
passiert, welche Werte die interessierenden Variablen haben.
Wenn du programmieren lernen willst, dann musst du vor allen Dingen auch
lernen dir selbst zu helfen.
Steffen M. schrieb:> Laut Aufgabe brauche ich keine move_character Methode, ich koennte die> Befehlserkennung und Ausführung fuer die Bewegung auch in die Main> schreiben.
Könnte man.
Im Prinzip braucht man überhaupt keine Funktionen bis auf main().
Nur sind das dann die Programme, die nie zum funktionieren zu kriegen
sind, weil kein Mensch mehr den Überblick mehr behalten kann.
Das eine ist eine technische Angelegenheit. Das andere ist die Fähigkeit
des Programmierers, sein Programm noch zu überblicken und zu verstehen.
Zwischen den beiden Dingen klafft eine klitzekleine Lücke. Soll heissen:
die ist riesig! In der Programmierung ist Organisation das halbe Leben.
Und Organisation erreicht man durch Datenstrukturen und durch das Divide
and Conquer Prinzip [Teile und Herrsche], wobei mit Teilen das Aufteilen
einer komplexen Funktionalität in einfachere Teilfunktionalitäten
gemeint ist. Rate mal wie man das wiederrum erreicht?
Dein Code funktioniert nicht, da Du mit ++ den Wert der Variable schon
beim pruefen aenderst und danach nochmal. (Immer vorrausgesetzt Du rufst
die Funktion mit &x und &y auf; das ist derzeit noch Dein Geheimnis)
Steffen M. schrieb:> Ziel des Spiels ist es, entweder alle Roboter mit (move_robots)> ineinander laufen zu lassen, so das keine Roboter auf dem Feld sind ->> Spieler gewinnt.
Es gibt kein move_robots.
Steffen M. schrieb:> Mir war es nur wichtig, dass ich erstmal meine Figur in meinem Spielfeld> bewegen kann, was leider immer noch nicht funktioniert.
Natürlich nicht.
Du musst erst mal lernen, die Tastatur Einzelzeichenweise abzufragen.
fflush(stdin) geht nicht, das wurde schon erwähnt.
Vielleicht verwendest du getch() aus conio.h
1
intmain()
2
{
3
intch;
4
time_tt;
5
srand((unsigned)time(&t));
6
printf("-------- R O B O T S -----------");
7
printf("Waehlen Sie ein Level [1-6]:");
8
scanf("%d",&level);// kann derb in die Hose gehen wenn nicht 1-6 eingegeben wird.
MaWin schrieb:> Vielleicht verwendest du getch() aus conio.h
Wobei conio.h und damit u.a. getch nicht zum Standard gehören. Die
Implementierung einer einfachen Tastaturabfrage ohne Enter und Echo ist
darum auch eine der typischen Hürden für Anfänger, die ein Konsolenspiel
oder ein Menüsystem schreiben wollen/müssen und keine conio.h einbinden
können (z.B. unter Linux). Mit kbhit und getch ist es dagegen sogar
nicht-blockierend trivial.
R. Rebentrost schrieb:> Wobei conio.h und damit u.a. getch nicht zum Standard gehören. Die> Implementierung einer einfachen Tastaturabfrage ohne Enter und Echo ist> darum auch eine der typischen Hürden für Anfänger, die ein Konsolenspiel> oder ein Menüsystem schreiben wollen/müssen und keine conio.h einbinden> können (z.B. unter Linux). Mit kbhit und getch ist es dagegen sogar> nicht-blockierend trivial
Und was ist die Lösung? Ncurses?
Dumdi Dum schrieb:> R. Rebentrost schrieb:>> Wobei conio.h und damit u.a. getch nicht zum Standard gehören.>> Und was ist die Lösung? Ncurses?
Eine Lösung dieses Problems kann nur sein
* systemspezifische Erweiterungen benutzen (kbhit, getch, ja auch
Ncurses)
* das Spiel so aufziehen, dass man keinen einzelnen Tastendruck ohne
Bestätigung durch Return erkennen muss. Auch das geht, allerdings nicht
bei einem klassischen Jump & Run Spiel. Dann liegt der Fokus so eines
Spieles eben in der strategischen Vorausplanung und weniger in der
Reaktionsschnelle (Siehe zb
http://www-math.bgsu.edu/~grabine/moria.html)
R. Rebentrost schrieb:> Wobei conio.h und damit u.a. getch nicht zum Standard gehören.> [...]> Mit kbhit und getch ist es dagegen sogar nicht-blockierend trivial.
... die aber beide ebenfalls kein Standard-C sind.
Rolf Magnus schrieb:> R. Rebentrost schrieb:>> Mit kbhit und getch ist es dagegen sogar nicht-blockierend trivial.>> ... die aber beide ebenfalls kein Standard-C sind.
Schon klar. Ich habe mich wohl etwas ungeschickt ausgedrückt.
@Dumdi Dum
Wenn du nach 'getch linux' suchst, wirst du ein paar Lösungen/Funktionen
finden (oft wird termios verwendet).
Karl Heinz schrieb:> Eine Lösung dieses Problems kann nur sein> * systemspezifische Erweiterungen benutzen (kbhit, getch, ja auch> Ncurses)> * das Spiel so aufziehen, dass man keinen einzelnen Tastendruck ohne> Bestätigung durch Return erkennen muss. Auch das geht, allerdings nicht> bei einem klassischen Jump & Run Spiel. Dann liegt der Fokus so eines> Spieles eben in der strategischen Vorausplanung und weniger in der> Reaktionsschnelle (Siehe zb> http://www-math.bgsu.edu/~grabine/moria.html)
* mit SDL2 ein eigenes Terminal bauen (Fenster erstellen und mit
Zeichentabelle die Ausgabe zeichnen) und Tastatureingabe über SDL2
einlesen.
> Ich weiss nicht in welcher Welt du lebst. Aber mit Millisekunden bist du> um Größenordnungen daneben. Hast du eine Ahnung, wass ein PC in 1> Millisekunde alles machen kann?
das kommt immer darauf an was für einen PC Du hast - bei einem
Kleinklein Programm wie dem Robots Spiel wird man das nicht merken, bei
größeren Anwendungen allerdings schon. Das merkt man dann in Betrieben
mit Altrechnern und den schnell zusammengefrickelten Programmen :)
Nur weil die Performance des PC es hergibt sollte man sich schlechten
Programmierstil nicht aneignen und Endlos if ... else if Schleifen
gehören dazu - von der Übersichtlichkeit mal ganz zu schweigen.
> Karl Heinz schrieb:> Eine Lösung dieses Problems kann nur sein> * systemspezifische Erweiterungen benutzen (kbhit, getch, ja auch> Ncurses)
ncurses ist m.W. schon C++, geht allerdings auch in C mit curses.h
kbhit und getch bedürfen conio.h
Ansonsten (Linux, etc.) wäre GTK+ noch eine Lösung ... da geht's dann
ins Eingemachte.
stirb auf einem anderen Planeten schrieb:> Endlos if ... else if Schleifen> gehören dazu - von der Übersichtlichkeit mal ganz zu schweigen.
1. if ist keine schleife
2. switch case ist eteas eingeschränkt, was die labels angeht. wenn man
später die Variable nicht auf feste werte prüfen, sondern vielleicht
gegen komplexere logische ausdrücke, andere Variablen, floats oder
Funktionsrückgaben oder sonst was, fängt man an das komplette ding
umzubauen, überall die breaks raus, überall die Klammern rein...
> 1. if ist keine schleife
okay, falsch ausgedrückt - trotzdem ist das Ganze bei längeren Abfragen
unübersichtlich. Aber macht alle wie Ihr meint.
> 2. switch case ist eteas eingeschränkt, was die labels angeht. wenn man> später die Variable nicht auf feste werte prüfen, sondern vielleicht> gegen komplexere logische ausdrücke, andere Variablen, floats oder> Funktionsrückgaben oder sonst was, fängt man an das komplette ding> umzubauen, überall die breaks raus, überall die Klammern rein...
insbesondere bei längeren Abfragen wird es einfach unübersichtlich,
siehe auch den Einwand weiter oben von Rolf Magnus
Per #define kann man via Präprozessor auch noch was machen.
Den Aufwand wirst Du bei nachträglichen Änderungen immer haben, auch bei
if ... else if Konstrukten.
> Sicherlich nicht.
So,so - ohne curses.h geht es nicht - das ist dann wie conio.h kein
Standard C mehr.
stirb auf einem anderen Planeten schrieb:> Nur weil die Performance des PC es hergibt sollte man sich schlechten> Programmierstil nicht aneignen und Endlos if ... else if Schleifen> gehören dazu - von der Übersichtlichkeit mal ganz zu schweigen.
Sorry.
Aber mit 'if-schleifen' hast du dich schon selbst disqualifiziert, noch
ehe ich mir den Rest durchlese.
stirb auf einem anderen Planeten schrieb:>> 1. if ist keine schleife> okay, falsch ausgedrückt - trotzdem ist das Ganze bei längeren Abfragen> unübersichtlich.
längere Abfrage?
Wird reden von maximal 9 Tasten! Nicht mehr. Keine 100, keine 1000,
keine 10000
Wenn sich das auf einem PC um mehr als Zehntel Mykrosekunden gegenüber
einem switch-case reisst, dann geb ich ein Bier aus. Die Chancen stehen
allerdings gar nicht so schlecht, dass ein Compiler diesen konkreten
switch-case intern sowieso wieder in eine if-else if Leiter umbaut, weil
sich hier eine binäre Suche gar nicht lohnt. Allenfalls könnte er noch
über eine Sprungleiste gehen. Aber auch da gibt es wieder etwas
Overhead, bis er den ASCII Code so umgebaut und auf Fehler geprüft hat,
damit er dann einen sauberen Array index kriegt. Bei der geringen Anzahl
an Fällen reden wir hier über einzelne Taktzyklen Unterschied. Aber
keinesfalls von Millisekunden.
Und wenn wir schon von übersichtlich sprechen:
Ein switch-case ist immer geschwätziger als eine if-else if Leiter. Die
kriegt man nur textuell kleiner, wenn man alle Formatierregeln über Bord
wirft, indem man die Zeilen anfüllt.
Aber: Gegen die Übersichtlichkeit hab ich gar nicht argumentiert.
Ausgangspunkt waren die Millisekunden. Und das ist ganz einfach in
diesem Fall Quatsch.
Karl Heinz schrieb:> Wenn sich das auf einem PC um mehr als Zehntel Mykrosekunden gegenüber> einem switch-case reisst, dann geb ich ein Bier aus.
Eigentlich gehe ich davon aus, dass der Compiler nach der Optimierung
gleich schnellen Assembler-Code ausspuckt (oder das man eigenlich gar
nicht sagen kann welches Konstrukt nach der Optimierung schneller ist;
außer man testet es).
Dumdi Dum schrieb:> Karl Heinz schrieb:>> Wenn sich das auf einem PC um mehr als Zehntel Mykrosekunden gegenüber>> einem switch-case reisst, dann geb ich ein Bier aus.>> Eigentlich gehe ich davon aus, dass der Compiler nach der Optimierung> gleich schnellen Assembler-Code ausspuckt (oder das man eigenlich gar> nicht sagen kann welches Konstrukt nach der Optimierung schneller ist;> außer man testet es).
Eben.
Dann gibt es ja auch noch die Alternative:
1
// 1 2 3 4 5 6 7 8 9
2
constintDeltaX[]={-1,0,1,-1,0,1,-1,0,1};
3
constintDeltaY[]={1,1,1,0,0,0,-1,-1,-1};
4
5
...
6
x_ziel=*x;
7
y_ziel=*y;
8
9
if(isdigit(Eingabe)){
10
x_ziel+=DeltaX[Eingabe-'0'];
11
y_ziel+=DeltaY[Eingabe-'0'];
12
}
13
...
die auch noch auf einen möglichen Geschwindigkeitsvorteil im Bereich
einzelner Taktzyklen zu untersuchen wäre. Durch Zusammenfassen der
Deltas in ein struct könnte man eventuell noch weitere einzelne
Taktzyklen rauskitzeln.
Vlad Tepesch schrieb:> Karl Heinz schrieb:>> Zehntel Mykrosekunden>> Wieviel sind denn das in Sekunden? :-P
1 x 10E-7 Sekunden
Aber ich denke eher, deine Anspielung gilt dem "y" als Ersatz für das
"µ".
Dumdi Dum schrieb:> Karl Heinz schrieb:>> Dann gibt es ja auch noch die Alternative:> das ist der richtige Ansatz.> Aber noch nicht richtig. So wäre es besser>>
1
>// 0 1 2 3 4 5 6 7 8 9
2
>constintDeltaX[]={0,-1,0,1,-1,0,1,-1,0,1};
3
>constintDeltaY[]={0,1,1,1,0,0,0,-1,-1,-1};
4
>
Danke. Die Taste 0 hab ich tatsächlich übersehen :-)
Klar. Bei einer Prozesssteuerung, die zeitkritisch ist, ist das alles
relevant. Aber doch nicht bei einem Spiel, das sowieso laufend auf den
Benutzer warten muss. Ich würde sagen, der TO hat noch ganz andere
Probleme, als sich um Optimierungen im Taktzyklenbereich Gedanken zu
machen.
Wie auch immer: Die Originalaussage, dass man mit switch-case
Millisekunden rausholen könne, ist masslos überzogen.
Das ist nicht nur nicht richtig, es ist nicht einmal falsch!
(Wolfgang Pauli)
Vlad Tepesch schrieb:> npn schrieb:>> Aber ich denke eher, deine Anspielung gilt dem "y" als Ersatz für das>> Die Anspielung war eher auf das "y" als Ersatz für das "i"
:-)
Ich bitte um Nachsicht.
Vlad Tepesch schrieb:> npn schrieb:>> Aber ich denke eher, deine Anspielung gilt dem "y" als Ersatz für das>> Die Anspielung war eher auf das "y" als Ersatz für das "i"
Ja, [lach], das meinte ich ja. Das "µ" ist mir rausgerutscht, weil oft
auch der Fehler gemacht wird, zum Beispiel "yF" zu schreiben statt "µF".
> Die Chancen stehen> allerdings gar nicht so schlecht, dass ein Compiler diesen konkreten> switch-case intern sowieso wieder in eine if-else if Leiter umbaut, weil> sich hier eine binäre Suche gar nicht lohnt.
in diesem Punkt irrst Du, siehe folgende Aussage:
"Many programming language optimize the switch statement so that it is
much faster than a standard if-else if structure provided the cases are
compiler constants. Many languages use a jump table or indexed branch
table to optimize switch statements. Wikipedia has a good discussion of
the switch statement. Also, here is a discussion of switch optimization
in C."
Quelle:
http://stackoverflow.com/questions/2158759/case-vs-if-else-if-which-is-more-efficient
Okay, es gibt natürlich auch schrottige Compiler ... das ist dann wie
mit den Millisekunden ;-)
> Wird reden von maximal 9 Tasten! Nicht mehr. Keine 100, keine 1000,> keine 10000
es geht ja auch ein wenig um den Programmierstil - wer das bei 9 Tasten
macht, fährt auch fort bei 20, 50, usw., weil er meint dank Compiler und
schnellem PC ist sowieso alles egal - diese Programmierweise merkt man
dann insbesondere bei alten PCs.
Zu Deiner Ehrenrettung folgendes Beispiel einer Messung mit C#:
http://www.dotnetperls.com/if-switch-performance
okay, mit den Millisekunden hab ich mich verhauen - aber bei großen
Programmen gibt es duchaus Effekte.