GOLang Game of Life Implementation












0














I decided to give Go a try and implemented a Game of Life Kata exercise in GOLang. I have no prior experience in Go and the majority of my experience comes from Java, C#, and Python. My code appears to be working as intended but not sure if I've implemented it in the GO way, or idiomatic Go.



I've seen a few Go examples online where the properties of the struct were always public, but that feels foreign coming from the object-orient world. The way I've implemented it the Board struct should never be able to get into an invalid state. I don't know if that is a mindset that is shared by the Go community.



I have a colleague who is big into Go and favors immutability and no side effects. I can see how that would be ideal for concurrency, but does the general Go community prefer avoiding mutations? As an example I could have implemented my Evolve method to return a new Board struct rather than mutate its state property.



Is there anything else that stands out as not being Go like?



package board

import (
"errors"
"math/rand"
"strconv"
"time"
)

const CellDead = 0
const CellAlive = 1

type board struct {
state int
rows int
columns int
}

/* Creates a new Board with the given dimensions. The dimensions, rows and columns,
must be positive integers greater than 0.
Returns a board populated with a random state. */
func NewRandomBoard(rows, columns int) (board, error) {

if rows < 1 || columns < 1 {
return board{}, errors.New("rows and columns must be a positive integer greater than 0")
}

initState := make(int, rows)
for i := range initState {
initState[i] = make(int, columns)
}

rand.Seed(time.Now().UnixNano())

// Populate random state
for i := range initState {
for j := range initState[i] {
initState[i][j] = rand.Intn((1 -0 + 1) + 0)
}
}

return board{state: initState, rows:rows, columns:columns}, nil
}

func NewBoard(initialState int) (board, error) {

if initialState == nil {
return board{}, errors.New("initialState cannot be nil")
}

if len(initialState) < 1 || len(initialState[0]) < 1 {
return board{}, errors.New("initialState must contain at least 1 row and 1 column")
}

colSize := len(initialState[0])
for i := 0; i < len(initialState); i++ {
if colSize != len(initialState[i]) {
return board{}, errors.New("initialState is a jagged 2D array, initialState cannot be jagged")
}
for j := 0; j < len(initialState[i]); j++ {
cellValue := initialState[i][j]
if cellValue < 0 || cellValue > 1 {
return board{}, errors.New("initialState may only contain values 0 or 1")
}
}
}

return board{state:initialState, rows: len(initialState), columns: len(initialState[0])}, nil
}

func (b *board) Evolve() {
newState := make(int, b.rows)
for i := range newState {
newState[i] = make(int, b.columns)
for j := range newState[i] {
newState[i][j] = nextStateForCell(b,i,j)
}
}
b.state = newState
}

func (b *board) State() int {
return b.state
}

func (b *board) Rows() int {
return b.rows
}

func (b *board) Columns() int {
return b.columns
}

func (b *board) PrettyPrint() {
for i := range b.state {
for j := range b.state[i] {
print(" " + strconv.Itoa(b.state[i][j]) + "")
}
println()
}
}

func nextStateForCell(b *board, i,j int) int {

neighborsAlive := 0
cellValue := b.state[i][j]
for x := -1; x <= 1; x++ {
for y := -1; y <= 1; y++ {
if i + x < 0 || i + x > (b.rows - 1) || y + j < 0 || y + j > (b.columns - 1) {
continue
}
neighborsAlive += b.state[i + x][y + j]
}
}
neighborsAlive -= cellValue

if cellValue == CellDead && neighborsAlive == 3 {
return CellAlive
} else if cellValue == CellAlive && (neighborsAlive < 2 || neighborsAlive > 3) {
return CellDead
} else {
return cellValue
}
}


The main file



package main

import (
"io.jkratz/katas/life/board"
)

func main() {

myBoard, err := board.NewRandomBoard(10, 10)
if err != nil {
panic("Failed to instantiate board")
}

myBoard.PrettyPrint()
println()
myBoard.Evolve()
myBoard.PrettyPrint()
}









share|improve this question



























    0














    I decided to give Go a try and implemented a Game of Life Kata exercise in GOLang. I have no prior experience in Go and the majority of my experience comes from Java, C#, and Python. My code appears to be working as intended but not sure if I've implemented it in the GO way, or idiomatic Go.



    I've seen a few Go examples online where the properties of the struct were always public, but that feels foreign coming from the object-orient world. The way I've implemented it the Board struct should never be able to get into an invalid state. I don't know if that is a mindset that is shared by the Go community.



    I have a colleague who is big into Go and favors immutability and no side effects. I can see how that would be ideal for concurrency, but does the general Go community prefer avoiding mutations? As an example I could have implemented my Evolve method to return a new Board struct rather than mutate its state property.



    Is there anything else that stands out as not being Go like?



    package board

    import (
    "errors"
    "math/rand"
    "strconv"
    "time"
    )

    const CellDead = 0
    const CellAlive = 1

    type board struct {
    state int
    rows int
    columns int
    }

    /* Creates a new Board with the given dimensions. The dimensions, rows and columns,
    must be positive integers greater than 0.
    Returns a board populated with a random state. */
    func NewRandomBoard(rows, columns int) (board, error) {

    if rows < 1 || columns < 1 {
    return board{}, errors.New("rows and columns must be a positive integer greater than 0")
    }

    initState := make(int, rows)
    for i := range initState {
    initState[i] = make(int, columns)
    }

    rand.Seed(time.Now().UnixNano())

    // Populate random state
    for i := range initState {
    for j := range initState[i] {
    initState[i][j] = rand.Intn((1 -0 + 1) + 0)
    }
    }

    return board{state: initState, rows:rows, columns:columns}, nil
    }

    func NewBoard(initialState int) (board, error) {

    if initialState == nil {
    return board{}, errors.New("initialState cannot be nil")
    }

    if len(initialState) < 1 || len(initialState[0]) < 1 {
    return board{}, errors.New("initialState must contain at least 1 row and 1 column")
    }

    colSize := len(initialState[0])
    for i := 0; i < len(initialState); i++ {
    if colSize != len(initialState[i]) {
    return board{}, errors.New("initialState is a jagged 2D array, initialState cannot be jagged")
    }
    for j := 0; j < len(initialState[i]); j++ {
    cellValue := initialState[i][j]
    if cellValue < 0 || cellValue > 1 {
    return board{}, errors.New("initialState may only contain values 0 or 1")
    }
    }
    }

    return board{state:initialState, rows: len(initialState), columns: len(initialState[0])}, nil
    }

    func (b *board) Evolve() {
    newState := make(int, b.rows)
    for i := range newState {
    newState[i] = make(int, b.columns)
    for j := range newState[i] {
    newState[i][j] = nextStateForCell(b,i,j)
    }
    }
    b.state = newState
    }

    func (b *board) State() int {
    return b.state
    }

    func (b *board) Rows() int {
    return b.rows
    }

    func (b *board) Columns() int {
    return b.columns
    }

    func (b *board) PrettyPrint() {
    for i := range b.state {
    for j := range b.state[i] {
    print(" " + strconv.Itoa(b.state[i][j]) + "")
    }
    println()
    }
    }

    func nextStateForCell(b *board, i,j int) int {

    neighborsAlive := 0
    cellValue := b.state[i][j]
    for x := -1; x <= 1; x++ {
    for y := -1; y <= 1; y++ {
    if i + x < 0 || i + x > (b.rows - 1) || y + j < 0 || y + j > (b.columns - 1) {
    continue
    }
    neighborsAlive += b.state[i + x][y + j]
    }
    }
    neighborsAlive -= cellValue

    if cellValue == CellDead && neighborsAlive == 3 {
    return CellAlive
    } else if cellValue == CellAlive && (neighborsAlive < 2 || neighborsAlive > 3) {
    return CellDead
    } else {
    return cellValue
    }
    }


    The main file



    package main

    import (
    "io.jkratz/katas/life/board"
    )

    func main() {

    myBoard, err := board.NewRandomBoard(10, 10)
    if err != nil {
    panic("Failed to instantiate board")
    }

    myBoard.PrettyPrint()
    println()
    myBoard.Evolve()
    myBoard.PrettyPrint()
    }









    share|improve this question

























      0












      0








      0







      I decided to give Go a try and implemented a Game of Life Kata exercise in GOLang. I have no prior experience in Go and the majority of my experience comes from Java, C#, and Python. My code appears to be working as intended but not sure if I've implemented it in the GO way, or idiomatic Go.



      I've seen a few Go examples online where the properties of the struct were always public, but that feels foreign coming from the object-orient world. The way I've implemented it the Board struct should never be able to get into an invalid state. I don't know if that is a mindset that is shared by the Go community.



      I have a colleague who is big into Go and favors immutability and no side effects. I can see how that would be ideal for concurrency, but does the general Go community prefer avoiding mutations? As an example I could have implemented my Evolve method to return a new Board struct rather than mutate its state property.



      Is there anything else that stands out as not being Go like?



      package board

      import (
      "errors"
      "math/rand"
      "strconv"
      "time"
      )

      const CellDead = 0
      const CellAlive = 1

      type board struct {
      state int
      rows int
      columns int
      }

      /* Creates a new Board with the given dimensions. The dimensions, rows and columns,
      must be positive integers greater than 0.
      Returns a board populated with a random state. */
      func NewRandomBoard(rows, columns int) (board, error) {

      if rows < 1 || columns < 1 {
      return board{}, errors.New("rows and columns must be a positive integer greater than 0")
      }

      initState := make(int, rows)
      for i := range initState {
      initState[i] = make(int, columns)
      }

      rand.Seed(time.Now().UnixNano())

      // Populate random state
      for i := range initState {
      for j := range initState[i] {
      initState[i][j] = rand.Intn((1 -0 + 1) + 0)
      }
      }

      return board{state: initState, rows:rows, columns:columns}, nil
      }

      func NewBoard(initialState int) (board, error) {

      if initialState == nil {
      return board{}, errors.New("initialState cannot be nil")
      }

      if len(initialState) < 1 || len(initialState[0]) < 1 {
      return board{}, errors.New("initialState must contain at least 1 row and 1 column")
      }

      colSize := len(initialState[0])
      for i := 0; i < len(initialState); i++ {
      if colSize != len(initialState[i]) {
      return board{}, errors.New("initialState is a jagged 2D array, initialState cannot be jagged")
      }
      for j := 0; j < len(initialState[i]); j++ {
      cellValue := initialState[i][j]
      if cellValue < 0 || cellValue > 1 {
      return board{}, errors.New("initialState may only contain values 0 or 1")
      }
      }
      }

      return board{state:initialState, rows: len(initialState), columns: len(initialState[0])}, nil
      }

      func (b *board) Evolve() {
      newState := make(int, b.rows)
      for i := range newState {
      newState[i] = make(int, b.columns)
      for j := range newState[i] {
      newState[i][j] = nextStateForCell(b,i,j)
      }
      }
      b.state = newState
      }

      func (b *board) State() int {
      return b.state
      }

      func (b *board) Rows() int {
      return b.rows
      }

      func (b *board) Columns() int {
      return b.columns
      }

      func (b *board) PrettyPrint() {
      for i := range b.state {
      for j := range b.state[i] {
      print(" " + strconv.Itoa(b.state[i][j]) + "")
      }
      println()
      }
      }

      func nextStateForCell(b *board, i,j int) int {

      neighborsAlive := 0
      cellValue := b.state[i][j]
      for x := -1; x <= 1; x++ {
      for y := -1; y <= 1; y++ {
      if i + x < 0 || i + x > (b.rows - 1) || y + j < 0 || y + j > (b.columns - 1) {
      continue
      }
      neighborsAlive += b.state[i + x][y + j]
      }
      }
      neighborsAlive -= cellValue

      if cellValue == CellDead && neighborsAlive == 3 {
      return CellAlive
      } else if cellValue == CellAlive && (neighborsAlive < 2 || neighborsAlive > 3) {
      return CellDead
      } else {
      return cellValue
      }
      }


      The main file



      package main

      import (
      "io.jkratz/katas/life/board"
      )

      func main() {

      myBoard, err := board.NewRandomBoard(10, 10)
      if err != nil {
      panic("Failed to instantiate board")
      }

      myBoard.PrettyPrint()
      println()
      myBoard.Evolve()
      myBoard.PrettyPrint()
      }









      share|improve this question













      I decided to give Go a try and implemented a Game of Life Kata exercise in GOLang. I have no prior experience in Go and the majority of my experience comes from Java, C#, and Python. My code appears to be working as intended but not sure if I've implemented it in the GO way, or idiomatic Go.



      I've seen a few Go examples online where the properties of the struct were always public, but that feels foreign coming from the object-orient world. The way I've implemented it the Board struct should never be able to get into an invalid state. I don't know if that is a mindset that is shared by the Go community.



      I have a colleague who is big into Go and favors immutability and no side effects. I can see how that would be ideal for concurrency, but does the general Go community prefer avoiding mutations? As an example I could have implemented my Evolve method to return a new Board struct rather than mutate its state property.



      Is there anything else that stands out as not being Go like?



      package board

      import (
      "errors"
      "math/rand"
      "strconv"
      "time"
      )

      const CellDead = 0
      const CellAlive = 1

      type board struct {
      state int
      rows int
      columns int
      }

      /* Creates a new Board with the given dimensions. The dimensions, rows and columns,
      must be positive integers greater than 0.
      Returns a board populated with a random state. */
      func NewRandomBoard(rows, columns int) (board, error) {

      if rows < 1 || columns < 1 {
      return board{}, errors.New("rows and columns must be a positive integer greater than 0")
      }

      initState := make(int, rows)
      for i := range initState {
      initState[i] = make(int, columns)
      }

      rand.Seed(time.Now().UnixNano())

      // Populate random state
      for i := range initState {
      for j := range initState[i] {
      initState[i][j] = rand.Intn((1 -0 + 1) + 0)
      }
      }

      return board{state: initState, rows:rows, columns:columns}, nil
      }

      func NewBoard(initialState int) (board, error) {

      if initialState == nil {
      return board{}, errors.New("initialState cannot be nil")
      }

      if len(initialState) < 1 || len(initialState[0]) < 1 {
      return board{}, errors.New("initialState must contain at least 1 row and 1 column")
      }

      colSize := len(initialState[0])
      for i := 0; i < len(initialState); i++ {
      if colSize != len(initialState[i]) {
      return board{}, errors.New("initialState is a jagged 2D array, initialState cannot be jagged")
      }
      for j := 0; j < len(initialState[i]); j++ {
      cellValue := initialState[i][j]
      if cellValue < 0 || cellValue > 1 {
      return board{}, errors.New("initialState may only contain values 0 or 1")
      }
      }
      }

      return board{state:initialState, rows: len(initialState), columns: len(initialState[0])}, nil
      }

      func (b *board) Evolve() {
      newState := make(int, b.rows)
      for i := range newState {
      newState[i] = make(int, b.columns)
      for j := range newState[i] {
      newState[i][j] = nextStateForCell(b,i,j)
      }
      }
      b.state = newState
      }

      func (b *board) State() int {
      return b.state
      }

      func (b *board) Rows() int {
      return b.rows
      }

      func (b *board) Columns() int {
      return b.columns
      }

      func (b *board) PrettyPrint() {
      for i := range b.state {
      for j := range b.state[i] {
      print(" " + strconv.Itoa(b.state[i][j]) + "")
      }
      println()
      }
      }

      func nextStateForCell(b *board, i,j int) int {

      neighborsAlive := 0
      cellValue := b.state[i][j]
      for x := -1; x <= 1; x++ {
      for y := -1; y <= 1; y++ {
      if i + x < 0 || i + x > (b.rows - 1) || y + j < 0 || y + j > (b.columns - 1) {
      continue
      }
      neighborsAlive += b.state[i + x][y + j]
      }
      }
      neighborsAlive -= cellValue

      if cellValue == CellDead && neighborsAlive == 3 {
      return CellAlive
      } else if cellValue == CellAlive && (neighborsAlive < 2 || neighborsAlive > 3) {
      return CellDead
      } else {
      return cellValue
      }
      }


      The main file



      package main

      import (
      "io.jkratz/katas/life/board"
      )

      func main() {

      myBoard, err := board.NewRandomBoard(10, 10)
      if err != nil {
      panic("Failed to instantiate board")
      }

      myBoard.PrettyPrint()
      println()
      myBoard.Evolve()
      myBoard.PrettyPrint()
      }






      go game-of-life






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked 20 mins ago









      jkratz55

      1133




      1133



























          active

          oldest

          votes











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210620%2fgolang-game-of-life-implementation%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown






























          active

          oldest

          votes













          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes
















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210620%2fgolang-game-of-life-implementation%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          404 Error Contact Form 7 ajax form submitting

          How to know if a Active Directory user can login interactively

          Refactoring coordinates for Minecraft Pi buildings written in Python