Feedback needed on my first project

I have partly implemented a simple snake game. Can somebody please have a look an let me know what is wrong with my style.

Lines 150 and 181 show my way getting rid of Maybe value. Please advise a better way to do it. Also please tell me how can I improve my elm style.

consider using elm-format and configure your editor to call elm-format on save.

On getting rid of a Maybe: consider using Maybe.withDefault

You can also experiment with using datastructures that encapsulate the fact that there is always a head : http://package.elm-lang.org/packages/mgold/elm-nonempty-list/latest

2 Likes

Congrats on your first project! :smiley: :snake:

In general, there are three approaches I use when dealing with Maybe:

  1. Use helper functions such as withDefault and map to avoid explicit casing.
  2. Pushing uncertainty to the edges of your code by separating code that checks for presence from code that calculates values.
  3. Modeling your code using other structures such that the need for Maybe is eliminated entirely.

In your case, it looks like most of the Maybes are introduced by calling List.head. I’m thinking a List may not be the best structure to model.

Some alternatives might be:

Library

List.NonEmpty as suggested by @pdamoc above

Custom Type

A custom type that guarantees you a head:

type alias Snake =
  { head : Coordinate
  , tail : List Coordinate
  }

-- OR

type Snake = Snake Coordinate (List Coordinate)

Note that these are basically custom versions of List.NonEmpty

Math

You could take a completely different approach and never store every segment of the snake. Instead you’d store the position of the head, the length of the snake, and the coordinates of any turns.

type alias Snake =
  { head : Coordinate
  , length : Int
  , turns : List Coordinate
  }

Then you’d use math to figure out if the snake intersects itself. This could make some things easier like lengthening or shortening the snake.

3 Likes

Thank you for your detailed critique. There is one thing I do not understand. In the math section, you propose a different approach, which you call easier for things like growing and shortening the snake.

I think my approach is the simplest and I would consider yours only when handling long snakes would cause performance problems.

The model has growth factor which when positive grows the snake and shrinks the snake when negative. When I cook the model I make sure the growthFactor is decreased towards 0, thus growing and shrinking the snake gradually. If I did not have the uncertain head problems function headBitSnake would be a one-liner. That was my reason for the code like this, but if I have time I will try your suggested approach and will see what happens.

Yeah I don’t know if the math approach is necessarily the best. :smiley:

You’re essentially trading a storage-based approach for computation-based one. That means you you need to do some more coordinate math but it also means you don’t have to constantly query and update a list of positions. It probably also allows you to avoid invalid states such as a snake with non-consecutive coordinates.

I’ve had good success favoring computation-based approaches but that doesn’t mean they’re always the best solution. It’s up to you to evaluate the tradeoffs.

For the beginner snake game, I will keep my original solution unless I start having performance problems. I will still examin deeper your tip about types.