Ilias95 έγραψε:Να λοιπόν η άσκηση με two dimensional array:
-
Μορφοποιημένος Κώδικας: Επιλογή όλων
-
#include <stdio.h>
#include <string.h> // memset()
#include <stdlib.h> // srand(), rand()
#include <time.h> // time()
#define ALPHABETA "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define NUM_ALPHABETA sizeof(ALPHABETA) / sizeof(ALPHABETA[0])
#define NUM_ROWS 10
#define NUM_COLUMNS 10
int main(void)
{
int row = 0, col = 0;
char table[NUM_ROWS][NUM_COLUMNS];
memset(table, '.', sizeof(table)); // init table with dots
table[0][0] = ALPHABETA[0];
srand((unsigned) time(NULL));
for (int i = 1; i < NUM_ALPHABETA - 1; i++) {
if ((table[row+1][col] != '.' || row == NUM_ROWS-1) && (table[row-1][col] != '.' || row == 0) && \
(table[row][col+1] != '.' || col == NUM_COLUMNS-1) && (table[row][col-1] != '.' || col == 0)) {
break; // terminate if there is no available position
}
while (1) {
int move = rand() % 4;
if (move == 0 && row > 0 && table[row-1][col] == '.') {
row--; // UP
break;
}
else if (move == 1 && col < NUM_COLUMNS-1 && table[row][col+1] == '.') {
col++; // RIGHT
break;
}
else if (move == 2 && row < NUM_ROWS-1 && table[row+1][col] == '.') {
row++; // DOWN
break;
}
else if (move == 3 && col > 0 && table[row][col-1] == '.') {
col--; // LEFT
break;
}
}
table[row][col] = ALPHABETA[i];
}
for (row = 0 ; row < NUM_ROWS; row++) {
for (col = 0; col < NUM_COLUMNS; col++)
printf("%c ", table[row][col]);
putchar('\n');
}
return 0;
}
ΥΓ. Με έσκασε μέχρι να το κάνω να δουλέψει. Δύο ώρες μου πήρε για να καταλάβω στο τέλος ότι είχα συντακτικό λάθος!

Ωραίος Ηλία!

Ακόμα έχεις όμως νομίζω ένα προβληματάκι, επειδή στη συνθήκη if μέσα στο κεντρικό σου for-loop ξεκινάς...
-
Μορφοποιημένος Κώδικας: Επιλογή όλων
-
for (int i = 1; i < NUM_ALPHABETA - 1; i++) {
if ((table[row+1][col] != '.' || row == NUM_ROWS-1) && (table[row-1][col] != '.' || row == 0) && \ ...
ενώ κανονικά πρέπει να πάνε πρώτα οι έλεγχοι για την τρέχουσα γραμμή (row) ...
-
Μορφοποιημένος Κώδικας: Επιλογή όλων
-
for (int i = 1; i < NUM_ALPHABETA - 1; i++) {
if ( (row == NUM_ROWS-1 || table[row+1][col] != '.') && (row == 0 || table[row-1][col] != '.') && \ ...
διότι έτσι όπως το έχεις τώρα, αν βρίσκεσαι ήδη π.χ. στην row NUM_ROWS-1, τότε το ξεκίνημα της if με: table[ row+1 ][ col ] != '.' ... βγάζει το row+1 εκτός ορίων του table.
Άλλο τώρα, έγραψα τη δική σου λύση αφενός με ορολογίες από τη δική μου 2η λύση, κι αφετέρου με εκτεταμένη χρήση του προ-επεξεργαστή...
-
Μορφοποιημένος Κώδικας: Επιλογή όλων
-
#include <stdio.h>
#include <string.h> // memset()
#include <stdlib.h> // srand(), rand()
#include <time.h> // time()
#define USED "ABCDEFGHIJKLMNOPQRSTUVWXYZ" // used squares contain letters
#define EMPTY '.' // empty squares contain a dot
#define NMOVES sizeof(USED) / sizeof(USED[0]) // max moves allowed
#define NROWS 10 // board height in rows
#define NCOLS 10 // board width in coloumns
// is current square blocked in all directions?
#define ISBLOCKED(table, row, col) \
( \
((row) == NROWS-1 || (table)[(row)+1][(col)] != EMPTY) \
&& ((row) == 0 || (table)[(row)-1][(col)] != EMPTY) \
&& ((col) == NCOLS-1 || (table)[(row)][(col)+1] != EMPTY) \
&& ((col) == 0 || (table)[(row)][(col)-1] != EMPTY) \
)
// is 1 row up available?
#define UP_ISAVAIL(table, row, col) \
( (row) > 0 && (table)[(row)-1][(col)] == EMPTY )
// is 1 col to the right available?
#define RT_ISAVAIL(table, row, col) \
( (col) < NCOLS-1 && (table)[(row)][(col)+1] == EMPTY )
// is 1 row down available?
#define DN_ISAVAIL(table, row, col) \
( (row) < NROWS-1 && (table)[(row)+1][(col)] == EMPTY )
// is 1 col to the left available?
#define LT_ISAVAIL(table, row, col) \
( (col) > 0 && (table)[(row)][(col)-1] == EMPTY )
// all possible directions for the next move
enum Dir { UP=0, RT, DN, LT };
int main( void )
{
int row = 0, col = 0;
char table[NROWS][NCOLS];
memset(table, EMPTY, sizeof(table)); // init table with EMPTY
table[0][0] = USED[0];
srand( (unsigned) time(NULL) );
for (int imove = 1; imove < (int)(NMOVES - 1); imove++)
{
if ( ISBLOCKED(table, row, col) )
break;
while (1)
{
int move = rand() % 4; // pick a random direction
if ( move == UP && UP_ISAVAIL(table, row, col) ) {
row--;
break;
}
if ( move == RT && RT_ISAVAIL(table, row, col) ) {
col++;
break;
}
if ( move == DN && DN_ISAVAIL(table, row, col) ) {
row++;
break;
}
if ( move == LT && LT_ISAVAIL(table, row, col) ) {
col--;
break;
}
}
table[row][col] = USED[imove];
}
/* print the table */
for (row = 0; row < NROWS; row++) {
for (col = 0; col < NCOLS; col++)
printf("%c ", table[row][col]);
putchar('\n');
}
return 0;
}
Ακολουθεί πιστά (πιστότατα) την λογική της λύσης σου (με μόνη διόρθωση την παραπάνω) και το έκανα απλά και μόνο για να σε βάλω στο πνεύμα του προ-επεξεργαστή, ο οποίος χρησιμοποιείται ευρέως στο C programming (χωρίς αυτό να σημαίνει πως δεν υπάρχουν αδυναμίες, κυρίως η έλλειψη type-checking σε όσα macros δέχονται ορίσματα).
Δεν ξέρω αν το γνωρίζετε, αλλά η C++ ξεκίνησε τη ζωή της γραμμένη αποκλειστικά στον προ-επεξεργαστή της C.
Το πλεονέκτημα με τον προ-επεξεργαστή, είναι πως αν χρησιμοποιηθεί σωστά και βάσει πλάνου (όχι δηλαδή απλά ορίζουμε στην τύχη σταθερές και macros ) τότε αφενός ανεβάζει κατακόρυφα το ευανάγνωστο του υπόλοιπου κώδικα (μειώνεται δραματικά η ανάγκη σχολίων στον υπόλοιπο κώδικα) και αφετέρου διευκολύνει αφάνταστα την παραμετροποίηση του προγράμματος. Δηλαδή σωστά οργανωμένες & ονοματισμένες σταθερές & macros(), συγκεντρωμένα σε ένα σημείο ή σε μεγάλα πρότζεκτ σε ένα header file, μας επιτρέπουν να αλλάζουμε αρχικές τιμές και όρια μέσα στα οποία δρα το πρόγραμμά μας.
Για παράδειγμα, στη προκειμένη περίπτωση, ορίζοντας με #define τον κενό χαρακτήρα '.' ως EMPTY, και χρησιμοποιώντας EMPTY στον υπόλοιπο κώδικα, μπορούμε κατόπιν με μια και μόνη κίνηση (στον ορισμό του EMPTY) να αλλάξουμε τον χαρακτήρα που μαρκάρει τα άδεια τετράγωνα, από τελεία ας πούμε σε παύλα '-'. Ομοίως το πλάτος ή/και ύψος του πίνακά μας, και ούτω καθ' εξής.
Στην ίδια φιλοσοφία, τα macros που δέχονται ορίσματα, μας επιτρέπουν να βελτιώνουμε/διορθώνουμε/εξετάζουμε τον κώδικά τους σε ένα μόνο μέρος, αντί να τον ψάχνουμε σε όλα τα σημεία που τον χρησιμοποιούμε (αυτό είναι κοινό χαρακτηριστικό με τη χρησιμότητα των συναρτήσεων, αλλά για τα macros δεν κάνει check τους τύπους των ορισμάτων ο compiler... επικίνδυνο πράγμα όταν είσαι αρχάριος, αλλά και γενικότερα).
Επίσης, η σωστή χρήση του προ-επεξεργαστή συνδυασμένη με ευέλικτο κώδικα, σου επιτρέπει να ελέγξεις την ευελιξία του κώδικά σου (την προσαρμοστικότητά του) σε διάφορες καταστάσεις, αλλάζοντας απλώς κάποιες τιμές στα #define σου. Για παράδειγμα, αν το NUM_ALPHABETA δεν ήταν ορισμένο έτσι ευέλικτα όπως το έχεις (σε συνάρτηση δηλαδή με το ALPHABETA) και ήταν ας πούμε ορισμένο hard-coded στην τιμή 26, τότε αν πρόσθετες αργότερα π.χ. και τα πεζά λατινικά γράμματα στη συνέχεια των κεφαλαίων μέσα στον ALPHABETA, το πρόγραμμά σου θα τα αγνοούσε. Ενώ τώρα αν τα προσθέσεις τα πεζά στο ALPHABETA θα προσαρμοστεί αυτόματα το πρόγραμμά σου... όχι μόνο θα τα συμπεριλαμβάνει στο output του αυτόματα, αλλά θα αυξήσει επίσης αυτόματα τον αριθμό επιτρεπόμενων κινήσεων.
Α btw, μια που το είπα αυτό, έχουμε και οι 2 μια σημαντική έλλειψη στους κώδικές μας... δεν εξετάζουμε ο αριθμός κινήσεων να ΜΗΝ ξεπερνάει το μέγιστο πλήθος του πίνακά μας (NROWS * NCOLS). Αν στο ALPHABET χρησιμοποιήσουμε πάνω από 100 γράμματα, τα προγράμματά μας κάποια στιγμή θα κρασάρουν! . Ένας εύκολος τρόπος να το δοκιμάσουμε είναι να αλλάξουμε τα NROWS και NCOLS σε 3 και 3 (από 10 και 10). Θα έχουμε έτσι 9 συνολικά τετράγωνα στον πίνακα, αλλά 26 επιτρεπόμενες κινήσεις... ΜΠΟΥΜ !!!!!

Και κάτι τελευταίο για τον κώδικα αυτόν, στο infinite-loop που κάνεις break μέσα σε κάθε if-else, τα else είναι περιττά... από τη στιγμή που κάνεις break δεν υπάρχει περίπτωση να συνεχίσει στα επόμενα ... οπότε δεν χρειάζονται τα else. Έτσι κι αλλιώς με ή χωρίς else, θα τα πιάσει με τη σειρά από πάνω μέχρι κάτω

Έγραψα κι άλλη εκδοχή του κώδικα της λύσης σου, βελτιώνοντας τον προηγούμενο που περιέγραψα παραπάνω. ΟΙ βασικές βελτιώσεις είναι
α) στα macros που διαχειρίζονται πιθανά μπλοκαρίσματα, κάνοντας τα και πιο ομοιογενή ως προς τις ονομασίες και τη λειτουργία τους, αλλά και χρησιμοποιώντας τα βασικά macros μέσα στο γενικότερο
β) κατάργησα τα
break σε εκείνα τα if-else του infinite loop, αντικαθιστώντας την λογική τους με μια boolean μεταβλητή:
success.
Δεν υπάρχει κάποιο ουσιαστικό πλεονέκτημα ή μειονέκτημα συγκριτικά με τον προηγούμενο κώδικα, πέρα από τον πιο ευανάγνωστο (και πιο δομημένο) κώδικα. Ο πιο δυσανάγνωστος (και λιγότερο δομημένος) κώδικας είναι ελαφρώς πιο efficient (και σε μνήμη και σε ταχύτητα)... σε αμελητέες ποσότητες, αλλά υπαρκτές.
-
Μορφοποιημένος Κώδικας: Επιλογή όλων
-
#include <stdio.h>
#include <string.h> // memset()
#include <stdlib.h> // srand(), rand()
#include <time.h> // time()
#include <stdbool.h> // _Bool, true, false
#define USED "ABCDEFGHIJKLMNOPQRSTUVWXYZ" // used squares contain letters
#define EMPTY '.' // empty squares contain a dot
#define NMOVES sizeof(USED) / sizeof(USED[0]) // max moves allowed
#define NROWS 10 // board height in rows
#define NCOLS 10 // board width in coloumns
enum Dir { UP=0, RT, DN, LT }; // next-move possible directions
// is current square blocked in all directions?
#define ISBLOCKED(table, row, col) \
( \
UP_ISBLOCKED( (table), (row), (col) ) \
&& RT_ISBLOCKED( (table), (row), (col) ) \
&& DN_ISBLOCKED( (table), (row), (col) ) \
&& LT_ISBLOCKED( (table), (row), (col) ) \
)
// is 1 row up blocked?
#define UP_ISBLOCKED(table, row, col) \
( (row) == 0 || (table)[(row)-1][(col)] != EMPTY )
// is next col blocked?
#define RT_ISBLOCKED(table, row, col) \
( (col) == NCOLS-1 || (table)[(row)][(col)+1] != EMPTY )
// is 1 row down blocked?
#define DN_ISBLOCKED(table, row, col) \
( (row) == NROWS-1 || (table)[(row)+1][(col)] != EMPTY )
// is previous col blocked?
#define LT_ISBLOCKED(table, row, col) \
( (col) == 0 || (table)[(row)][(col)-1] != EMPTY )
/*********************************************************//**
*
************************************************************/
int main(void)
{
int row = 0, col = 0;
char table[NROWS][NCOLS];
memset(table, EMPTY, sizeof(table)); // init table to EMPTY
table[0][0] = USED[0];
srand( (unsigned) time(NULL) );
for (int imove = 1; imove < (int)(NMOVES - 1); imove++)
{
_Bool success = false;
if ( ISBLOCKED(table, row, col) ) // blocked in all dirs
break;
while ( !success )
{
int move = rand() % 4; // pick a random direction
success = false;
if ( move == UP && !UP_ISBLOCKED(table, row, col) ) {
row--;
success = true;
}
else if ( move == RT && !RT_ISBLOCKED(table, row, col) ) {
col++;
success = true;
}
else if ( move == DN && !DN_ISBLOCKED(table, row, col) ) {
row++;
success = true;
}
else if ( move == LT && !LT_ISBLOCKED(table, row, col) ) {
col--;
success = true;
}
}
table[row][col] = USED[imove];
}
/* print the table */
for (row = 0; row < NROWS; row++) {
for (col = 0; col < NCOLS; col++)
printf("%c ", table[row][col]);
putchar('\n');
}
return 0;
}
ΥΓ. Έχω παρατηρήσει πως αποφεύγεις τη χρήση συναρτήσεων, γιατί; Οι συναρτήσεις είναι ο θεμελιώδεις λίθος των procedural γλωσσών.