2017-03-22 1 views
-1

간단히 설명하면 : 내 Game of Life 프로그램을 실행하면 빈 세포로 죽습니다. 단일 사이클에 대해 시뮬레이션을 실행하면 처리 할 수있는 살아있는 셀이 없으므로 빈 상태 여야합니다. 그러나, 내 프로그램을 실행하면 몇 가지 작은 죽은 세포가 오른쪽 하단 모서리에 '살아'가됩니다. 왜 이런지는 확실치 않지만 살아있는 세포 이웃을 확인하는 방법 때문인지 또는 세포를 표시하는 방법 때문인 것으로 믿습니다. 여기에 무슨 뜻인지의 사진입니다 Conway의 삶의 게임 : 빈/죽은 세포 (C/SDL2)만으로 프로그램을 실행할 때 외계 세포가 인쇄 됨

enter image description here

#include <SDL.h> 
#include <stdio.h> 
#include <stdbool.h> 

#define CELL_SIZE 10 
#define GRID_WIDTH 100 
#define GRID_HEIGHT 100 
#define SCREEN_WIDTH (GRID_WIDTH * CELL_SIZE) 
#define SCREEN_HEIGHT (GRID_HEIGHT * CELL_SIZE) 

typedef enum {ALIVE, DEAD} State; 
typedef struct{ 
    int x; 
    int y; 
    State state; 
} Cell; 

// SDL related functions 
SDL_Window *createWindow(char *title); 
SDL_Renderer *createRenderer(SDL_Window *window); 
void drawGrid(SDL_Renderer *r); 
void drawCells(SDL_Renderer *r, int a[][GRID_WIDTH]); 

// Game of Life functions 
void updateCells(int a[][GRID_WIDTH]); // takes cells array as input 
int countLivingNeighbours(int a[][GRID_WIDTH], int x, int y); 

int main(int argc, char *argv[]){ 
    // Initialise SDL 
    SDL_Init(SDL_INIT_VIDEO); 

    // Create window and renderer 
    SDL_Window *window = createWindow("Game of Life"); 
    SDL_Renderer *renderer = createRenderer(window); 

    // Setup event handling + mouse co-ordinate handling 
    SDL_Event event; 
    int mouseX, mouseY; 
    bool mouse_left_down = false; 
    bool mouse_right_down = false; 

    // Set all cells to initial state of dead 
    int cells[GRID_HEIGHT][GRID_WIDTH]; 
    int cx, cy; 
    for(cy = 0; cy < GRID_HEIGHT; cy++){ 
     for(cx = 0; cx < GRID_WIDTH; cx++){ 
      cells[cy][cx] = DEAD; 
     } 
    } 

    // MAIN LOOP // 
    while(1){ 
     // Handle events/input 
     while(SDL_PollEvent(&event) != 0){ 
      switch(event.type){ 
       case SDL_QUIT: // Check if user has quit 
        return 1; 

       // Check if user is HOLDING left or right mouse button 
       case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: 
        switch(event.button.button){ 
         case SDL_BUTTON_LEFT: mouse_left_down = !mouse_left_down; break; 
         case SDL_BUTTON_RIGHT: mouse_right_down = !mouse_right_down; break; 
        } 

       // If user presses space, simulate a single change 
       case SDL_KEYDOWN: 
        if(event.key.keysym.sym == SDLK_SPACE) 
         updateCells(cells); 

      } 
     } 

     // Get user mouse button input - left click gives life to cell at current co-ords, right click kills 
     SDL_GetMouseState(&mouseX, &mouseY); 
     if(mouse_left_down == true) 
      cells[mouseY/CELL_SIZE][mouseX/CELL_SIZE] = ALIVE; 
     else if(mouse_right_down == true) 
      cells[mouseY/CELL_SIZE][mouseX/CELL_SIZE] = DEAD; 

     // Set screen colour to white 
     SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); 

     // Render white to screen (clear screen) 
     SDL_RenderClear(renderer); 

     // Draw the grid and living cells 
     drawGrid(renderer); 
     drawCells(renderer, cells); 

     // Update screen 
     SDL_RenderPresent(renderer); 
    } 

    // Exit SDL and SDL_image 
    SDL_Quit(); 
    return 0; 
} 

/* 
1. Any live cell with fewer than two live neighbours dies, as if caused by underpopulation. 
2. Any live cell with two or three live neighbours lives on to the next generation. 
3. Any live cell with more than three live neighbours dies, as if by overpopulation. 
4. Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction. 
*/ 
void updateCells(int a[][GRID_WIDTH]){ 
    int new[GRID_HEIGHT][GRID_WIDTH]; 
    int cy, cx; // vertical count, horizontal count 

    for(cy = 0; cy < GRID_HEIGHT; cy++){ 
     for(cx = 0; cx < GRID_WIDTH; cx++){ 
      // Any live cell with fewer than two live neighbours dies, as if caused by underpopulation. 
      if(a[cy][cx] == ALIVE && countLivingNeighbours(a, cx, cy) < 2) 
       new[cy][cx] = DEAD; 

      // Any live cell with two or three live neighbours lives on to the next generation. 
      else if(a[cy][cx] == ALIVE && (countLivingNeighbours(a, cx, cy) == 2 || countLivingNeighbours(a, cx, cy) == 3)) 
       new[cy][cx] = ALIVE; 

      // Any live cell with more than three live neighbours dies, as if by overpopulation. 
      else if(a[cy][cx] == ALIVE && countLivingNeighbours(a, cx, cy) > 3) 
       new[cy][cx] = DEAD; 

      // Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction. 
      else if(a[cy][cx] == DEAD && countLivingNeighbours(a, cx, cy) == 3) 
       new[cy][cx] = ALIVE; 

      else 
       new[cy][cx] = DEAD; 
     } 
    } 

    // Update all cells into new states 
    for(cy = 0; cy < GRID_HEIGHT; cy++){ 
     for(cx = 0; cx < GRID_WIDTH; cx++){ 
      a[cy][cx] = new[cy][cx]; 
     } 
    } 
} 

// THERE'S NO ERROR CHECKING HERE WHICH IS BAD 
// Should ideally check if a cell even exists before checking its state 
int countLivingNeighbours(int a[][GRID_WIDTH], int x, int y){ 
    int count = 0, cx, cy; 

    for(cy = y - 1; cy <= y + 1; cy++){ 
     for(cx = x - 1; cx <= x + 1; cx++){ 

      // Ensure neighbouring cell is not out of bounds 
      if(!(cy < 0 || cx < 0 || cy > GRID_HEIGHT || cx > GRID_WIDTH)){ 
       // If there is a living neighbouring cell, add to count 
       if(a[cy][cx] == ALIVE) 
        count++; 

       // disregard current cell as it is not a neighbour 
       if(a[cy][cx] == ALIVE && cx == x && cy == y) 
        count--; 
      } 


     } 
    } 

    return count; 
} 

void drawGrid(SDL_Renderer *r){ 
    // Draw vertical grid lines 
    for(int v = CELL_SIZE; v < SCREEN_WIDTH; v += CELL_SIZE){ 
     // Set draw colour to grey 
     SDL_SetRenderDrawColor(r, 110, 110, 110, 110); 

     // Draw vertical line 
     SDL_RenderDrawLine(r, v, 0, v, SCREEN_HEIGHT); 
    } 

    // Draw horizontal grid lines 
    for(int h = CELL_SIZE; h < SCREEN_HEIGHT; h += CELL_SIZE){ 
     // Set draw colour to grey 
     SDL_SetRenderDrawColor(r, 110, 110, 110, 110); 

     // Draw horizontal line 
     SDL_RenderDrawLine(r, 0, h, SCREEN_WIDTH, h); 
    } 
} 

void drawCells(SDL_Renderer *r, int a[][GRID_WIDTH]){ 
    // Define cell width/height 
    SDL_Rect cellRect; 
    cellRect.w = CELL_SIZE + 1; // Same size as one cell +1 so it covers the grid line fully 
    cellRect.h = CELL_SIZE + 1; // Same size as one cell +1 so it covers the grid line fully 

    // Draw living cells 
    int cx, cy; 
    for(cy = 0; cy < GRID_HEIGHT; cy++){ 
     for(cx = 0; cx < GRID_WIDTH; cx++){ 
      if(a[cy][cx] == ALIVE){ 
       // Set cell x/y pos 
       cellRect.x = cx * CELL_SIZE; 
       cellRect.y = cy * CELL_SIZE; 

       SDL_SetRenderDrawColor(r, 0, 0, 0, 0); 
       SDL_RenderFillRect(r, &cellRect); 
      } 
     } 
    } 
} 

SDL_Window *createWindow(char *title){ 
    SDL_Window *window = SDL_CreateWindow(
     title,     // Title 
     SDL_WINDOWPOS_CENTERED, // Initial window x position 
     SDL_WINDOWPOS_CENTERED, // Initial window y position 
     SCREEN_WIDTH,   // Window Width 
     SCREEN_HEIGHT,   // Window Height 
     0      // Flags 
    ); 

    if(window == NULL){ 
     printf("Failed to create window. %s\n", SDL_GetError()); 
     exit(EXIT_FAILURE); 
    } 

    return window; 
} 

SDL_Renderer *createRenderer(SDL_Window *window){ 
    SDL_Renderer *renderer = SDL_CreateRenderer(
     window,      // Window 
     -1,       // Monitor index (-1 for first available) 
     SDL_RENDERER_ACCELERATED // Flags 
    ); 

    if(renderer == NULL){ 
     printf("Failed to create renderer. %s\n", SDL_GetError()); 
     exit(EXIT_FAILURE); 
    } 

    return renderer; 
} 

는 이상하게도, 이것은 단지 GRID_WIDTH 및 GRID_HEIGHT 더 높은 값으로 설정 될 때 일어날 것으로 보인다. GRID_WIDTH가 150으로 설정되었지만 둘 다 더 낮은 값 (30으로 테스트 됨)으로 설정하면 프로그램이 멈추거나 충돌하지만 모든 것이 원활하게 작동합니다.

어떤 도움이 많이 감사합니다 :)

+0

당신은 전혀 디버깅을 수행 했습니까? 이를 위해 디버거를 사용하십시오. 최소한 프로그램이 충돌하는 곳 또는 "걸려있을 때"무엇을하는지 알려줄 것입니다. 당신이 이미 그것을했다면, 당신이 찾은 것을 공유하십시오. – kaylum

+0

'셀 '은 로컬로 정의 된 배열입니다. 스택에 꼭 들어 맞을 수도 있습니다. –

+0

이 주제에 대한 귀하의 [이전 질문] (http://stackoverflow.com/questions/42955950/conways-game-of-life-cell-changes-being-calculated-incorrectly-after-changing)에서 발견 한 적이 있습니다. 플러스 또는 마이너스 1이 배열 경계를 나눕니 다. 네이버 카운트 기능을 다시 보지 않고 각 네이버마다 루프가없는 8 개의 명시적인 테스트를 수행해야합니다. –

답변

1
 // Ensure neighbouring cell is not out of bounds 
     if(!(cy < 0 || cx < 0 || cy > GRID_HEIGHT || cx > GRID_WIDTH)){ 

이 검사는 잘못된 것입니다. cy가 GRID_HEIGHT와 같거나 cx가 GRID_WIDTH와 같으면 여전히 범위를 벗어납니다. 이는 오른쪽 하단 셀의 좌표가 GRID_HEIGHT-1 및 GRID_WIDTH-1이기 때문입니다. 물론

, 그것은해야한다 :

 if(!(cy < 0 || cx < 0 || cy >= GRID_HEIGHT || cx >= GRID_WIDTH)){ 
+0

고마워요. 어떻게 그렇게 사소한 것을 놓쳤는지 모르겠군요. – Sato

관련 문제