The Relicans

Cover image for "A Game of Life" to be used in the real life.
Vladimir Ulogov
Vladimir Ulogov

Posted on

"A Game of Life" to be used in the real life.

Introduction

Famous "Game of Life" been an inspiration for a generations of mathematicians, computer programming geeks, scientists and people alike. But what about put this algorithm to a practical use ? By someone who do not holding a postdoctoral in mathematics... I already proposed a slightly modified rules, to help to make this algorithm to become a practical instrument, and now, I am proposing an implementation in Golang, which will make that you will be able to use that algorithm in your practical applications, written in Go.

Architecture

All elements of the Go module github.com/vulogov/GoLife implemented as a structures with interface functions to those structures.

GoLife

Implementation of the Cell

Each Cell is placed in the world and do have an X and Y coordinates in this world. Those coordinates could be obtained with interface functions X():int and Y():int. World do have a limited size and is convoluted. More about World later. In addition to the coordinates, each Cell have a name. Name is set with interface function SetName(string): and could be obtained with interface function Name():string. Name do not play any particular role in the game logic. You can set any name as string to a Cell. When world is created, all Cells in the World do have coordinates, but not a name. So, the name is an abstract, that is relevantly used outside of GoLife module.

Next and very important attribute is a status. This attribute holds value false if Cell is dead, or true if Cell is Live. The value of the status attribute could be obtained with an interface function Alive():bool. Attribute age and related function Age():int will give you access to the Age of the Cell. When Cell changes the status, Cell Age is reset to 0. Each step in the "Game of Life" increase Cell Age to 1.

Next group of attributes dedicated to the Cell immunity to an unfavorable condition. Attributes upImmunity:int and opImmunity:int are holding the current "under-population" and "over-population" immunities thresholds. If Cell is detected to be a subject to a under-population or over-population, it will not die immediately. Instead of that, with each step of the Game, value of ether opImmunity or upImmunity will be decreased to 1. If Cell is Live and one of those counters goes 0, Cell changes status to the Dead. Immunity assigned to the Cell during the process of bringing Cell to Life. Two random numbers are generated and there values are from 0 to maximum Immunity threshold, defined for the World. upImmGiven:int and opImmGiven:int are holding initial immunity given to the Cell when the Cell comes to Life. The purposes of those attributes are, if in the World we enable Cell recovery, then if Cell "health" been compromised by unfavorable condition, but did not become 0, which is threshold for Death, when Cell go back into a favorable condition, upImmunity:int and opImmunity:int will be increased to 1 with every step while Cell is in healthy condition, until those values will reached initial values defined in upImmGiven:int and opImmGiven:int.

The last pair of useful attribute/function is a degrading:bool and Degrading():bool*. If Cell is Live and in unfavorable condition, but immunity is not yet compromised, this function returns true, otherwise if Cell is Live and is in favorable condition, it returns false

Interface function Step():bool brings Cell to the next Step.

If Cell is Live and it's age reaches limit, which is set to the World, Cell will "die of old age" and become Dead.

Interface function String():string return a string representation of the Cell.

Implementation of the World.

World is a home for the matrix of the Cells. Each Cell is having X and Y coordinate in the world and Cell name plays no role in Game. World is convoluted or folded, "North" and "South" boundaries of the World are connected as well as "East" and "West". World defined as a structure with an interface functions.

First, World is having a name:string . Name of the World is reserved for a User to use and play no role in algorithm.

World do have a size with xMax:int and yMax:int. Interface functions X():int and Y():int are returning those values.

World do have an ageMax:int. This value is used to determine of what to do in case if Live Cell reaches that age. It will die. If Dead Cell reaches that Age, during the Step():bool, GoLife will call the function defined in procreateFunc:func () bool. If this function returns true, Cell will change from Dead to Live.

immunityMax:int bring an upper limit to a value assigned to a Cell immunity. Lower limit is 0.

age:int holds information on current age of the World. This value manifestoes in "now many times function Step() was called for the World"

stepFunc: func() is a reference to a function executed during each step.

NotificationCh: chan Cell is a channel for the Cell's. Every time when Cell changes status, GoLife sending it to this channel. Size of the channel defined by constant CHSIZE.

And now, few words about basic interface functions to the World:

  • Cell(int,int): (*Cell, error). This function returns a reference to a Cell defined by coordinates and error
  • GetCell(int,int): *Cell Unlike previous function, this one returns a reference to a Cell or nil.
  • Step(): This function calculates the next step for the World, by calculating Step():bool for each Cell in the World.
  • Print(): This debug function display printable representation of the World on STDOUT.

Two group of interface functions I would like to discuss separately. First group, represented by function Procreate(func ():bool): This function set the function to the world, that called in determination, shall the Dead Cell procreate Life. If function returns true, then Cell will change status from Dead to Live. Otherwise NOOP.

Second group, controls if the World, permits to the Cells recovery from unfavorable conditions. By default, it will not. If you call ImmunityRecovery():, then recovery will be permitted beginning next step. If you call NoImmunityRecovery():, then, it will not.

Use of the module

The use of the github.com/vulogov/GoLife module is very simple. First, you have to create a new World by calling package function GoLife.NewWorld(string,int,int,int,int): *World. This function will return a reference to the created World.

  • first parameter is a string with a name of the world
  • second and third, defines the x and y size of the World
  • fourth, defined a maximum Age
  • fifth, will set the maximum immunity threshold.

Example:

world := GoLife.NewWorld("world", 5, 5, 120, 10)
Enter fullscreen mode Exit fullscreen mode

This command, will create World with 25 cells organized in 5x5. Maximum age will be 120, upper immunity threshold will be 10.

By default, life procreation is disabled. You have to define a function, which will control procreation. Let's define a function, which will generate a random number between 1 and 10 and permits procreation if this number is 0. Example:

world.Procreate(func() bool {
    if rand.Intn(10) == 0 {
      return true
    }
    return false
})
Enter fullscreen mode Exit fullscreen mode

You can define this function ether inline, or import it from some other module.

Next, you better set some initial Cells to Live. Otherwise, you will be waiting for Procreation and if you do not set the Procreation function, your World will be desolate forever. Example:

world.ToLife(4,0)
world.ToLife(4,1)
world.ToLife(4,2)
world.ToLife(10,10)
Enter fullscreen mode Exit fullscreen mode

Note, if you will try to operate with Cells outside of the World boundaries, NOOP will happens.

And now, when everything is set, let's loop our world through the steps and see the Life evolving. You have to call Step(): function for the World and read information about changed Cells from the World.NotificationCh: chan Cell attribute. Example:

for {
    world.Print()
    world.Step()
    if len(world.NotificationCh) > 0 {
      fmt.Printf("World is changing on step [%v]\n", 
             world.Age())
      for len(world.NotificationCh) > 0 {
        cell := <- world.NotificationCh
        fmt.Println(cell.String())
      }
    }
}
Enter fullscreen mode Exit fullscreen mode

If everything is fine, you can enjoy watching the "Game of Life"
The Game of Life

Conclusion

This very simple module, will permit you to bring a power of modified Life algorithm into your application. Bring your application to Life and spread the joy. Hope, use of github.com/vulogov/GoLife will be fun and enjoyable. If you feel, that you can improve the code, author accepts pull requests. Report bugs, and be good !

Discussion (1)

Collapse
vulogov profile image
Vladimir Ulogov Author

For some reason, good portion of the article was missed during the publishing. I was recover a missed part and re-publish.