Forum: PC-Programmierung C-Programmieren: Spiel Robots


von Steffen M. (ektoplasma)


Lesenswert?

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:
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <time.h>
4
5
#define LINKS 4
6
#define RECHTS 6
7
#define OBEN 8
8
#define UNTEN 2
9
#define BLEIBEN 5
10
11
#define LINKSOBEN 7
12
#define RECHTSOBEN 9
13
#define LINKSUNTEN 1
14
#define RECHTSUNTEN 3
15
16
#define XRANGE 22
17
#define YRANGE 7
18
19
void show_world(int level, int x, int y);
20
void init_world(int level, int x, int y);
21
int move_character(char feld[YRANGE][XRANGE], int *x, int *y, int richtung);
22
void compute_random_free_cell (char feld[YRANGE][XRANGE], int *x, int *y);
23
24
int level;
25
int richtung;
26
int y;
27
int x;
28
29
int running;
30
char eingabe;
31
32
typedef struct{int x; int y;}Koordinaten;
33
34
Koordinaten Roboter = {2,4};
35
Koordinaten Spieler = {3,6};
36
37
char feld[YRANGE][XRANGE];
38
39
int main()
40
{
41
    printf("-------- R O B O T S -----------");
42
    system("cls");
43
    printf("Waehlen Sie ein Level [1-6]:");
44
    scanf("%d", &level);
45
    show_world(level, x, y);
46
    fflush(stdin);
47
48
    while(1){
49
50
        ///passiert etwas
51
        
52
        }
53
     return 0;
54
    }
55
}
56
int move_character(char feld[YRANGE][XRANGE], int *x, int *y, int richtung){
57
58
char hindernis;
59
char sieg;
60
61
switch(richtung){
62
63
64
        case OBEN:  hindernis = feld[*y--][*x];
65
66
                        switch(hindernis){
67
68
                            case '.':   feld[*y][*x] = '.';
69
                                        feld[*y--][*x] = '@';
70
                                        break;                       /// nach oben laufen und vorheriger Zelle zuruecksetzen
71
72
                            case 'R':   feld[*y][*x] = '.';
73
                                        feld[*y--][*x] = 'X';           /// -> Verloren. In Roboter gelaufen
74
                                        sieg = 'R';
75
                                        break;
76
77
                            default:
78
                                        feld[*y][*x] = '@';             ///stehen bleiben
79
                                        break;
80
                        }
81
                    break;
82
83
84
        case LINKS: hindernis = feld[*y][*x--];
85
86
                        switch(hindernis){
87
88
                            case '.':   feld[*y][*x] = '.';
89
                                        feld[*y][*x--] = '@';
90
                                        break;                       /// nach oben laufen und vorheriger Zelle zuruecksetzen
91
92
                            case 'R':   feld[*y][*x] = '.';
93
                                        feld[*y][*x--] = 'X';           /// -> Verloren. In Roboter gelaufen
94
                                        sieg = 'R';
95
                                        break;
96
97
                            default:
98
                                        feld[*y][*x] = '@';             ///stehen bleiben
99
                                        break;
100
                        }
101
                    break;
102
103
104
        case UNTEN: hindernis = feld[*y++][*x];
105
106
                        switch(hindernis){
107
108
                            case '.':   feld[*y][*x] = '.';
109
                                        feld[*y++][*x] = '@';
110
                                        break;                       /// nach oben laufen und vorheriger Zelle zuruecksetzen
111
112
                            case 'R':   feld[*y][*x] = '.';
113
                                        feld[*y++][*x] = 'X';           /// -> Verloren. In Roboter gelaufen
114
                                        sieg = 'R';
115
                                        break;
116
117
                            default:
118
                                        feld[*y][*x] = '@';             ///stehen bleiben
119
                                        break;
120
                        }
121
                    break;
122
123
124
        case RECHTS:  hindernis = feld[*y][*x--];
125
126
                        switch(hindernis){
127
128
                            case '.':   feld[*y][*x] = '.';
129
                                        feld[*y][*x++] = '@';
130
                                        break;                       /// nach oben laufen und vorheriger Zelle zuruecksetzen
131
132
                            case 'R':   feld[*y][*x] = '.';
133
                                        feld[*y][*x++] = 'X';           /// -> Verloren. In Roboter gelaufen
134
                                        sieg = 'R';
135
                                        break;
136
137
                            default:
138
                                        feld[*y][*x] = '@';             ///stehen bleiben
139
                                        break;
140
                        }
141
                    break;
142
143
144
145
        case LINKSOBEN: hindernis = feld[*y--][*x--];
146
147
                        switch(hindernis){
148
149
                            case '.':   feld[*y][*x] = '.';
150
                                        feld[*y--][*x--] = '@';
151
                                        break;                       /// nach oben laufen und vorheriger Zelle zuruecksetzen
152
153
                            case 'R':   feld[*y][*x] = '.';
154
                                        feld[*y--][*x--] = 'X';           /// -> Verloren. In Roboter gelaufen
155
                                        sieg = 'R';
156
                                        break;
157
158
                            default:
159
                                        feld[*y][*x] = '@';             ///stehen bleiben
160
                                        break;
161
                        }
162
                    break;
163
164
165
        case RECHTSOBEN:  hindernis = feld[*y--][*x++];
166
167
                            switch(hindernis){
168
169
                                case '.':   feld[*y][*x] = '.';
170
                                            feld[*y--][*x++] = '@';
171
                                            break;                       /// nach oben laufen und vorheriger Zelle zuruecksetzen
172
173
                                case 'R':   feld[*y][*x] = '.';
174
                                            feld[*y--][*x++] = 'X';           /// -> Verloren. In Roboter gelaufen
175
                                            sieg = 'R';
176
                                            break;
177
178
                                default:
179
                                            feld[*y][*x] = '@';             ///stehen bleiben
180
                                            break;
181
                            }
182
                        break;
183
184
185
        case LINKSUNTEN:   hindernis = feld[*y--][*x++];
186
187
                            switch(hindernis){
188
189
                                case '.':   feld[*y][*x] = '.';
190
                                            feld[*y--][*x++] = '@';
191
                                            break;                       /// nach oben laufen und vorheriger Zelle zuruecksetzen
192
193
                                case 'R':   feld[*y][*x] = '.';
194
                                            feld[*y--][*x++] = 'X';           /// -> Verloren. In Roboter gelaufen
195
                                            sieg = 'R';
196
                                            break;
197
198
                                default:
199
                                            feld[*y][*x] = '@';             ///stehen bleiben
200
                                            break;
201
                            }
202
                        break;
203
204
205
206
        case RECHTSUNTEN:   hindernis = feld[*y++][*x++];
207
208
                            switch(hindernis){
209
210
                                case '.':   feld[*y][*x] = '.';
211
                                            feld[*y++][*x++] = '@';
212
                                            break;                       /// nach oben laufen und vorheriger Zelle zuruecksetzen
213
214
                                case 'R':   feld[*y][*x] = '.';
215
                                            feld[*y++][*x++] = 'X';           /// -> Verloren. In Roboter gelaufen
216
                                            sieg = 'R';
217
                                            break;
218
219
                                default:
220
                                            feld[*y][*x] = '@';             ///stehen bleiben
221
                                            break;
222
                            }
223
                        break;
224
225
    
226
        case BLEIBEN: feld[*y][*x] = '@'; break;
227
228
229
230
//neue Position setzen
231
    }
232
}
233
234
void show_world(int level, int x, int y){
235
236
    int i;
237
    int j;
238
239
    printf("\n");
240
    //leeres Array belegen.
241
    //horizontal
242
    for(j = 0; j < XRANGE; j++){
243
        feld[0][j] = '#';
244
        feld[YRANGE][j] = '#';}
245
    //vertikal
246
    for(i = 0; i < YRANGE; i++){
247
        feld[i][0] = '#';
248
        feld[i][XRANGE-1] = '#';}
249
    //innen
250
    for(i = 1; i < YRANGE; i++){
251
        for(j = 1; j < XRANGE-1; j++){
252
            feld[i][j] = '.';}
253
    }
254
    //Spieler + ROboter + Wände
255
256
    init_world(level, x, y);
257
258
    //Array ausgeben
259
    for(i=0; i<XRANGE; i++) {
260
        for(j=0; j<XRANGE; j++){
261
    printf("%c", feld[i][j]);
262
        }
263
  printf("\n");
264
    }
265
}
266
267
void compute_random_free_cell(char feld[YRANGE][XRANGE], int *x, int *y){
268
269
    char compare;
270
    int xran;
271
    int yran;
272
    time_t t;
273
274
    srand((unsigned) time(&t));
275
276
    xran = rand() % XRANGE;
277
    yran = rand() % YRANGE;
278
279
    *x = xran;
280
    *y = yran;
281
282
    compare = feld[*y][*x];;
283
284
    while(compare != '.'){
285
286
            xran = rand() % XRANGE;
287
            yran = rand() % YRANGE;
288
289
            *x = xran;
290
            *y = yran;
291
292
            compare = feld[*y][*x];}
293
}
294
295
296
void init_world(int level, int x, int y){
297
298
    int robots;
299
    int walls;
300
    int hyper_space;
301
    int r = 0;
302
    int w = 0;
303
    int h = 0;
304
305
306
    switch(level){
307
308
        case 1:   robots = 2; walls = 0; hyper_space = 1;
309
                    break;
310
311
        case 2:   robots = 4; walls = 0; hyper_space = 2;
312
                    break;
313
314
        case 3:   robots = 8; walls = 0; hyper_space = 3;
315
                    break;
316
317
        case 4:   robots = 4; walls = 20; hyper_space = 1;
318
                    break;
319
320
        case 5:   robots = 8; walls = 40; hyper_space = 2;
321
                    break;
322
323
        case 6:   robots = 16; walls = 80; hyper_space = 3;
324
                    break;
325
        }
326
327
328
        feld[6][6] = '@'; //spieler
329
330
        while(r < robots){
331
        compute_random_free_cell(feld, &x ,&y);
332
        //printf("Das hier steht drin: %d %d\n", x, y);
333
        feld[y][x] = 'R';
334
        r++;}
335
336
        while(w < walls){
337
        compute_random_free_cell(feld, &x ,&y);
338
        feld[y][x] = 'X';
339
        w++;}
340
341
        /*
342
343
        Hyper_space:        --> Leertaste gedrückt!
344
345
                            while(hyper_space > h){
346
                            compute_random_free_cell(feld, &x ,&y);
347
                            feld[y][x] = '@';
348
                            hyper_space--;}
349
350
                            */
351
352
    }

von Dumdi D. (dumdidum)


Lesenswert?

Ja, ist redundater code. Und sollte eigentlich nicht funktionieren. Ist 
der getestet?

von Karl H. (kbuchegg)


Lesenswert?

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.

von Steffen M. (ektoplasma)


Lesenswert?

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?

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

> 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
1
void compute_random_free_cell(char feld[YRANGE][XRANGE], int *x, int *y){

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
struct Datum
2
{
3
  int Tag;
4
  int Monat;
5
  int Jahr;
6
};

oder ein
1
struct Adresse
2
{
3
  char Stadt[40];
4
  int  Postleitzahl;
5
  char Strasse[40];
6
  int  Hausnummer;
7
};

oder eben bei dir
1
struct Koordinate
2
{
3
  int x;
4
  int y;
5
};

und enstprechende Variablen von diesem jeweiligen Typ
1
  struct Datum Hochzeitstag;
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
1
void printAddress( struct Adresse Ort)
2
{
3
   ...
4
[c]
5
und da die Funktion eine komplette Adresse mit allem drum und dran erhält, brauch ich dann in der Argumentschnittstelle nicht mehr alles einzeln aufführen. Das Objekt 'Ort', das vom Datentyp 'struct Adresse' ist enthält bereits all diese Angaben.
6
7
Habe ich eine Person, die ja zweifellos irgendwo wohnt und einen Geburtstag hat
8
[c]
9
struct Person
10
{
11
  char           Vorname[40];
12
  char           Nachnahme[40];
13
  struct Adresse Wohnort;
14
  struct Datum   Geburtstag;
15
};
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
1
void printAddress( struct Adresse Ort )
2
{
3
  printf( "%s %d\n", Ort.Strasse, Ort.Hausnummer );
4
  printf( "%d %s\n", Ort.Postleitzahl, Ort.Stadt );
5
}
6
7
int main()
8
{
9
  struct Person Otto =
10
   {
11
       "Otto",
12
       "Lilienthal",
13
       { "Koeln",
14
         4711,
15
         "mittlerer Ring",
16
         8
17
       },
18
       {    28, 2, 1856 }
19
    };
20
21
22
  printAdresse( Otto.Wohnort );
23
  printDatum( Otto.Geburtstag );
24
}


In deinem Fall
1
void compute_random_free_cell(char feld[YRANGE][XRANGE], int *x, int *y){
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
1
void compute_random_free_cell(char feld[YRANGE][XRANGE], Koordinaten* Position)
2
{
3
.....
4
5
   Position->x = ......
6
   Position->y = ......
7
8
...
9
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

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.
1
  int x_ziel = *x, y_ziel = *y;
2
3
  if( Eingabe == '6' )
4
    x_ziel++;
5
  else if( Eingabe == '4' )
6
    x_ziel--;
7
  else if( Eingabe == '2' )
8
    y_ziel++;
9
  else if( Eingabe == '8' )
10
    y_ziel--;
11
12
13
  ..... punktewertung ausrechnen, roboter schlagen lassen
14
  was auch immer, aber irgendwann kommt es zu
15
16
  if( feld[y_ziel][x_ziel] == '.' ) {
17
    feld[*y][*x] = '.';            // dort ist der Roboter nicht mehr
18
    feld[y_ziel][x_ziel] = '@';    // dafür ist er jetzt dort
19
  }
20
  else if( 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

: Bearbeitet durch User
von Schermi (Gast)


Lesenswert?

Hi, nur aus Interesse:
Studierst du zufällig in Ingolstadt? ;-)

von Steffen M. (ektoplasma)


Lesenswert?

Schermi schrieb:
> Hi, nur aus Interesse:
> Studierst du zufällig in Ingolstadt? ;-)

Ja, das tue ich ^^

von Steffen M. (ektoplasma)


Lesenswert?

1
struct Koordinaten{int x; int y;};
2
3
struct Koordinaten Roboter;
4
Roboter.x = 1;
5
Roboter.y = 1;
6
struct Koordinaten Spieler;

Wieso laesst er mich das nicht machen?
Build Message sagt:
error: expected '=' ',' ';' or '__attribute__' before '.' token.

von Vlad T. (vlad_tepesch)


Lesenswert?

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.

: Bearbeitet durch User
von Vlad T. (vlad_tepesch)


Lesenswert?

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
1
void compute_random_free_cell(char feld[YRANGE][XRANGE], int *x, int *y)
2
{
3
    char compare;
4
    int xran;
5
    int yran;
6
    do{
7
        xran = rand() % XRANGE;
8
        yran = rand() % YRANGE;
9
10
        *x = xran;
11
        *y = yran;
12
13
        compare = feld[*y][*x];;
14
    
15
    }while(compare != '.');
16
}

von Steffen M. (ektoplasma)


Lesenswert?

1
>   if( Eingabe == '6' )
2
>     x_ziel++;
3
>   else if( Eingabe == '4' )
4
>     x_ziel--;
5
>   else if( Eingabe == '2' )
6
>     y_ziel++;
7
>   else if( Eingabe == '8' )
8
>     y_ziel--;

Mit dem Code kann ich aber die Diagonalen Spielerläufe nicht abdecken 
oder?
1
  else if( Eingabe == '5' )
2
    y_ziel, x_ziel;

Sowas funktioniert ja nicht.
1
> struct Adresse
2
> {
3
>   char Stadt[40];
4
>   int  Postleitzahl;  // <--
5
>   char Strasse[40];
6
>   int  Hausnummer;    // <--
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 ^^

von Steffen M. (ektoplasma)


Lesenswert?

1
 compute_random_free_cell(feld, &x ,&y);
2
        printf("HIER:x=%d y=%d\n", x , y);
3
        feld[y][x] = '@'; //spieler

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?

von Vlad T. (vlad_tepesch)


Lesenswert?

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.

: Bearbeitet durch User
von Jay (Gast)


Lesenswert?

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:
1
if( Eingabe == '6' ) {
2
    x_ziel++;
3
} else if( Eingabe == '4' ) {
4
    x_ziel--;
5
} else if .....
6
.....

von Vlad T. (vlad_tepesch)


Lesenswert?

Jay schrieb:
> Dann schreibst du dafür halt vier weitere Vergleiche für 1, 3, 7, und 9.

genau, man kann nämlich auch mehrere Sachen kombinieren:
1
if( Eingabe == '5' ) {
2
    x_ziel++;
3
    x_ziel--;
4
} else if .....
5
.....

;-)

Jay schrieb:
> verführen lassen, sondern ganz brav und methodisch immer Klammern
> setzen:

das am besten von Anfang an angewöhnen

von Steffen M. (ektoplasma)


Lesenswert?

> 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
    else if( Eingabe == '4' ){
4
    x_ziel--;}
5
    else if( Eingabe == '2' ){
6
    y_ziel++;}
7
    else if( Eingabe == '8' ){
8
    y_ziel--;}
9
    else if( Eingabe == '7' ){
10
    y_ziel--;
11
    x_ziel--;}
12
    else if( Eingabe == '9' ){
13
    y_ziel--;
14
    x_ziel++;}
15
    else if( Eingabe == '3' ){
16
    y_ziel++;
17
    x_ziel++;}
18
    else if( Eingabe == '1' ){
19
    y_ziel++;
20
    x_ziel--;}
21
    else if( 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?

: Bearbeitet durch User
von Vlad T. (vlad_tepesch)


Lesenswert?

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
  }else if( Eingabe == '4' ){
4
    x_ziel--;
5
  }else if( 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.

: Bearbeitet durch User
von R. Rebentrost (Gast)


Lesenswert?

Ich habe den Thread nicht im Detail verfolgt, aber so wäre es evtl. 
übersichtlicher, wenn du (noch) keine Tabelle verwenden willst:
1
int dx, dy;
2
3
switch(Eingabe)
4
{
5
  case '1': dx = -1; dy =  1; break;
6
  case '2': dx =  0; dy =  1; break;
7
  case '3': dx =  1; dy =  1; break;
8
  case '4': dx = -1; dy =  0; break;
9
  case '6': dx =  1; dy =  0; break;
10
  case '7': dx = -1; dy = -1; break;
11
  case '8': dx =  0; dy = -1; break;
12
  case '9': dx =  1; dy = -1; break;
13
  default:  dx =  0; dy =  0;
14
}
15
16
...
17
18
x_ziel += dx;
19
y_ziel += dy;

von stirb auf einem anderen Planeten (Gast)


Lesenswert?

> 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.

von Klaus (Gast)


Lesenswert?

Steffen M. schrieb:
> Schermi schrieb:
>> Hi, nur aus Interesse:
>> Studierst du zufällig in Ingolstadt? ;-)
>
> Ja, das tue ich ^^

Ah. So wie Viktor Frankenstein!

von MaWin (Gast)


Lesenswert?

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
char feld[WHOLE+1];
6
int spieler;
7
8
int move_character(int richtung) // 1..9 für Richtung, 5 und 0 bleibt stehen
9
{
10
    static int bewegung[10]=
11
    {             0,
12
       LINELEN-1, LINELEN, LINELEN+1,
13
       -1,        0,       +1,
14
       -LINELEN-1,-LINELEN,-LINELEN+1
15
    };
16
    int neu=spieler+bewegung[richtung];
17
    if(feld[neu]=='R') return 0; // verloren
18
    if(feld[neu]=='.')
19
    {
20
        feld[spieler]='.';
21
        spieler=neu;
22
        feld[spieler]='@';
23
    }
24
    return 1;
25
}
26
27
void show_world(void)
28
{
29
    puts(feld);
30
}
31
32
int compute_random_free_cell(void)
33
{
34
    int pos;
35
    
36
    do
37
    {
38
        pos=rand(WHOLE);
39
    }
40
    while(feld[pos]!='.');
41
    return pos;
42
}
43
44
void init_world(int level)
45
{
46
    static struct 
47
    [
48
        int robots,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
int main()
82
{
83
    time_t t;
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
    return 0;
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.

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

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.

von Steffen M. (ektoplasma)


Lesenswert?

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.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

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
else if (b)
6
{
7
    // Code
8
}
9
else if (c)
10
{
11
    // Code
12
}

Am Rande noch...
Das hier ist in C verboten:

Steffen M. schrieb:
> fflush(stdin);

von Blinky (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

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?

von Dumdi D. (dumdidum)


Lesenswert?

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)

von MaWin (Gast)


Lesenswert?

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
int main()
2
{
3
    int ch;
4
    time_t t;
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.
9
    spieler=LINELEN*6+6; // Startposition 6,6
10
    init_world(level-1);
11
    while(1)
12
    {
13
        show_world();
14
        ch=getch();
15
        if(ch>='1'&&ch<='9') if(!move_character(ch-'0')) break;
16
    }
17
    return 0;
18
}

von R. Rebentrost (Gast)


Lesenswert?

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.

von Dumdi D. (dumdidum)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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)

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

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.

von R. Rebentrost (Gast)


Lesenswert?

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).

von TriHexagon (Gast)


Lesenswert?

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.

von stirb auf einem anderen Planeten (Gast)


Lesenswert?

> 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.

von Nase (Gast)


Lesenswert?

stirb auf einem anderen Planeten schrieb:
> ncurses ist m.W. schon C++
Sicherlich nicht.

von Vlad T. (vlad_tepesch)


Lesenswert?

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...

: Bearbeitet durch User
von stirb auf einem anderen Planeten (Gast)


Lesenswert?

> 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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Dumdi D. (dumdidum)


Lesenswert?

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).

von Karl H. (kbuchegg)


Lesenswert?

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
const int DeltaX[] = { -1,  0,  1, -1,  0,  1, -1,  0,  1 };
3
const int DeltaY[] = {  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.

: Bearbeitet durch User
von Dumdi D. (dumdidum)


Lesenswert?

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
const int DeltaX[] = { 0, -1,  0,  1, -1,  0,  1, -1,  0,  1 };
3
const int DeltaY[] = { 0,  1,  1,  1,  0,  0,  0, -1, -1, -1 };

: Bearbeitet durch User
von Vlad T. (vlad_tepesch)


Lesenswert?

Karl Heinz schrieb:
> Zehntel Mykrosekunden

Wieviel sind denn das in Sekunden? :-P

: Bearbeitet durch User
von npn (Gast)


Lesenswert?

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 
"µ".

von Karl H. (kbuchegg)


Lesenswert?

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
> const int DeltaX[] = { 0, -1,  0,  1, -1,  0,  1, -1,  0,  1 };
3
> const int DeltaY[] = { 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)

: Bearbeitet durch User
von Vlad T. (vlad_tepesch)


Lesenswert?

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"

von Karl H. (kbuchegg)


Lesenswert?

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.

von npn (Gast)


Lesenswert?

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".

von stirb auf einem anderen Planeten (Gast)


Lesenswert?

> 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.

von stirb auf einem anderen Planeten (Gast)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.