Permalänk
Medlem

Game of life C

Håller på med en uppgift om Game of life (https://sv.wikipedia.org/wiki/Game_of_Life) i programspråket C. Min kod ser ut som följande och tror att problemet sitter i funktionen countNeighbour då jag tror att beräkningen av variabeln neighbour blir fel eftersom att jag får för många överlevande.

/* * Programmeringsteknik med C och Matlab * Fall 19 * Assignment 3 * File: ou3.c * Description: A simple implementation of Conway's Game of Life. * Author: Alva Granqvist * CS username: tfy19agt * Date: 191022 * Input: Choice of initial configuration and then instruction to step * or exit. * Output: Prints the game field in each step. * Limitations: No validation of input. */ #include <stdio.h> #include <stdlib.h> #include <time.h> /* Constants, representation of states */ #define ALIVE 'X' #define DEAD '.' /* Declaration of data structure */ typedef struct{ char current; char next; } cell; /* Declaration of functions */ void initField(const int rows, const int cols, cell field[rows][cols]); void loadGlider(const int rows, const int cols, cell field[rows][cols]); void loadSemaphore(const int rows, const int cols, cell field[rows][cols]); void loadRandom(const int rows, const int cols, cell field[rows][cols]); void loadCustom(const int rows, const int cols, cell field[rows][cols]); int randomise (void); void countNeighbour(const int rows, const int cols, cell field[rows][cols]); /* Function: main * Description: Start and run games, interact with the user. * Input: About what initial structure and whether to step or exit. * Output: Information to the user, and the game field in each step. */ int main(void) { const int rows = 20; const int cols = 20; cell field[rows][cols]; initField(rows, cols, field); do { for (int r = 0; r < rows; r++) { if (r !=0) { printf("\n"); } for (int c = 0; c < cols; c++) { if (field[r][c].current==ALIVE) { printf("X "); } else { printf(". "); } } } printf("\n"); printf("Select one of the following options:\n (enter) Step\n"); printf(" (any) Exit \n"); countNeighbour(rows, cols, field); } while (getchar() == '\n'); return 0; } /* Function: initField * Description: Initialize all the cells to dead, then asks the user about * which structure to load, and finally load the structure. * Input: The field array and its size. * Output: The field array is updated. */ void initField(const int rows, const int cols, cell field[rows][cols]) { for (int r = 0 ; r < rows ; r++) { for (int c = 0 ; c < cols ; c++) { field[r][c].current = DEAD; } } printf("Select field spec to load ([G]lider, [S]emaphore, [R]andom "); printf("or [C]ustom): "); int ch = getchar(); /* Ignore following newline */ if (ch != '\n') { getchar(); } switch (ch) { case 'g': case 'G': loadGlider(rows, cols, field); break; case 's': case 'S': loadSemaphore(rows, cols, field); break; case 'r': case 'R': loadRandom(rows, cols, field); break; case 'c': case 'C': default: loadCustom(rows, cols, field); break; } } /* Function: loadGlider * Description: Inserts a glider into the field. * Input: The field array and its size. * Output: The field array is updated. */ void loadGlider(const int rows, const int cols, cell field[rows][cols]) { field[0][1].current = ALIVE; field[1][2].current = ALIVE; field[2][0].current = ALIVE; field[2][1].current = ALIVE; field[2][2].current = ALIVE; } /* Function: loadSemaphore * Description: Inserts a semaphore into the field. * Input: The field array and its size. * Output: The field array is updated. */ void loadSemaphore(const int rows, const int cols, cell field[rows][cols]) { field[8][1].current = ALIVE; field[8][2].current = ALIVE; field[8][3].current = ALIVE; } /* Function: loadRandom * Description: Inserts a random structure into the field. * Input: The field array and its size. * Output: The field array is updated. There is a 50 % chance that a cell * is alive. */ void loadRandom(const int rows, const int cols, cell field[rows][cols]) { srand(time(NULL)); for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { if (randomise()==1) { field[r][c].current = ALIVE; } } } } /* Function: loadCustom * Description: Lets the user specify a structure that then is inserted into * the field. * Input: The field array and its size. * Output: The field array is updated. */ void loadCustom(const int rows, const int cols, cell field[rows][cols]) { printf("Give custom format string: "); do { int r, c; scanf("%d,%d", &r, &c); field[r][c].current = ALIVE; } while (getchar() != '\n'); } /* Function: randomise * Description: Randomises a number, 0 or 1. * Input: No input. * Output: The randomised number, 0 or 1. */ int randomise (void) { int random = rand() % 2; return random; } /* Function: countNeighbor * Description: Counts how many living naeigbours each cell has and determinates * if the cell will have a living or dead citicent in the next * generation. * Input: The field array and its size. * Output: The field array is updated. */ void countNeighbour(const int rows, const int cols, cell field[rows][cols]) { for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { int neighbours = 0; if (r == 0 || c == 0 || r == rows-1 || c == cols-1) { if (r == 0 ) { for (int rowNei = r; rowNei <= r+1; rowNei++) { for (int colNei = c-1; colNei <= c+1; colNei++) { if (field[rowNei][colNei].current == ALIVE) { neighbours += 1; } } } } if (c == 0) { for (int rowNei = r-1; rowNei <= r+1; rowNei++) { for (int colNei = c; colNei <= c+1; colNei++) { if (field[rowNei][colNei].current == ALIVE) { neighbours += 1; } } } } if (r == rows-1) { for (int rowNei = r-1; rowNei <= r; rowNei++) { for (int colNei = c-1; colNei <= c+1; colNei++) { if (field[rowNei][colNei].current == ALIVE) { neighbours += 1; } } } } if (c == cols-1) { for (int rowNei = r-1; rowNei <= r+1; rowNei++) { for (int colNei = c-1; colNei <= c; colNei++) { if (field[rowNei][colNei].current == ALIVE) { neighbours += 1; } } } } } else { for (int rowNei = r-1; rowNei <= r+1; rowNei++) { for (int colNei = c-1; colNei <= c+1; colNei++) { if (field[rowNei][colNei].current == ALIVE) { neighbours += 1; } } } } if (neighbours <= 1 || neighbours >=4) { field[r][c].current = DEAD; } if (neighbours == 2 || neighbours == 3) { field[r][c].current = ALIVE; } } } }

Permalänk
Medlem

Du måste specificerad exakt vad du har för problem. Ingen här är sugen på att felsöka din skoluppgift.
Lägg även koden i code-taggar så den blir lättläslig.

Permalänk
Medlem
Permalänk
Hedersmedlem

Jag tror att du skulle må bra av att förenkla countNeighbor så att den, givet ett "field", bredd, höjd, X-koordinat och Y-koordinate räknar antalet levande grannar.

Du kan inte uppdatera "field" direkt, eftersom då påverkar du ju antalet grannar i nästa cell. Lagra istället resultatet i en ny array!

Permalänk
Hedersmedlem

countNeighbor behöver absolut förenklas men du kan fundera på hur den funkar i utkanten av banan. Hur kollar du grannarna om r = 0 och c = 0? Är det rätt?

Kollade bara 30 sek men det såg inte rätt ut.

Permalänk
Medlem

Ett annat tips är att istället för att ha fem nästan identiska for-loopar i countNeighbor så skulle du få avsevärt mindre kod om du först räknar ut vilka rutor du ska leta efter grannar i och sen bara har en for-loop som gör det.

Permalänk
Medlem

Utöver att förenkla countNeighbour, fundera lite över funktionsindelning och rimliga funktionsnamn. countNeighbour borde endast räkna ut hur många grannar som finns, och endast för en cell, men den gör i princip allt för ett generationsskifte nu, och för alla celler på en gång.

Visa signatur

Redbox: Asrock B650 Lightning ATX, 7800x3D -20CCO, XFX 6950XT, 2x32GB Corsair Vengence 6400 CL32, WD SN770 2TB, Corsair RMe 1000, Lian Li Lancool 216, Peerless Assassin 120 SE
Purpbox: Z87-Pro, I5 4670K@4.2, Sapphire 290 TRI-X, 2x8GB Crucial Tactical@stock, Deep Silence 1
Samsung Evo 250+500GB + QVO 1TB, 2x1TB 7200RPM backup/lagring
Det var bättre förr: E5300 2600MHz -> 3640MHz, Celeron 300A -> 450MHz

Permalänk
99:e percentilen

Nu skriver jag nästan aldrig C, men för mig ser det väldigt konstigt ut att countNeighbours har den tomma typen, void, som returtyp. Borde inte returtypen rimligen vara int?

Visa signatur

Skrivet med hjälp av Better SweClockers

Permalänk
Datavetare

Verkar som du tänkt rätt när cell strukturen skapades, för ett sätt att gå till nästa generation är att initialt spara tillståndet för nya generationen i next för att som sista steg kopiera alla next till current. Nu använder du aldrig next.

Dock finns det betydligt mer lömska buggar i koden, bl.a. indexering med -1...

if (r == 0 || c == 0 || r == rows-1 || c == cols-1) { if (r == 0 ) { for (int rowNei = r; rowNei <= r+1; rowNei++) { for (int colNei = c-1; colNei <= c+1; colNei++) { if (field[rowNei][colNei].current == ALIVE) { neighbours += 1; } } } }

Misstaget här är att missa att c==0 kan vara sant samtidigt som r==0 är sant.

Bästa sättet att lösa just det problemet är att skapa något likt detta

char cellState(int rows, int cols, cell field[rows][cols], int r, int c) { if (r < 0 || c < 0 || r >= rows || c >= cols) { /* Outside world-box */ return DEAD; } return field[r][c].current; }

och sedan skapa en variant av countNeighbour() som dels faktiskt gör vad namnet säger (d.v.s. räknar antal grannar, men låter bli att gå till nästa generation) och dels använder cellState(). Antalet argument till cellState() visar att det inte skulle vara dumt att ha en typ som håller tillståndet på "världen", d.v.s. har koll på antal rader/kolumner samt tillståndet på alla celler. Men är helt upp till dig om det känns värt att fixa det här.

Kan även tipsa om att när du räknar antalet grannar, se till att inte räkna den cell vars grannar du försöker räkna (det är en annan bug som nuvarande kod har).

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Hedersmedlem

Det är inte OK att förstöra trådstarten när du hittar svar på din fråga. Det sabbar för andra som kan få hjälp av din tråd och det är respektlöst mot de som har försökt hjälpa dig.

Tråd låst.