简体   繁体   中英

Define 2D grid within a struct and accessing cells

I try to define a 2D array within a struct. Using an array it works well :

#include <stdio.h>
#define sizeY 10
#define sizeX 10
#define LIVE 1
#define DEAD 0

typedef int TableType[sizeY][sizeX];

void printTable(TableType grid) {
    int height, width;

    for (height = 0; height < sizeY; height++) {
        for (width = 0; width < sizeX; width++) {
            if (grid[height][width] == LIVE) {
                printf("X");
            } else {
                printf("-");
            }
        }
        printf("\n");
    }
    printf("\n");
}

void clearTable(TableType grid) {
    int height, width;
    for (height = 0; height < sizeY; height++) {
        for (width = 0; width < sizeX; width++) {
            grid[height][width] = DEAD;
        }
    }
}

int main(void) {
    TableType table;

    clearTable(table);
    printTable(table);

    return 0;
}

But using a struct I got an error using the code below :

||=== Build file: "no target" in "no project" (compiler: unknown) ===|
||In function 'printTable':|
|17|error: used struct type value where scalar is required|
|17|error: expected ')' before '{' token|
In function 'clearTable':|
|32|error: expected ';' before '{' token|
||=== Build failed: 3 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

This is another version of the code:

#include <stdio.h>
#define sizeY 10
#define sizeX 10
#define LIVE 1
#define DEAD 0

typedef struct {
    int y;
    int x;
} TableType;

void printTable(TableType grid) {
    int height, width;

    for (height = 0; height < sizeY; height++) {
        for (width = 0; width < sizeX; width++) {
            if (grid{height, width} == LIVE) {
                printf("X");
            } else {
                printf("-");
            }
        }
        printf("\n");
    }
    printf("\n");
}

void clearTable(TableType grid) {
    int height, width;
    for (height = 0; height < sizeY; height++) {
        for (width = 0; width < sizeX; width++) {
            grid{height, width} = DEAD;
        }
    }
}

int main(void) {
    TableType table;

    clearTable(table);
    printTable(table);

    return 0;
}

I suppose I do not use correctly my structure and would a lot appreciate any help achieving this, thanks a lot !


EDIT :

I update my primary request by giving more details on my course and what I try to achieve at the end. The final output is a game project called "Game Of Life".

I updated the code below with comments, describing what is Game Of Life, detailed key points with more functions and lots of commented code that show what's missing. I'm sorry to be blocked again but I get difficulties to manage cells usage, since new requirements (for example a cell must be also a struct).

Here is the updated code :

/*
 * The Game of Life
 * http://en.wikipedia.org/wiki/Conway's_Game_of_Life
 *
 * Key requirements :
 * - Limit the size of the world to 10x10 cells
 * - The world (grid) must be a struct composed by cells
 * - A cell must be a struct
 * - Each cell is in one of two possible states : Live or Dead
 * - Any live cell with fewer than two live neighbours dies, as if caused by under-population
 * - Any live cell with two or three live neighbours lives on to the next generation
 * - Any live cell with more than three live neighbours dies, as if by overcrowding
 * - Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction
 * - Between each generation, ask if it's necessary to restart the next generation or to leave the game
 * - Having dedicated function displayWorld() to display a representation of this world
 * - Having dedicated function newGeneration() to generate next step of the world
 * - In the function above, implement conditions based on cell's neighbors to define if each cell is alive or dead
 * - Alive cells are represented by a "+"
 * - Dead cells are represented by a "-"
 * - New borned cells are represented by a "0"
 */

#include <stdio.h>
#define sizeY 10
#define sizeX 10
#define LIVE 1
#define DEAD 0

// A world must be a struct
typedef struct {
  // @ToDo Missing: a cell must be a struct too
  // Means we have a world struct that includes a cell struct
  int y;
  int x;
  unsigned char status;
} World;

void displayWorld(World grid[], int table_size) {
  int i;
  for (i = 0; i < table_size; i++) {
    //printf ("P(%d,%d) = ", grid[i].x, grzid[i].y);

    if (i % sizeY == 0 && i != 0) {
      printf("\n");
    }

    // Missing a third state here;
    // When a cell become LIVE for the first time, it must be shown with a "0" char
    if (grid[i].status == LIVE) {
      printf("+");
    } else {
      printf("-");
    }
  }

  printf("\n");
}

void clearTable(World grid[], int table_size) {
  int i = 0;
  int height, width;
  for (height = 0; height < sizeY; height++) {
    for (width = 0; width < sizeX; width++) {
      grid[i].y = height;
      grid[i].x = width;
      grid[i].status = DEAD;
      //printf ("Setting P(%d,%d) to DEAD\n", width, height);
      i++;
    }
  }
}

int getNeighborValue(World grid[], int row, int col) {
    // if (row < 0 || row >= HEIGHT
    //  || col < 0 || col >= WIDTH
    //  || table[row][col] != LIVE )
    // {
    //  return 0;
    // } else {
    //  return 1;
    // }
}

// We count here how many neighbors a cell have
int getNeighborCount(World grid[], int row, int col) {
  int neighbor = 0;

  // This is not finished and will not work that way
  // neighbor += getNeighborValue(grid, row - 1, col - 1);
  // neighbor += getNeighborValue(grid, row - 1, col);
  // neighbor += getNeighborValue(grid, row - 1, col + 1);
  // neighbor += getNeighborValue(grid, row, col - 1);
  // neighbor += getNeighborValue(grid, row, col + 1);
  // neighbor += getNeighborValue(grid, row + 1, col - 1);
  // neighbor += getNeighborValue(grid, row + 1, col);
  // neighbor += getNeighborValue(grid, row + 1, col + 1);

  return neighbor;
}

// Here we define which cells become LIVE OR DEAD on next generation;
// based on cell's neighbors function above
void newGeneration(World gridA[], int table_size) {
  World gridB;
    int neighbor, height, width;

    // for (height = 0; height < table_size; height++) {
    //  for (width = 0; width < table_size; width++) {
    //      neighbor = getNeighborCount(gridA, height, width);
    //      if (neighbor==3) {
    //          gridB[height][width] = LIVE;
    //      } else if (neighbor == 2 && tableA[height][width] == LIFE_YES) {
    //          gridB[height][width] = LIVE;
    //      } else {
    //          gridB[height][width] = DEAD;
    //      }
    //  }
    // }
  //
    // for (height = 0; height < table_size; height++) {
    //  for (width = 0; width < table_size; width++) {
    //      gridA[height][width] = gridB[height][width];
    //  }
    // }
}

// Instead of using hardcoded loadStartData below, we ask the user to define organisms
void askUser(World grid[], int table_size) {
    // int i;
    // int n;
    // int height, width;
  //
    // printf("Enter the amount of initial organisms: ");
    // scanf("%d", &n);
    // for (i = 0; i < n; i++) {
    //  printf("Enter dimensions (x y) where organism %d will live: ", i + 1);
    //  scanf("%d %d", &height, &width);
    //  grid[height][width] = LIVE;
    // }
  //
    // displayWorld(grid);
    // printf("Generation 0");
}

// Here I will define starting LIFE_YES cells data called by main()
void loadStartData(World grid[], int table_size) {
  // for (i = 0; i < table_size; i++) {
  //   switch (grid) {
  //     Switch case the cells we want to set LIVE on it
  //     using findIndexForPoint function...
  //     case /* value */:
  //   }
  // }
}

int findIndexForPoint(World grid[], int table_size, int x, int y) {
  int i;
  for (i = 0; i < table_size; i++) {
    if (grid[i].x == x && grid[i].y == y) {
      // found it
      return i;
    }
  }

   // not found, return -1;
  return -1;
}

int main() {
  World grid[sizeX*sizeY] = {0};
  char end;
  int generation = 0;

  clearTable(grid, sizeX * sizeY);

  // Or we load hardcoded start data or we let the user chose ...
  // loadStartData(grid, sizeX * sizeY);
  // askUser(grid, sizeX * sizeY)

  // Keeping that example
  int index = findIndexForPoint(grid,sizeX*sizeY,5,3);
  if (index != -1) {
    grid[index].status = LIVE;
  }

  displayWorld(grid, sizeX*sizeY);

  do {
      //newGeneration(grid);
    //displayWorld(grid, sizeX * sizeY);
        printf("Generation %d\n", ++generation);
        printf("Press q to quit or 1 to continue: ");
        scanf(" %c", &end);
    } while (end != 'q') ;

    return 0;
}

Thanks anyway !

Since this seems your "homework" I'll use a very simplistic approach:

#include <stdio.h>
#define sizeY 10
#define sizeX 10
#define LIVE 1
#define DEAD 0

typedef struct {
    int y;
    int x;
    unsigned char status;
} TableType;


void printTable(TableType grid[], int table_size) {
    int i;
    for (i = 0; i < table_size; i++) {
      //printf ("P(%d,%d) = ", grid[i].x, grid[i].y); 

      if (i % sizeY == 0 && i != 0) {
          printf("\n");
      }

      if (grid[i].status == LIVE) {
          printf("X");
      } else {
          printf("-");
      }
    }
    printf("\n");
}

void clearTable(TableType grid[], int table_size) {
    int i = 0;
    int height, width;
    for (height = 0; height < sizeY; height++) {
        for (width = 0; width < sizeX; width++) {
            grid[i].y = height;
            grid[i].x = width;
            grid[i].status = DEAD;
            //printf ("Setting P(%d,%d) to DEAD\n", width, height);
            i++;
        }
    }
}

int findIndexForPoint(TableType grid[], int table_size, int x, int y) {
   int i;
   for (i = 0; i < table_size; i++) {
      if (grid[i].x == x && grid[i].y == y) {
         // found it
         return i;
      }
   }

   // not found, return -1;
   return -1;
}


int main () {
   TableType grid[sizeX*sizeY] = {0};
   clearTable(grid, sizeX*sizeY);

   // access via index but you are not sure which x,y
   grid[10].status = LIVE; 

   // This way you know exactly which x,y
   int index = findIndexForPoint (grid,sizeX*sizeY,5,3);
   if (index != -1) {
      grid[index].status = LIVE;
   }

   printTable(grid, sizeX*sizeY);
}

The table size is sizeX*sizeY and we are using this very special case where i try to re-use most of your code and supposing an,n Points "matrix". So, basically it means that its not optimized and if you mess with sizes of X and Y problems may arise.

As said, now you just have an array of 100 points (sizeX * sizeY) which you will initialize on clearTable. This function will set x,y values to whatever you want, in this case will be your n by n matrix (where n = 10).

printTable does the same but will print the values.

Compile and test it.. then adapt it to your needs.

Output:

----------
X---------
----------
-----X----
----------
----------
----------
----------
----------
----------

Confirm it here

EDIT:

Answer to your 2nd question... please don't do this again :/ There is room for improvements. Try to understand it!

/*
 * The Game of Life
 * http://en.wikipedia.org/wiki/Conway's_Game_of_Life
 *
 * Key requirements :
 * - Limit the size of the world to 10x10 cells
 * - The world (grid) must be a struct composed by cells
 * - A cell must be a struct
 * - Each cell is in one of two possible states : Live or Dead
 * - Any live cell with fewer than two live neighbours dies, as if caused by under-population
 * - Any live cell with two or three live neighbours lives on to the next generation
 * - Any live cell with more than three live neighbours dies, as if by overcrowding
 * - Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction
 * - Between each generation, ask if it's necessary to restart the next generation or to leave the game
 * - Having dedicated function displayWorld() to display a representation of this world
 * - Having dedicated function newGeneration() to generate next step of the world
 * - In the function above, implement conditions based on cell's neighbors to define if each cell is alive or dead
 * - Alive cells are represented by a "+"
 * - Dead cells are represented by a "-"
 * - New borned cells are represented by a "0"
 */

#include <stdio.h>
#include <stdlib.h>
#define WORLD_HEIGHT 10
#define WORLD_WIDTH  10

typedef enum {
   CELL_STATE_INVALID=-1,
   CELL_STATE_DEAD   = 0,
   CELL_STATE_LIVE   = 1,
   CELL_STATE_NEWBORN= 2,
   CELL_N_STATES
} CellStateType;

typedef struct _cell {
  // @ToDo Missing: a cell must be a struct too
  // Means we have a world struct that includes a cell struct
  int y;
  int x;
  unsigned char status;
} Cell;

typedef struct _world {
   int width;
   int height;
   int generation;
   Cell *grid;
} World;

// helper function to find for x,y cell on cell array
int findIndexForCell(World world, int x, int y) {
   int i;
   for (i = 0; i < world.width * world.height; i++) {
      if (world.grid[i].x == x && world.grid[i].y == y) {
         // found it, return indice
         return i;
      }
   }

   // not found, return -1;
   return -1;
}


// function will get cell status for given x, y
// returns invalid status if x,y outside world dimensions
CellStateType getCellStatus(World world, int x, int y) {
   int index;

   index = findIndexForCell(world, x, y);

   if (index == -1) {
      return CELL_STATE_INVALID;
   } else {
      return world.grid[index].status;
   }
}


// function will set cell status and return true or false
// if able to set status or not.
int setCellStatus(World world, int x, int y, CellStateType status) {
   int index;

   index = findIndexForCell(world, x, y);

   if (index == -1) {
      return 0;
   } else {
      world.grid[index].status = status;
      return 1;
   }
}


// Create a new world
World createWorld (int width, int height) {
   World world;

   world.width = width;
   world.height = height;
   world.generation = 0;

   // Content of grid is not initialized
   world.grid = (Cell *) malloc (sizeof(Cell) * (world.width * world.height));

   return world;
}


void displayWorld(World world) {
  int i;
  int size_world = world.width * world.height;

  for (i = 0; i < size_world; i++) {
     if (i % world.width == 0 && i != 0) {
        printf("\n");
     }

     // Missing a third state here;
     // When a cell become LIVE for the first time, it must be shown with a "0" char
     switch (world.grid[i].status) {
        case CELL_STATE_NEWBORN:
           printf("0");
           break;
        case CELL_STATE_DEAD:
           printf("-");
           break;
        case CELL_STATE_LIVE:
           printf("+");
           break;
        default:
           printf("?");
           break;
     }
  }

  printf("\n");
}


void initWorld(World world) {
  int x,y;
  int i = 0;

  world.generation = 0;

  for (y = 0; y < world.height; y++) {
    for (x = 0; x < world.width; x++) {
      world.grid[i].y = y;
      world.grid[i].x = x;
      world.grid[i].status = CELL_STATE_DEAD;
      i++;
    }
  }
}

int getNeighborValue(World world, int row, int col) {
     if (row < 0 || row >= world.height 
      || col < 0 || col >= world.width 
      || getCellStatus(world, row, col) < CELL_STATE_LIVE)
     {
      return CELL_STATE_DEAD;
     } else {
      return CELL_STATE_LIVE;
     }
}

// We count here how many neighbors a cell have
int getNeighborCount(World world, int row, int col) {
   int neighbor = 0;

   // This is not finished and will not work that way
   neighbor += getNeighborValue(world, row - 1, col - 1);
   neighbor += getNeighborValue(world, row - 1, col);
   neighbor += getNeighborValue(world, row - 1, col + 1);
   neighbor += getNeighborValue(world, row, col - 1);
   neighbor += getNeighborValue(world, row, col + 1);
   neighbor += getNeighborValue(world, row + 1, col - 1);
   neighbor += getNeighborValue(world, row + 1, col);
   neighbor += getNeighborValue(world, row + 1, col + 1);

   return neighbor;
}

// Here we define which cells become LIVE OR DEAD on next generation;
// based on cell's neighbors function above
World newGeneration(World world) {
   World temp;
   temp = createWorld(world.width, world.height);
   initWorld(temp);

   int neighbor, height, width;

   for (height = 0; height < world.height; height++) {
      for (width = 0; width < world.width; width++) {
         neighbor = getNeighborCount(world, width, height);
         if (getCellStatus(world, width, height) >= CELL_STATE_LIVE) {
            if (neighbor == 2 || neighbor == 3) {
               setCellStatus(temp, width, height, CELL_STATE_LIVE);
            } else {
               setCellStatus(temp, width, height, CELL_STATE_DEAD);
            }
         } else {
            if (neighbor == 3) {
               setCellStatus(temp, width, height, CELL_STATE_NEWBORN);
            }
         }
      }
   }

   return temp;
}

// Instead of using hardcoded loadStartData below, we ask the user to define organisms
void askUser(World world) {
   int i;
   int n;
   int height, width;

   printf("Enter the amount of initial organisms: ");
   scanf("%d", &n);
   for (i = 0; i < n; i++) {
      do {
         printf("Enter dimensions (x y) where organism %d will live: ", i + 1);
         scanf("%d %d", &width, &height);
      } while (setCellStatus(world, width, height, CELL_STATE_LIVE) == 0); 
   }
}

// Here I will define starting LIFE_YES cells data called by main()
void loadStartData(World world) {

  // Let's check the Beacon:
  // **
  // **
  //   **
  //   **
  setCellStatus(world, 2,2, CELL_STATE_LIVE);
  setCellStatus(world, 3,2, CELL_STATE_LIVE);
  setCellStatus(world, 2,3, CELL_STATE_LIVE);
  setCellStatus(world, 3,3, CELL_STATE_LIVE);

  setCellStatus(world, 4,4, CELL_STATE_LIVE);
  setCellStatus(world, 5,4, CELL_STATE_LIVE);
  setCellStatus(world, 4,5, CELL_STATE_LIVE);
  setCellStatus(world, 5,5, CELL_STATE_LIVE);
}



int main() {
   char end;
   World myworld;
   int generation = 0;

   myworld = createWorld(WORLD_WIDTH, WORLD_HEIGHT);   
   initWorld(myworld);

   // Or we load hardcoded start data or we let the user chose ...
   loadStartData(myworld);
   //askUser(myworld);

   while (end != 'q') {
      displayWorld(myworld);
      myworld = newGeneration(myworld);
      printf("Generation %d\n", ++generation);
      printf("Press q to quit or 1 to continue: ");
      scanf(" %c", &end);
   }

   return 0;
}

I think you're mixing things here. Why would you need an struct to solve a problem that can be solved with only an int array?

The first version of your code is good, but you define a global variable with a typedef that you don't need because int is a standard type. And then, in the main, delete the table you define because that type doesn't exist.


If you're forced to use an struct, then:

typedef struct {
    int y;
    int x;
    int is_dead;
} TableType;

You should add a variable with the cell's state.

Then, when you want to access to a cell state in the loops:

grid[height][width].is_dead

Also note that you're declaring two different things:

  • At the begining: typedef int TableType[sizeY][sizeX]; . That's incorrect. It should be TableType table[sizeY][sizeX]; .
  • At the main you should delete the table declaration because you're making a global variable, so you don't need it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM