2

So I'm trying to print the map for my snake game. Here is the code:

#define WIDTH 20
#define HEIGHT 20

struct coordinate {
    int x;
    int y;
};

typedef struct coordinate coordinate;
coordinate map[HEIGHT][WIDTH];

void init_map(){ // Function initializes the map with the corresponding coordinates
for(int i = 0; i < HEIGHT; i++){
    for(int j = 0; j < WIDTH; j++){
        map[i][j].y = i;
        map[i][j].x = j;
    }
  }
} /* init_map */

// Function initializes the first snake with the corresponding coordinates
void init_snake1(coordinate snake1[], int snake1_length){ 
  snake1[0].x = WIDTH/2;
  snake1[0].y = HEIGHT/2;
  snake1[1].x = snake1[0].x;
  snake1[1].y = snake1[0].y+1;
} /* init_snake1 */

void print_map(coordinate snake1[], int snake1_length){
  for(int i = 0; i < HEIGHT; i ++){
    for(int j = 0; j < WIDTH; j++){
      if(map[i][j].x == 0 && map[i][j].y == 0){
        printf("#");
      }else if(map[i][j].x == WIDTH-1 && map[i][j].y == HEIGHT-1){
       printf("#");
      }else if(map[i][j].y == 0 || map[i][j].y == HEIGHT-1){
        printf("#");
      }else if(map[i][j].x == 0 || map[i][j].x == WIDTH-1){
        printf("#");
      }else if(map[i][j].x > 0 && map[i][j].x < WIDTH-1 && map[i][j].y > 0 || map[i][j].y < HEIGHT-1){
        for(int k = 0; k < snake1_length; k++){
          if(map[i][j].x == snake1[k].x && map[i][j].y == snake1[k].y){
            printf("x");
          }else{
            printf(" ");
          }
        }
      }
    }
    printf("\n");
  }
}/* print_map */

My problem is that when the map is printed it seems like to many blankspaces are printed within the map so that the right border is not beginning when the top or bottom border ends. As well as that the snakes tail is also shifted, only the snakes head seems to be in the right place. For better understanding of the problem I supply here the Console Output

Michael Dorgan
  • 12,453
  • 3
  • 31
  • 61
  • You don't really need the `map` at all - just use `i` and `j` to work out the y & x coordinates that you're printing out. – Chris Turner Jan 18 '19 at 17:59
  • And that last `if` statement looks iffy - you're mixing `||` and `&&` and probably need some extra brackets (gcc reports that as a warning for me) – Chris Turner Jan 18 '19 at 18:00

2 Answers2

1

I'm not really sure why you are storing coordinates at all for this. You can do it just with the snake list(s) and the known border size. If you wanted, you could "print" into your 2D array for collision checking later and just print the array as a list of strings, but for now:

// These should be "int" types should be "bool", but am using old-school int values for old C standards

#define WIDTH 20
#define HEIGHT 20

struct coordinate {
  int x;
  int y;
};

typedef struct coordinate coordinate;

int isBorder(int x, int y)
{
    return x == 0 || x == WIDTH-1 || y == 0 || y == HEIGHT - 1;
}

int isSnake(int x, int y, coordinate snake[], int snake_length)
{
  for(int i = 0; i < snake_length; i++)
  {
    if(x == snake[i].x && y == snake[i].y)
    {
      return 1;
    }
  }

  return 0;
}

void print_map(coordinate snake1[], int snake1_length)
{
  for(int y = 0; y < HEIGHT; y++)
  {
    for(int x = 0; x < WIDTH; x++)
    {
      if(isBorder(x, y))
      {
        printf("#");
      }
      else if(isSnake(x, y, snake1, snake1_length))
      {
        printf("x");
      }
      else
      {
        printf(" ");
      }
    }

    printf("\n");
  }
}

int main(void) 
{
  coordinate snake1[2] = {{3,3},{3,4}};
  print_map(snake1, 2);
  return 0;
}

Notice how using functions for the work makes it much clearer and cleaner to read. It also makes it trivial to add more snakes in the future - just change the isSnake() function to take more arrays. If you absolutely have to use the global map for storage, you can change all the printf() values to print to that array instead. I see no benefit whatsoever though to having your map be a list 2D array of coordinates - it should be of types. I think you may have misunderstood instructions on that part.

  int map[HEIGHT][WIDTH];

  if(isBorder(x, y))
  {
    map[y][x] = BorderType;
  }
  else if(isSnake(x, y, snake1, snake1_length))
  {
    map[y][x] = SnakeType;
  }
  else
  { 
    map[y][x] = EmptyType;
  }

Having this map would make it easier to do future collision detection and kill snakes. In that case, you would only want to print the border once and check if the new square for the snake was already empty - I'm guess you will get to this in a few weeks.

Michael Dorgan
  • 12,453
  • 3
  • 31
  • 61
  • I understood all of it except the last piece of code. I don't know what for example `map[y[x] = BorderType;` does. What is "BorderType"? – Fo Young Areal Lo Jan 18 '19 at 19:03
  • These would be some value you make up - a constant that represents what is stored at that location - say BorderType = 1, SnakeType = 2; EmptyType = 0; – Michael Dorgan Jan 18 '19 at 20:48
  • Added bonus is the above code actually works as-is right now so you can debug it and see how it all works. – Michael Dorgan Jan 18 '19 at 20:51
  • Coming from a mostly swift and objective-c and and some node-js background as well I am fairly new to straight c code. Obviously you have supreme talent and have worked with c/c++ and assembly for a long time. My question is then as a large c app begins to scale is it worth it to go in and do all of the minor optimizations (e.g. loop unrolling, bitwise ops, less method calls) or does it tend to naturally develop into higher level concepts. (obviously there can be heap/stack trade offs) like types as you have shown. – Greg Price Jan 26 '19 at 19:32
  • 1
    Honestly, you code for maintainability first. The compiler is good at converting functions and small things into local area optimizations. The micro optimizations you call out tend to only be issues in only the most often called portions of code. Using a profiler will point these out. So yes, higher level concepts are built upon small building blocks when done correctly and performance of these small blocks rarely matters in the overall scheme of things, but when they do, a profiler will show you where. – Michael Dorgan Jan 28 '19 at 20:07
  • Right, the compiler optimizations. Thanks for the reminder – Greg Price Jan 31 '19 at 07:33
0

The logic for printing a board is ok.... but will cause scaling issues if you arent careful with the math/spacing. Also you have a snake printing problem. I had to run it a couple times to see what was happening. I altered it to use the pythagorean theorem

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define WIDTH 10
#define HEIGHT 10

struct coordinate {
     int x;
     int y;
};

typedef struct coordinate coordinate;

struct snake {
    coordinate head;
    coordinate tail;
};

typedef struct snake snake;

coordinate map[HEIGHT][WIDTH];  
int init_snake(snake *s, coordinate snake_head, coordinate snake_tail){
    //Add param error checking
    s->head = snake_head;
    s->tail = snake_tail;
    return 0;
} /* init_snake1 */

int print_map(snake *s){
    int snake_length;
    int head;
    int tail;

    if ((head = pow(s->head.x - s->tail.x, 2)) < 0) {
        perror("print_map(): pow():");
        return -1;
    }

    if ((tail = pow(s->head.y - s->tail.y, 2)) < 0) {
        perror("print_map(): pow():\n");
        return -1;
    }

    if ((snake_length = sqrt(head + tail)) < 0) {
        perror("print_map(): sqrt():\n");
        return -1;
    }

    for(int i = 0; i < HEIGHT; i++){
       for(int j = 0; j < WIDTH; j++){
           if(map[i][j].x == 0 && map[i][j].x < WIDTH){
               printf("#");
           } else if(map[i][j].y == 0 && map[i][j].y < HEIGHT){
               printf("#");
           } else if(map[i][j].x == HEIGHT - 1 && map[i][j].x > 0){
               printf("#");
           } else if(map[i][j].y == WIDTH - 1 && map[i][j].y > 0){
               printf("#");
           } else if (s->head.x == map[i][j].x && s->tail.x == map[i][j].x && snake_length > 0) {
               printf("x");
               snake_length -= 1;
           } else if (s->head.y == map[i][j].y && s->tail.y == map[i][j].y && snake_length > 0) {
               printf("x");
               snake_length -= 1;
            } else {
               printf(" ");
            }
            printf("\n");
         }
    return 0;
 }/* print_map */

int main(int argc, char **argv) {
    init_map();
    snake *s = malloc(sizeof(snake));;
    coordinate h;
    h.x = 3;
    h.y = 4;
    coordinate t;
    t.x = 7;
    t.y = 4;
    if ((init_snake(s, h, t)) == -1) {
            fprintf(stderr, "init_snake(): failed initialization'\n");
            free(s);
            s = NULL;
            exit(1);
    }
    print_map(s);
    free(s);
    s = NULL;
    return 0;
 }

As a reminder provide an Minimal, Complete, and Verifiable example

Greg Price
  • 2,556
  • 1
  • 24
  • 33