titan

Up since 11/8/17 02:45 pm

eos

Up since 11/14/17 11:20 pm

rhea

Up since 10/17/17 05:40 pm

hpss

Up since 11/20/17 09:15 am

atlas1

Up since 11/15/17 07:25 am

atlas2

Up since 11/27/17 10:45 am
OLCF User Assistance Center

Can't find the information you need below? Need advice from a real person? We're here to help.

OLCF support consultants are available to respond to your emails and phone calls from 9:00 a.m. to 5:00 p.m. EST, Monday through Friday, exclusive of holidays. Emails received outside of regular support hours will be addressed the next business day.

CPU Game of Life

Contents

  1. GOL.c
  2. GOL.f90
 

Introduction

The GOL is an example of cellular automaton that utilizes a 2 dimensional stencil. For each game iteration the integer value of each cell in the 2D game grid is determined by summing it’s 8 closest neighbors and then applying the game rules, with the initial game state randomly generated. Each cell has two states, alive or dead, represented as an integer 1 or 0. Periodic boundary conditions are enforced through the use of ghost cells. Cell updates are not propagated through until the end of each iteration, leaving the board static during calculations.

If we wish to play GOL with a 3×3 grid to compensate for the periodic boundary conditions we will need to actually hold a 5×5 grid in memory. This would be the area inside of the blue dashed line in the image below. The cells that are not part of the ‘visible’ 3×3 grid but part of the 5×5 grid we will refer to as ghost cells.
GOL Game board

The full source can be viewed or download at the OLCF GitHub. Please direct any questions or comments to help@nccs.gov

GOL.c

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

#define SRAND_VALUE 1985

// Add up all neighbors and return the number of live neighbors
int getNeighbors(int** grid, int i, int j)
{
    int numNeighbors;
    numNeighbors = grid[i+1][j] + grid[i-1][j]     //lower upper
                 + grid[i][j+1] + grid[i][j-1]     //right left
                 + grid[i+1][j+1] + grid[i-1][j-1] //diagonals
                 + grid[i-1][j+1] + grid[i+1][j-1];

    return numNeighbors;
}

int main(int argc, char* argv[])
{
    int i,j,iter;
    // Linear game grid dimension
    int dim = 1024;
    // Number of game iterations
    int maxIter = 1024;

    // Allocate square grid of (dim+2)^2 elements, 2 added for ghost cells
    int **grid = (int**) malloc( sizeof(int*) * (dim+2) );
    for(i = 0; i<dim+2; i++)
        grid[i] = (int*) malloc( sizeof(int*) * (dim+2) );

    // Allocate newGrid
    int **newGrid = (int**) malloc( sizeof(int*) * (dim+2) );
    for(i = 0; i<dim+2; i++)
        newGrid[i] = (int*) malloc( sizeof(int*) * (dim+2) );

    // Assign initial population randomly
    srand(SRAND_VALUE);
    for(i = 1; i<=dim; i++) {
        for(j = 1; j<=dim; j++) {
            grid[i][j] = rand() % 2;
        }
    }

    // Main game loop
    for (iter = 0; iter<maxIter; iter++) {
        // Left-Right columns
        for (i = 1; i<=dim; i++) {
            grid[i][0] = grid[i][dim]; // Copy first real column to right ghost column
            grid[i][dim+1] = grid[i][1]; // Copy last real column to left ghost column
        }
        // Top-Bottom rows
        for (j = 0; j<=dim+1; j++) {
            grid[0][j] = grid[dim][j]; // Copy first real row to bottom ghost row
            grid[dim+1][j] = grid[1][j]; // Copy last real row to top ghost row
        }

        // Now we loop over all cells and determine their fate
        for (i = 1; i<=dim; i++) {
            for (j = 1; j<=dim; j++) {
                // Get the number of neighbors for a given grid point
                int numNeighbors = getNeighbors(grid, i, j);

                // Here we have explicitly all of the game rules
                if (grid[i][j] == 1 && numNeighbors < 2)
                    newGrid[i][j] = 0;
                else if (grid[i][j] == 1 && (numNeighbors == 2 || numNeighbors == 3))
                    newGrid[i][j] = 1;
                else if (grid[i][j] == 1 && numNeighbors > 3)
                    newGrid[i][j] = 0;
                else if (grid[i][j] == 0 && numNeighbors == 3)
                    newGrid[i][j] = 1;
                else
                    newGrid[i][j] = grid[i][j];
            }
        }

        // Done with one step so we swap our grids and iterate again
        int **tmpGrid = grid;
        grid = newGrid;
        newGrid = tmpGrid;
    }// End main game loop

    // Sum up alive cells and print results
    int total = 0;
    for (i = 1; i<=dim; i++) {
        for (j = 1; j<=dim; j++) {
            total += grid[i][j];
        }
    }
    printf("Total Alive: %d\n", total);

    // Release memory
    free(grid);
    free(newGrid);

    return 0;
}

Compiling:

$ cc GOL.c -o gol.out

Running:

$ aprun ./gol.out 
Total Alive: 45224

GOL.f90

! Add up all neighbors and return the number of live neighbors
integer function neighbors(grid, i, j)
    implicit none
    integer,intent(in) :: grid(:,:), i, j
    neighbors = grid(i,j+1) + grid(i,j-1)&    !right & left
              + grid(i+1,j) + grid(i-1,j)&    !lower & upper
              + grid(i+1,j+1) + grid(i-1,j-1)& !diagonals
              + grid(i-1,j+1) + grid(i+1,j-1)
    return
end function neighbors

program main
    implicit none
    interface
        integer function neighbors(grid, i, j)
            integer,intent(in) :: grid(:,:), i, j
        end function neighbors
    endinterface

    integer :: i,j,iter,seed(8),numNeighbors,total
    real :: randm
    ! Linear game grid dimension
    integer :: dim = 1024
    ! Number of game iterations
    integer :: maxIter = 2**10

    ! Game grid pointers
    integer,dimension(:,:),pointer :: grid, newGrid, tmpGrid

    ! Allocate square grid of (dim+2)^2 elements, 2 added for ghost cells
    allocate(grid(dim+2,dim+2))
    allocate(newGrid(dim+2,dim+2))

    ! Assign initial population randomly
    seed = (/1985, 2011, 2012, 500, 24, 15, 99, 8/)
    call random_seed(PUT=seed)
    do j=1,dim
        do i=1,dim
            call random_number(randm)
            grid(i,j) = nint(randm)
        enddo
    enddo

    ! Main game loop
    do iter=1,maxITer
        ! Top-Bottom ghost rows
        do j=2,dim+1
            grid(1,j) = grid(dim+1,j) !Copy first game grid row to bottom ghost row
            grid(dim+2,j) = grid(2,j) !Copy first game grid row to top ghost row
        enddo

        ! Left-Right ghost columns
        do i=1,dim+2
            grid(i,1) = grid(i, dim+1) !Copy first game grid column to right ghost column
            grid(i,dim+2) = grid(i,2)  !Copy last game grid column to left ghost column
        enddo

        ! Now we loop over all cells and determine their fate
        do j=2,dim+1
            do i=2,dim+1
                ! Get the number of neighbors for a given grid point
                numNeighbors = neighbors(grid, i ,j)

                ! Here we have explicitly all of the game rules
                if(grid(i,j) == 1 .AND. numNeighbors < 2) then
                    newGrid(i,j) = 0
                elseif(grid(i,j) == 1 .AND. (numNeighbors == 2 .OR. numNeighbors == 3)) then
                    newGrid(i,j) = 1
                elseif(grid(i,j) == 1 .AND. numNeighbors > 3) then
                    newGrid(i,j) = 0
                elseif(grid(i,j) == 0 .AND. numNeighbors == 3) then
                    newGrid(i,j) = 1
                else
                    newGrid(i,j) = grid(i,j)
                endif
            enddo
        enddo

        ! Done with one step so we swap our grids and iterate again
        tmpGrid => grid
        grid => newGrid
        newGrid => tmpGrid

    enddo! End main game loop  

    ! Sum up alive cells and print results
    total = 0
    do j=2,dim+1
        do i=2,dim+1
            total = total + grid(i,j)
        enddo
    enddo
    print *, "Total Alive", total

    ! Release memory
    deallocate(grid)
    deallocate(newGrid)

end program

Compiling:

$ ftn GOL.f90 -o GOL.out

Running:

$ aprun ./gol.out 
Total Alive        46464