# Double Snake - Elm game

**URL:** <https://discourse.elm-lang.org/t/double-snake-elm-game/6737>\
**Category:** Show and Tell\
**Created:** [January 11, 2021, 4:32am UTC](https://discourse.elm-lang.org/t/double-snake-elm-game/6737 "2021-01-11T04:32:15Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![FrancoisConstant](https://avatars.discourse-cdn.com/v4/letter/f/7ba0ec/32.png) [@FrancoisConstant](https://discourse.elm-lang.org/u/FrancoisConstant)\
**Post date:** [January 11, 2021, 4:32am UTC](https://discourse.elm-lang.org/t/double-snake-elm-game/6737/1 "2021-01-11T04:32:15Z")

</div>

I’ve used Elm at work a bit and I thought that language would be perfect for video games so I gave it a shot.

 ![double-snake-screenshot](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/2X/5/57fa0d0c510be0920c3699e7cf41094519a43fb6.jpeg)

Code is here:

> <https://github.com/FrancoisConstant/elm-double-snake/blob/main/src/Main.elm>

It’s playable here:  
[https://www.eatsomecode.com/games/double-snake/](https://www.eatsomecode.com/games/double-snake/)

I’m happy to get any feedback - especially regarding game development. I think the code looks OK except for the doUpdateFrame function (maybe there are some patterns I should learn about).

_The CSS is hacky and not important though._

---

<div class="post-metadata">

**Author:** ![DullBananas](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dullbananas/32/3709_2.png) [@DullBananas](https://discourse.elm-lang.org/u/DullBananas)\
**Post date:** [January 11, 2021, 3:16pm UTC](https://discourse.elm-lang.org/t/double-snake-elm-game/6737/2 "2021-01-11T15:16:20Z")

</div>

These should not be all caps:

```elm
type Game
    = NOT_STARTED
    | WIP
    | PAUSED
    | GAME_OVER

type Direction
    = UP
    | RIGHT
    | DOWN
    | LEFT

type Key
    = SPACE
    | DIRECTION Direction

```

Replacing `DIRECTION` with `Direction` will cause name conflict, so use `Arrow`.

---

<div class="post-metadata">

**Author:** ![DullBananas](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dullbananas/32/3709_2.png) [@DullBananas](https://discourse.elm-lang.org/u/DullBananas)\
**Post date:** [January 11, 2021, 4:10pm UTC](https://discourse.elm-lang.org/t/double-snake-elm-game/6737/3 "2021-01-11T16:10:56Z")

</div>

Also, you should implement touchscreen controls. You can use simple buttons, or you can implement swipe gestures ([dullbananas/elm-touch](https://elm.dmy.fr/packages/dullbananas/elm-touch/latest/) might help with this).

---

<div class="post-metadata">

**Author:** ![allanderek](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/allanderek/32/1360_2.png) [@allanderek](https://discourse.elm-lang.org/u/allanderek)\
**Post date:** [January 11, 2021, 6:16pm UTC](https://discourse.elm-lang.org/t/double-snake-elm-game/6737/4 "2021-01-11T18:16:08Z")

</div>

For the most part I think your Elm is entirely fine, I’d be fairly happy receiving this sort of code and being asked to work on it. It’s the nature of feedback that it’s mostly negative so bear in mind that I think generally this code is of pretty good quality.

Your source code comments are mostly useless. You have a lot of this kind of thing:

```elm
--
-- MAIN
--

main : Program () Model Msg

```

even worse:

```elm
--
-- SUBSCRIPTIONS
-- (new frames & new user inputs)
--

subscriptions _ =
    Sub.batch <|
        [ Browser.Events.onKeyDown (Decode.map KeyPushed keyDecoder)
        , Browser.Events.onAnimationFrameDelta Frame
        ]

```

Note, if you **really** think this requires documentation, how about:

```elm
subscriptions _ =
    let 
          userInput =
              Browser.Events.onKeyDown (Decode.map KeyPushed keyDecoder)
          newFrames =
              Browser.Events.onAnimationFrameDelta Frame
    in
    Sub.batch <|
        [ userInput
        , newFrames
        ]

```

All I’m doing here is translating your comments into executable code that will get checked by the compiler.

You might think the following is helpful:

```elm
--
-- UTILS
--

asSnakeIn : Model -> Snake -> Model
asSnakeIn model snake =
    { model | snake = snake }

```

I don’t think I’ve ever once thought “I wonder where all the utility functions are?” in code that has omitted such a comment. I’m usually looking for a particular function and I can just do that by searching for it. If you really think that these functions deserve to be grouped together somehow, how about actually using the module system? In this case, I can see why you wouldn’t because making a ‘Utils’ module would necessitate also factoring out some of the types. This is also a pretty small project so to me it feels right that it’s all in a single file. This sort of situation makes me pine for syntactic modules that do not necessarily correspond to a file, but that’s digressing somewhat. The point is your `UTILS` comment is more distracting than useful.

Here’s another one that looks useful:

```elm
{- Making sure the player cannot reverse the direction.

-}

canUpdateDirection : Direction -> List Direction -> Bool
canUpdateDirection newDirection directions =

```

Just rename the function `preventDirectionReverse` or have that as a `let` introduced condition:

```elm
canUpdateDirection : Direction -> List Direction -> Bool
canUpdateDirection newDirection directions =
    let
        latestDirection =
            List.reverse directions |> List.head |> Maybe.withDefault LEFT
        isDirectionReverse =
              (newDirection == UP && latestDirection == DOWN)
           || (newDirection == DOWN && latestDirection == UP)
           || (newDirection == LEFT && latestDirection == RIGHT)
           || (newDirection == RIGHT && latestDirection == LEFT)

    in
    (newDirection /= latestDirection)
        && not isDirectionReverse

```

Note I might write that as:

```elm
isDirectionReverse =
   List.member 
      (newDirection, latestDirection )
      [ (UP, DOWN)
      , (DOWN, UP)
      , (LEFT, RIGHT)
      , (RIGHT, LEFT)
      ]

```

but whatever your preference is.

Here’s another where a simple re-naming is better than a comment:

```elm
{- Model when starting OR re-starting the game

-}

getDefaultModel : Game -> Model
getDefaultModel game =

```

Just rename this to `resetGame` or perhaps `resetModelForGameStart`.

## Actual code issue

So personally I feel there is really only one deficiency in the code, apples store whether or not they are ‘eaten’. But that seems to be in order to remove them, rather than because you want eaten apples to still be on your model. You betray this with your comment here:

```elm
type alias Apple =
    { position : Position
    , eaten : Bool -- once eaten, the apple will be replaced
    }

```

and similarly here (again the comment betraying the opportunity to refactor):

```elm
-- apples
    -- (non-sense positions - replaced via the commands)
    , apples =
        [ { position = Position 49 49, eaten = True }
        , { position = Position 50 50, eaten = True }
        ]

```

So first of all, I would just remove the apple from the list of apples when it is eaten. Secondly if you must use a command to generate the random new position then when that command messages back (in the handler to `NewApplePosition`) just **create** the apple at that point, and add the newly created apply to the model. That means in your initial model you do not need the two ‘nonsense’ apples, just set the list to the empty list, since you then fire off two commands you’ll end up with two apples. Whenever an apple is eaten, you do not need to record this fact, just remove it and fire off a command to create a new one.

Secondly, you don’t really need to use commands for this. If you store the ‘seed’ on your model, you can just call `Random.step` which will give you a new random value **and** the new seed to update on your model.

I’m sure there are other nit-picky things I could state such as:

```elm
getApplePositions : Model -> List Position
getApplePositions model =
    List.map (\apple -> apple.position) model.apples

```

can be written as `List.map .position model.apples` but if you’re happy with the above then that’s also fine. And most other stuff is mostly personal preference.

---

<div class="post-metadata">

**Author:** ![Mousaka](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/mousaka/32/1531_2.png) [@Mousaka](https://discourse.elm-lang.org/u/Mousaka)\
**Post date:** [January 14, 2021, 2:50pm UTC](https://discourse.elm-lang.org/t/double-snake-elm-game/6737/5 "2021-01-14T14:50:21Z")

</div>

Cool! One annoying thing I found was that I lose when the black snake rams into me. Feels more like it should be a win 🙂

---

<div class="post-metadata">

**Author:** ![ChristophP](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/christophp/32/1528_2.png) [@ChristophP](https://discourse.elm-lang.org/u/ChristophP)\
**Post date:** [January 14, 2021, 3:15pm UTC](https://discourse.elm-lang.org/t/double-snake-elm-game/6737/6 "2021-01-14T15:15:24Z")

</div>

Hey awesome, very fun game 😃 Also I really enjoy reading the code since it’s veryb descriptive `doesOtherSnakeEat`, `setApples`. Very nice 😃

---

<div class="post-metadata">

**Author:** ![FrancoisConstant](https://avatars.discourse-cdn.com/v4/letter/f/7ba0ec/32.png) [@FrancoisConstant](https://discourse.elm-lang.org/u/FrancoisConstant)\
**Post date:** [January 16, 2021, 3:03am UTC](https://discourse.elm-lang.org/t/double-snake-elm-game/6737/7 "2021-01-16T03:03:17Z")

</div>

Correct :+1 - it’s updated here: [Remove all caps types · FrancoisConstant/elm-double-snake@2f124c0 · GitHub](https://github.com/FrancoisConstant/elm-double-snake/commit/2f124c058ca49d959d1da30e7d0e7ed5da9ce784)

---

<div class="post-metadata">

**Author:** ![FrancoisConstant](https://avatars.discourse-cdn.com/v4/letter/f/7ba0ec/32.png) [@FrancoisConstant](https://discourse.elm-lang.org/u/FrancoisConstant)\
**Post date:** [January 16, 2021, 3:18am UTC](https://discourse.elm-lang.org/t/double-snake-elm-game/6737/8 "2021-01-16T03:18:57Z")

</div>

Thanks a lot for the code review.

> It’s the nature of feedback that it’s mostly negative

Yes, no problem. I’ve received and done plenty of code reviews (_also, I’m married_).

* * *

Updates are here:

- [Minor refactoring: · FrancoisConstant/elm-double-snake@0d0ff8a · GitHub](https://github.com/FrancoisConstant/elm-double-snake/commit/0d0ff8ae9d78c8e9dd0644e939593033aad63b79)
- [Refactor apple random placement code · FrancoisConstant/elm-double-snake@54290cc · GitHub](https://github.com/FrancoisConstant/elm-double-snake/commit/54290ccff30a93033da3eac0b5765aa7ec88998e)

* * *

> Note I might write that as:
> 
> ```auto
> isDirectionReverse =
> List.member 
> (newDirection, latestDirection )
> [ (UP, DOWN)
> , (DOWN, UP)
> , (LEFT, RIGHT)
> , (RIGHT, LEFT)
> ]
> 
> ```

👍

* * *

> you can just call `Random.step`

Good one; I’ve used that. I didn’t even save the seed in the model; just used the time for the following seeds.

* * *

I’ve kept the comments like – MODEL, – UPDATE, etc. These are just meant to break down the code a bit without creating new files. _I think (vague memory) that Evan did that in one of his presentation._

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [January 26, 2021, 3:19am UTC](https://discourse.elm-lang.org/t/double-snake-elm-game/6737/9 "2021-01-26T03:19:01Z")

</div>

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
