# Thread: Multi-Array Problem: Random Walk of Alphabet on 10x10 Matrix (Beginner)

1. ## Multi-Array Problem: Random Walk of Alphabet on 10x10 Matrix (Beginner)

Hi All,

Another beginner question. I am to write a "random walk" program of the English alphabet (A thru Z) on a 10x10 matrix. While I think I have a solution, it could be cleaner.

Any suggestions on how to tighten this code would be appreciated, as I am still learning about arrays.

Code:
```#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define N 10
#define TRUE 1
#define FALSE 0

int main() {
char ch = 'A', mat[N][N] = {
{'.', '.', '.', '.', '.', '.', '.', '.', '.', '.'},
{'.', '.', '.', '.', '.', '.', '.', '.', '.', '.'},
{'.', '.', '.', '.', '.', '.', '.', '.', '.', '.'},
{'.', '.', '.', '.', '.', '.', '.', '.', '.', '.'},
{'.', '.', '.', '.', '.', '.', '.', '.', '.', '.'},
{'.', '.', '.', '.', '.', '.', '.', '.', '.', '.'},
{'.', '.', '.', '.', '.', '.', '.', '.', '.', '.'},
{'.', '.', '.', '.', '.', '.', '.', '.', '.', '.'},
{'.', '.', '.', '.', '.', '.', '.', '.', '.', '.'},
{'.', '.', '.', '.', '.', '.', '.', '.', '.', '.'}};

int direction, zero = 0, one = 0, two = 0, three = 0, row = 0, col = 0;

srand((unsigned) time(NULL));

mat[row][col] = ch;

ch++;

while (ch > 'A' && ch <= 'Z'){

if (zero == TRUE && one == TRUE && two == TRUE && three == TRUE)
break;

direction = rand() % 4;

switch (direction) {
case 0:
if (zero == TRUE)
break;

zero = TRUE;

if (row < N)
row = row + 1;
else
break;

if(mat[row][col] == '.'){
mat[row][col] = ch;
ch++;
zero = one = two = three = FALSE;

break;
}
else {
row = row -1;

break;
}

case 1:
if (one == TRUE)
break;

one = TRUE;

if (row > 0)
row = row - 1;
else
break;
if(mat[row][col] == '.'){
mat[row][col] = ch;
ch++;
zero = one = two = three = FALSE;

break;
}
else {
row = row + 1;
break;
}

case 2:
if (two == TRUE)
break;

two = TRUE;

if (col > 0)
col = col - 1;
else
break;
if(mat[row][col] == '.'){
mat[row][col] = ch;
ch++;
zero = one = two = three = FALSE;
break;
}
else{
col = col + 1;
break;
}

case 3:
if (three == TRUE)
break;

three = TRUE;

if (col < N)
col = col + 1;
else
break;
if(mat[row][col] == '.'){
mat[row][col] = ch;
ch++;
zero = one = two = three = FALSE;

break;
}
else{
col = col - 1;
break;
}

}

}

printf ("\n\n");
for (int row = 0; row < N; row++) {
for (int col = 0; col < N; col++){
printf("%c ", mat[row][col]);
}
printf("\n");
}

printf("\n\n\n");
return 0;
}```

2. I think you need to start learning about writing your own functions.
100+ lines of code in main, which is the result of lots of ctrl-c / ctrl-v (copy and paste) is a bad place to be.

Well not that it's necessarily bad, but you need to examine the differences between the two copies after you've done an edit.
- The original block of code becomes a function.
- The edits you made become parameters to that function.

So that where you originally had two near identical copies of the code, you now have one parametrised function, and two function calls with parameters.

Another problem with copy/paste code is that if you want to make it do something different, aside from the actual time and effort in doing the changes, is making sure you make the change in ALL the copies.

Anyway, some code.
Code:
```#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define N 10

// So you can change N without editing an array initialiser.
void init(char mat[N][N]) {
for ( int r = 0 ; r < N ; r++ ) {
for ( int c = 0 ; c < N ; c++ ) {
mat[r][c] = '.';
}
}
}

void print(char mat[N][N]) {
for ( int r = 0 ; r < N ; r++ ) {
for ( int c = 0 ; c < N ; c++ ) {
printf("%c ",mat[r][c]);
}
printf("\n");
}
}

void pickMove(int *dx, int *dy) {
int direction = rand() % 4;
switch ( direction ) {
case 0:
*dx = 1; *dy = 0;
break;
case 1:
*dx = -1; *dy = 0;
break;
case 2:
*dx = 0; *dy = 1;
break;
case 3:
*dx = 0; *dy = -1;
break;
}
}

int isValidMove(char mat[N][N], int row, int col, int dx, int dy) {
int r = row + dx, c = col + dy;
// is out of bounds?
if ( r < 0 || r >= N ) return 0;
if ( c < 0 || c >= N ) return 0;
// is occupied?
if ( mat[r][c] != '.' ) return 0;
// is in bounds and unoccupied
return 1;
}

int main ( ) {
char mat[N][N];
char ch = 'A';
int row = 0, col = 0;
init(mat);
mat[row][col] = ch;
srand((unsigned) time(NULL));
int tryagain = 0;
int maxtries = 5;
for ( ch = 'B' ; ch <= 'Z' ; ) {
int dx, dy;
pickMove(&dx,&dy);
if ( isValidMove(mat,row,col,dx,dy) ) {
row += dx;
col += dy;
mat[row][col] = ch++;
tryagain = 0;
} else if ( tryagain++ == maxtries ) {
break;
}
}
print(mat);
return 0;
}```
Now you can start to play around with various ideas without significantly rewriting the code each time.
- change the value of N
- change the persistence of the walker by changing maxtries
- change pickMove to only pick directions which are usable
- change isValidMove to perhaps implement pac-man style edge wraparound

3. Modern C (C99, which you should be using) has stdbool.h, which defines bool, true, and false.
When we are testing for true or false, we don't say one == true, we just say one. And to test for false we say !one.
It looks like zero, one, two, and three would be better named down, up, right, and left, respectively.

You can use an enum to define symbols for the 0, 1, 2, 3 that you use to represent the directions.

N is a little short for a global symbol. How about SIZE? And you actually have to check for < SIZE-1 not just < SIZE since the row and col values are allowed to be 0 to SIZE-1. (That's an actual bug in your program.)

Sticking close to your version, the following is a cleanup.
Code:
```#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h> // memset
#include <time.h>

#define SIZE 10

enum { DOWN, UP, LEFT, RIGHT, NUM_DIRS };

int main() {
srand(time(NULL));

int row = SIZE / 2, col = SIZE / 2;  // start in the middle

// These are used to tell then we are boxed in.
// true means we can go in that direction.
bool down = true, up = true, left = true, right = true;

char mat[SIZE][SIZE];
memset(mat, '.', SIZE * SIZE);

char ch = 'A';
mat[row][col] = ch++;

while (ch <= 'Z' && (down || up || left || right)) {
switch (rand() % NUM_DIRS) {
case DOWN:
if (row < SIZE - 1 && mat[row+1][col] == '.') {
mat[++row][col] = ch++;
down = up = left = right = true;
}
else
down = false;
break;
case UP:
if (row > 0 && mat[row-1][col] == '.') {
mat[--row][col] = ch++;
down = up = left = right = true;
}
else
up = false;
break;
case LEFT:
if (col > 0 && mat[row][col-1] == '.') {
mat[row][--col] = ch++;
down = up = left = right = true;
}
else
left = false;
break;
case RIGHT:
if (col < SIZE - 1 && mat[row][col+1] == '.') {
mat[row][++col] = ch++;
down = up = left = right = true;
}
else
right = false;
break;
}
}

printf ("\n");
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++)
printf("%c ", mat[row][col]);
printf("\n");
}
printf("\n");

return 0;
}```

4. That's another version. But it is always the same arrangement of the alphabet.

Code:
```#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define MAT 10

void initMatrix(char matrix[MAT][MAT]);

int main(void)
{
system("cls");
char matrix[MAT][MAT];

initMatrix(matrix);

return(0);
}

void initMatrix(char matrix[MAT][MAT])
{
//Alphabet starts bei 65 decimal.
int a = 65;
for (int i = 1; i <= MAT; i++)
{
for (int j = 1; j <= MAT; j++)
{
if (rand() % 4 == 0)
{
printf("%c ", a++);
}
else
{
printf("%c ", matrix[i][j] = '.');
}
}
printf("\n");
}
}```

5. Here's a version that uses four bits in a single unsigned int instead of the four separate boolean variables.
Code:
```#include <stdio.h>
#include <stdlib.h>
#include <string.h> // memset
#include <time.h>

#define SIZE 10
#define EMPTY '.'

enum { DOWN, UP, LEFT, RIGHT, NUM_DIRS };

enum { BLOCK_DOWN=1, BLOCK_UP=2, BLOCK_LEFT=4, BLOCK_RIGHT=8, BLOCKED=15 };

int main() {
srand(time(NULL));

int row = SIZE / 2, col = SIZE / 2;  // start in the middle

// blocked == BLOCKED when there is no viable direction
unsigned blocked = 0;

char mat[SIZE][SIZE];
memset(mat, EMPTY, SIZE * SIZE);

char ch = 'A';
mat[row][col] = ch++;

while (ch <= 'Z' && blocked != BLOCKED) {
switch (rand() % NUM_DIRS) {
case DOWN:
if (row < SIZE - 1 && mat[row+1][col] == EMPTY) {
mat[++row][col] = ch++;
blocked = 0;
}
else
blocked |= BLOCK_DOWN;
break;
case UP:
if (row > 0 && mat[row-1][col] == EMPTY) {
mat[--row][col] = ch++;
blocked = 0;
}
else
blocked |= BLOCK_UP;
break;
case LEFT:
if (col > 0 && mat[row][col-1] == EMPTY) {
mat[row][--col] = ch++;
blocked = 0;
}
else
blocked |= BLOCK_LEFT;
break;
case RIGHT:
if (col < SIZE - 1 && mat[row][col+1] == EMPTY) {
mat[row][++col] = ch++;
blocked = 0;
}
else
blocked |= BLOCK_RIGHT;
break;
}
}

printf ("\n");
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++)
printf("%c ", mat[row][col]);
printf("\n");
}
printf("\n");

return 0;
}```

6. Now is the arrangement of the alphabet really random. "srand" sets a random starting point for "rand" at every start.

Code:
```#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

#define MAT 10

void initMatrix(char matrix[MAT][MAT]);

int main(void)
{
system("cls");
char matrix[MAT][MAT];

initMatrix(matrix);

return(0);
}

void initMatrix(char matrix[MAT][MAT])
{
//Alphabet starts bei 65 decimal.
int a = 65;
time_t zeit;

//"srand" sets a random starting point for "rand" at every start.
srand(time(&zeit));

for (int i = 1; i <= MAT; i++)
{
for (int j = 1; j <= MAT; j++)
{
if (rand() % 4 == 0)
{
printf("%c ", a++);
}
else
{
printf("%c ", matrix[i][j] = '.');
}
}
printf("\n");
}
}```

7. Originally Posted by Kernelpanic
Now is the arrangement of the alphabet really random. "srand" sets a random starting point for "rand" at every start.
The problem you are solving is random too since it's not what the OP is doing.
And it's kind of dumb to post pictures when a simple copy/paste would do the trick.

8. Originally Posted by john.c
The problem you are solving is random too since it's not what the OP is doing.
And it's kind of dumb to post pictures when a simple copy/paste would do the trick.
Which trick?

9. The trick of learning English? Forget it.

10. Hi All,
Thanks so much for your responses! As a self-learner (without any use of videos), your advice has been a godsend. Most importantly, it has motivated me to continue despite some of my struggles with learning C.

@Salem: Thanks for the suggestions--this will be super useful when I start ploughing through King's "Functions" chapter, which is after "Arrays." Very much appreciate you showing me how to write this differently.

@john.c: Extremely grateful to you for your style edits and comments (both in this post and my last one)! I've truly learned a lot from your code. As for C99...I've been using King's "C Programming: A Modern Approach." (It's the first edition, and my print copy has a copyright of 1996.) Do you recommend that I get King's latest edition, or will this first edition suffice for simply learning the basics?

@Kernelpanic: thanks for your suggestions! My random walk is slightly different in scope; namely, each letter is supposed to "walk" to a neighboring cell which means that I cannot skip multiple rows or columns.