Conway's Game of Life F#Compact game of life implementationConway's Game of Life in F#Optimize Conway's Game...

How would one buy a used TIE Fighter or X-Wing?

Cryptic with missing capitals

Broken patches on a road

Difference between two quite-similar Terminal commands

Solving Fredholm Equation of the second kind

Eww, those bytes are gross

Is there any differences between "Gucken" and "Schauen"?

Quenching swords in dragon blood; why?

How to tag distinct options/entities without giving any an implicit priority or suggested order?

Checking for the existence of multiple directories

Can a person refuse a presidential pardon?

How to prevent users from executing commands through browser URL

The effects of magnetism in radio transmissions

Does Windows 10's telemetry include sending *.doc files if Word crashed?

Why don't American passenger airlines operate dedicated cargo flights any more?

How do I say "Brexit" in Latin?

Can an insurance company drop you after receiving a bill and refusing to pay?

Groups acting on trees

Why do members of Congress in committee hearings ask witnesses the same question multiple times?

How to deal with an incendiary email that was recalled

Can we use the stored gravitational potential energy of a building to produce power?

How to acknowledge an embarrassing job interview, now that I work directly with the interviewer?

How to explain planetary rings pulsating?

Book where aliens are selecting humans for food consumption



Conway's Game of Life F#


Compact game of life implementationConway's Game of Life in F#Optimize Conway's Game of LifeImplementing Conway's Game of LifeConway's Game of Life simulationGame of life in F#Conway's Game of LifeConway's Game of Life pythonSDL2.0 Conway's Game Of LifeConway's Game of Life classPython Conway's Game of life













8












$begingroup$


I am a C# developer learning F#. Would appreciate any suggestions on how this code can be improved. Main points I am interested in:




  • Functional style of the code, absense of imperative style(C#) artifacts

  • F# collection data types and functions usage

  • General code(not algorithm) efficiency


  • Code style, naming etc.



    let life board =
    let flatten = Seq.cast<int> >> Seq.toArray

    let neighboursCount (board: int[,]) x y =
    board.[y..y+2, x..x+2]
    |> flatten
    |> Array.mapi (fun i el -> (i <> 4, el))
    |> Array.filter fst |> Array.map snd
    |> Array.filter ((=) 1) |> Array.length

    let expandBoard board =
    let N = Array2D.length1 board
    array2D [|
    yield [| yield board.[N - 1, N - 1]; yield! board.[N - 1, *]; yield board.[N - 1, 0] |]
    for i in [0..N - 1] do yield [| yield board.[i, N - 1]; yield! board.[i, *]; yield board.[i, 0] |]
    yield [| yield board.[0, N - 1]; yield! board.[0, *]; yield board.[N - 1, 0] |]
    |]

    let getNextBoard board =
    let eBoard = expandBoard board
    board |> Array2D.mapi (fun i j elem ->
    match neighboursCount eBoard j i with
    | 2 -> elem
    | 3 -> 1
    | _ -> 0)

    let isGameOver board prevBoards =
    board |> flatten |> Array.forall ((=) 0) || List.contains board prevBoards

    let rec lifeRec board prevBoards =
    printfn "%A n" board
    if isGameOver board prevBoards
    then ()
    else getNextBoard board |> lifeRec <| board::prevBoards

    lifeRec board []

    let N = 5
    let rnd = System.Random()
    Array2D.init N N (fun _ _ -> rnd.Next 2) |> life











share|improve this question











$endgroup$




bumped to the homepage by Community 13 hours ago


This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.




















    8












    $begingroup$


    I am a C# developer learning F#. Would appreciate any suggestions on how this code can be improved. Main points I am interested in:




    • Functional style of the code, absense of imperative style(C#) artifacts

    • F# collection data types and functions usage

    • General code(not algorithm) efficiency


    • Code style, naming etc.



      let life board =
      let flatten = Seq.cast<int> >> Seq.toArray

      let neighboursCount (board: int[,]) x y =
      board.[y..y+2, x..x+2]
      |> flatten
      |> Array.mapi (fun i el -> (i <> 4, el))
      |> Array.filter fst |> Array.map snd
      |> Array.filter ((=) 1) |> Array.length

      let expandBoard board =
      let N = Array2D.length1 board
      array2D [|
      yield [| yield board.[N - 1, N - 1]; yield! board.[N - 1, *]; yield board.[N - 1, 0] |]
      for i in [0..N - 1] do yield [| yield board.[i, N - 1]; yield! board.[i, *]; yield board.[i, 0] |]
      yield [| yield board.[0, N - 1]; yield! board.[0, *]; yield board.[N - 1, 0] |]
      |]

      let getNextBoard board =
      let eBoard = expandBoard board
      board |> Array2D.mapi (fun i j elem ->
      match neighboursCount eBoard j i with
      | 2 -> elem
      | 3 -> 1
      | _ -> 0)

      let isGameOver board prevBoards =
      board |> flatten |> Array.forall ((=) 0) || List.contains board prevBoards

      let rec lifeRec board prevBoards =
      printfn "%A n" board
      if isGameOver board prevBoards
      then ()
      else getNextBoard board |> lifeRec <| board::prevBoards

      lifeRec board []

      let N = 5
      let rnd = System.Random()
      Array2D.init N N (fun _ _ -> rnd.Next 2) |> life











    share|improve this question











    $endgroup$




    bumped to the homepage by Community 13 hours ago


    This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.


















      8












      8








      8





      $begingroup$


      I am a C# developer learning F#. Would appreciate any suggestions on how this code can be improved. Main points I am interested in:




      • Functional style of the code, absense of imperative style(C#) artifacts

      • F# collection data types and functions usage

      • General code(not algorithm) efficiency


      • Code style, naming etc.



        let life board =
        let flatten = Seq.cast<int> >> Seq.toArray

        let neighboursCount (board: int[,]) x y =
        board.[y..y+2, x..x+2]
        |> flatten
        |> Array.mapi (fun i el -> (i <> 4, el))
        |> Array.filter fst |> Array.map snd
        |> Array.filter ((=) 1) |> Array.length

        let expandBoard board =
        let N = Array2D.length1 board
        array2D [|
        yield [| yield board.[N - 1, N - 1]; yield! board.[N - 1, *]; yield board.[N - 1, 0] |]
        for i in [0..N - 1] do yield [| yield board.[i, N - 1]; yield! board.[i, *]; yield board.[i, 0] |]
        yield [| yield board.[0, N - 1]; yield! board.[0, *]; yield board.[N - 1, 0] |]
        |]

        let getNextBoard board =
        let eBoard = expandBoard board
        board |> Array2D.mapi (fun i j elem ->
        match neighboursCount eBoard j i with
        | 2 -> elem
        | 3 -> 1
        | _ -> 0)

        let isGameOver board prevBoards =
        board |> flatten |> Array.forall ((=) 0) || List.contains board prevBoards

        let rec lifeRec board prevBoards =
        printfn "%A n" board
        if isGameOver board prevBoards
        then ()
        else getNextBoard board |> lifeRec <| board::prevBoards

        lifeRec board []

        let N = 5
        let rnd = System.Random()
        Array2D.init N N (fun _ _ -> rnd.Next 2) |> life











      share|improve this question











      $endgroup$




      I am a C# developer learning F#. Would appreciate any suggestions on how this code can be improved. Main points I am interested in:




      • Functional style of the code, absense of imperative style(C#) artifacts

      • F# collection data types and functions usage

      • General code(not algorithm) efficiency


      • Code style, naming etc.



        let life board =
        let flatten = Seq.cast<int> >> Seq.toArray

        let neighboursCount (board: int[,]) x y =
        board.[y..y+2, x..x+2]
        |> flatten
        |> Array.mapi (fun i el -> (i <> 4, el))
        |> Array.filter fst |> Array.map snd
        |> Array.filter ((=) 1) |> Array.length

        let expandBoard board =
        let N = Array2D.length1 board
        array2D [|
        yield [| yield board.[N - 1, N - 1]; yield! board.[N - 1, *]; yield board.[N - 1, 0] |]
        for i in [0..N - 1] do yield [| yield board.[i, N - 1]; yield! board.[i, *]; yield board.[i, 0] |]
        yield [| yield board.[0, N - 1]; yield! board.[0, *]; yield board.[N - 1, 0] |]
        |]

        let getNextBoard board =
        let eBoard = expandBoard board
        board |> Array2D.mapi (fun i j elem ->
        match neighboursCount eBoard j i with
        | 2 -> elem
        | 3 -> 1
        | _ -> 0)

        let isGameOver board prevBoards =
        board |> flatten |> Array.forall ((=) 0) || List.contains board prevBoards

        let rec lifeRec board prevBoards =
        printfn "%A n" board
        if isGameOver board prevBoards
        then ()
        else getNextBoard board |> lifeRec <| board::prevBoards

        lifeRec board []

        let N = 5
        let rnd = System.Random()
        Array2D.init N N (fun _ _ -> rnd.Next 2) |> life








      f# game-of-life






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Dec 13 '18 at 10:33







      Andrew Boklashko

















      asked Nov 17 '18 at 20:23









      Andrew BoklashkoAndrew Boklashko

      412




      412





      bumped to the homepage by Community 13 hours ago


      This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.







      bumped to the homepage by Community 13 hours ago


      This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
























          2 Answers
          2






          active

          oldest

          votes


















          1












          $begingroup$

          This is a most elegant solution. I have two small improvements:





          let neighboursCount (board: int[,]) x y =
          board.[y..y+2, x..x+2]
          |> flatten
          |> Array.indexed
          |> Array.sumBy (fun (i, n) -> if i <> 4 then n else 0)




          let expandBoard board =
          let N = (Array2D.length1 board) - 1
          array2D [|
          yield [| yield board.[N, N]; yield! board.[N, *]; yield board.[N, 0] |]
          for i in [0..N] do yield [| yield board.[i, N]; yield! board.[i, *]; yield board.[i, 0] |]
          yield [| yield board.[0, N]; yield! board.[0, *]; yield board.[N, 0] |]
          |]


          Here is N set to (Array2D.length1 board) - 1 because that's the value you're using through out the function.






          share|improve this answer









          $endgroup$





















            0












            $begingroup$

            Review for the updated version (because there are no answers yet, I think you can still remove the first version).



            I really like your implementation. Especially the idea of expanding the board instead of calculating the opposide indices as i did). That makes the code much more readable / understandable. I also like the way you imlemented the neighboursCount function - the combination of mapi, filter and map is a good example for functional programming.



            In fact, I don't see much room for improvement.



            Maybe you could rename neighboursCount into countNeighbours an move the board to the end of the argument list so it can be used like: eBorad |> countNeighbours j i



            Within the isGameOver function, I think I would prefer writing prevBoards |> List.contains board over List.contains board prevBoards.



            Thanks for sharing your solution :).






            share|improve this answer









            $endgroup$













            • $begingroup$
              I believe neighboursCount came from my OO background, where you name object properties like that. Also that is a bit unusual that I can use piping where it seems to be unneded, but in fact version with piping is much more readable, so I accept your point.
              $endgroup$
              – Andrew Boklashko
              Dec 13 '18 at 10:29











            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%2f207879%2fconways-game-of-life-f%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            1












            $begingroup$

            This is a most elegant solution. I have two small improvements:





            let neighboursCount (board: int[,]) x y =
            board.[y..y+2, x..x+2]
            |> flatten
            |> Array.indexed
            |> Array.sumBy (fun (i, n) -> if i <> 4 then n else 0)




            let expandBoard board =
            let N = (Array2D.length1 board) - 1
            array2D [|
            yield [| yield board.[N, N]; yield! board.[N, *]; yield board.[N, 0] |]
            for i in [0..N] do yield [| yield board.[i, N]; yield! board.[i, *]; yield board.[i, 0] |]
            yield [| yield board.[0, N]; yield! board.[0, *]; yield board.[N, 0] |]
            |]


            Here is N set to (Array2D.length1 board) - 1 because that's the value you're using through out the function.






            share|improve this answer









            $endgroup$


















              1












              $begingroup$

              This is a most elegant solution. I have two small improvements:





              let neighboursCount (board: int[,]) x y =
              board.[y..y+2, x..x+2]
              |> flatten
              |> Array.indexed
              |> Array.sumBy (fun (i, n) -> if i <> 4 then n else 0)




              let expandBoard board =
              let N = (Array2D.length1 board) - 1
              array2D [|
              yield [| yield board.[N, N]; yield! board.[N, *]; yield board.[N, 0] |]
              for i in [0..N] do yield [| yield board.[i, N]; yield! board.[i, *]; yield board.[i, 0] |]
              yield [| yield board.[0, N]; yield! board.[0, *]; yield board.[N, 0] |]
              |]


              Here is N set to (Array2D.length1 board) - 1 because that's the value you're using through out the function.






              share|improve this answer









              $endgroup$
















                1












                1








                1





                $begingroup$

                This is a most elegant solution. I have two small improvements:





                let neighboursCount (board: int[,]) x y =
                board.[y..y+2, x..x+2]
                |> flatten
                |> Array.indexed
                |> Array.sumBy (fun (i, n) -> if i <> 4 then n else 0)




                let expandBoard board =
                let N = (Array2D.length1 board) - 1
                array2D [|
                yield [| yield board.[N, N]; yield! board.[N, *]; yield board.[N, 0] |]
                for i in [0..N] do yield [| yield board.[i, N]; yield! board.[i, *]; yield board.[i, 0] |]
                yield [| yield board.[0, N]; yield! board.[0, *]; yield board.[N, 0] |]
                |]


                Here is N set to (Array2D.length1 board) - 1 because that's the value you're using through out the function.






                share|improve this answer









                $endgroup$



                This is a most elegant solution. I have two small improvements:





                let neighboursCount (board: int[,]) x y =
                board.[y..y+2, x..x+2]
                |> flatten
                |> Array.indexed
                |> Array.sumBy (fun (i, n) -> if i <> 4 then n else 0)




                let expandBoard board =
                let N = (Array2D.length1 board) - 1
                array2D [|
                yield [| yield board.[N, N]; yield! board.[N, *]; yield board.[N, 0] |]
                for i in [0..N] do yield [| yield board.[i, N]; yield! board.[i, *]; yield board.[i, 0] |]
                yield [| yield board.[0, N]; yield! board.[0, *]; yield board.[N, 0] |]
                |]


                Here is N set to (Array2D.length1 board) - 1 because that's the value you're using through out the function.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Jan 30 at 14:19









                Henrik HansenHenrik Hansen

                7,49511128




                7,49511128

























                    0












                    $begingroup$

                    Review for the updated version (because there are no answers yet, I think you can still remove the first version).



                    I really like your implementation. Especially the idea of expanding the board instead of calculating the opposide indices as i did). That makes the code much more readable / understandable. I also like the way you imlemented the neighboursCount function - the combination of mapi, filter and map is a good example for functional programming.



                    In fact, I don't see much room for improvement.



                    Maybe you could rename neighboursCount into countNeighbours an move the board to the end of the argument list so it can be used like: eBorad |> countNeighbours j i



                    Within the isGameOver function, I think I would prefer writing prevBoards |> List.contains board over List.contains board prevBoards.



                    Thanks for sharing your solution :).






                    share|improve this answer









                    $endgroup$













                    • $begingroup$
                      I believe neighboursCount came from my OO background, where you name object properties like that. Also that is a bit unusual that I can use piping where it seems to be unneded, but in fact version with piping is much more readable, so I accept your point.
                      $endgroup$
                      – Andrew Boklashko
                      Dec 13 '18 at 10:29
















                    0












                    $begingroup$

                    Review for the updated version (because there are no answers yet, I think you can still remove the first version).



                    I really like your implementation. Especially the idea of expanding the board instead of calculating the opposide indices as i did). That makes the code much more readable / understandable. I also like the way you imlemented the neighboursCount function - the combination of mapi, filter and map is a good example for functional programming.



                    In fact, I don't see much room for improvement.



                    Maybe you could rename neighboursCount into countNeighbours an move the board to the end of the argument list so it can be used like: eBorad |> countNeighbours j i



                    Within the isGameOver function, I think I would prefer writing prevBoards |> List.contains board over List.contains board prevBoards.



                    Thanks for sharing your solution :).






                    share|improve this answer









                    $endgroup$













                    • $begingroup$
                      I believe neighboursCount came from my OO background, where you name object properties like that. Also that is a bit unusual that I can use piping where it seems to be unneded, but in fact version with piping is much more readable, so I accept your point.
                      $endgroup$
                      – Andrew Boklashko
                      Dec 13 '18 at 10:29














                    0












                    0








                    0





                    $begingroup$

                    Review for the updated version (because there are no answers yet, I think you can still remove the first version).



                    I really like your implementation. Especially the idea of expanding the board instead of calculating the opposide indices as i did). That makes the code much more readable / understandable. I also like the way you imlemented the neighboursCount function - the combination of mapi, filter and map is a good example for functional programming.



                    In fact, I don't see much room for improvement.



                    Maybe you could rename neighboursCount into countNeighbours an move the board to the end of the argument list so it can be used like: eBorad |> countNeighbours j i



                    Within the isGameOver function, I think I would prefer writing prevBoards |> List.contains board over List.contains board prevBoards.



                    Thanks for sharing your solution :).






                    share|improve this answer









                    $endgroup$



                    Review for the updated version (because there are no answers yet, I think you can still remove the first version).



                    I really like your implementation. Especially the idea of expanding the board instead of calculating the opposide indices as i did). That makes the code much more readable / understandable. I also like the way you imlemented the neighboursCount function - the combination of mapi, filter and map is a good example for functional programming.



                    In fact, I don't see much room for improvement.



                    Maybe you could rename neighboursCount into countNeighbours an move the board to the end of the argument list so it can be used like: eBorad |> countNeighbours j i



                    Within the isGameOver function, I think I would prefer writing prevBoards |> List.contains board over List.contains board prevBoards.



                    Thanks for sharing your solution :).







                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered Dec 12 '18 at 8:57









                    JanDotNetJanDotNet

                    6,9131339




                    6,9131339












                    • $begingroup$
                      I believe neighboursCount came from my OO background, where you name object properties like that. Also that is a bit unusual that I can use piping where it seems to be unneded, but in fact version with piping is much more readable, so I accept your point.
                      $endgroup$
                      – Andrew Boklashko
                      Dec 13 '18 at 10:29


















                    • $begingroup$
                      I believe neighboursCount came from my OO background, where you name object properties like that. Also that is a bit unusual that I can use piping where it seems to be unneded, but in fact version with piping is much more readable, so I accept your point.
                      $endgroup$
                      – Andrew Boklashko
                      Dec 13 '18 at 10:29
















                    $begingroup$
                    I believe neighboursCount came from my OO background, where you name object properties like that. Also that is a bit unusual that I can use piping where it seems to be unneded, but in fact version with piping is much more readable, so I accept your point.
                    $endgroup$
                    – Andrew Boklashko
                    Dec 13 '18 at 10:29




                    $begingroup$
                    I believe neighboursCount came from my OO background, where you name object properties like that. Also that is a bit unusual that I can use piping where it seems to be unneded, but in fact version with piping is much more readable, so I accept your point.
                    $endgroup$
                    – Andrew Boklashko
                    Dec 13 '18 at 10:29


















                    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.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f207879%2fconways-game-of-life-f%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

                    is 'sed' thread safeWhat should someone know about using Python scripts in the shell?Nexenta bash script uses...

                    How do i solve the “ No module named 'mlxtend' ” issue on Jupyter?

                    Pilgersdorf Inhaltsverzeichnis Geografie | Geschichte | Bevölkerungsentwicklung | Politik | Kultur...