# 2048 - A complete clone of the classic game

**URL:** https://discourse.elm-lang.org/t/2048-a-complete-clone-of-the-classic-game/8717
**Category:** Show and Tell
**Created:** [October 16, 2022, 10:08am UTC](https://discourse.elm-lang.org/t/2048-a-complete-clone-of-the-classic-game/8717 "2022-10-16T10:08:03Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![dwayne](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dwayne/32/5478_2.png) [@dwayne](https://discourse.elm-lang.org/u/dwayne)
#### Post date: [October 16, 2022, 10:08am UTC](https://discourse.elm-lang.org/t/2048-a-complete-clone-of-the-classic-game/8717/1 "2022-10-16T10:08:03Z")

</div>

2048 is one of my favorite games to play. When I first learned Elm, 2048 was also one of the first games I tried to clone. My efforts were shared [here](https://discourse.elm-lang.org/t/yet-another-2048-clone-but-this-one-may-be-suitable-for-learning-how-to-design-programs-in-elm/1480) and this is what it looked like:

 ![elm-2048-v1](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/2X/f/f0fc88865ad2fdb62d6d292e5a5dc1f2bfd9045c.png)

A little more than 4 years has past since my first attempt and I gained so much experience with Elm over those years. I wanted to challenge myself to overcome all the hurdles I previously encountered while building the app. The major one being how to handle all those animations. I’m happy to report that I figured out how to make it all work and that’s why I’m so excited to share the fruits of my labor.

I present to you all [2048](https://dwayne.github.io/elm-2048/) ([source code](https://github.com/dwayne/elm-2048)). Have fun!

 ![elm-2048-v2](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/2X/6/6462bbd12c78512f910308b1c91edef04ca8fb3e.png)

---

<div class="post-metadata">

### Author: ![Lucas\_Payr](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/lucas_payr/32/4993_2.png) [@Lucas\_Payr](https://discourse.elm-lang.org/u/Lucas_Payr)
#### Post date: [October 16, 2022, 11:44am UTC](https://discourse.elm-lang.org/t/2048-a-complete-clone-of-the-classic-game/8717/2 "2022-10-16T11:44:20Z")

</div>

This is really well-made.

I just played your old version yesterday and was wondering if one could create a complete clone of the original in elm.

This is the perfect example. Playing through it, it really felt authentic.

---

<div class="post-metadata">

### Author: ![ViniciusAtaide](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/viniciusataide/32/4636_2.png) [@ViniciusAtaide](https://discourse.elm-lang.org/u/ViniciusAtaide)
#### Post date: [October 19, 2022, 5:05am UTC](https://discourse.elm-lang.org/t/2048-a-complete-clone-of-the-classic-game/8717/3 "2022-10-19T05:05:46Z")

</div>

dude this is pro level elm, so pretty and organized. everything. please I beg you to go on YouTube and share some good tips or general idea how you build an elm application or even this game.

---

<div class="post-metadata">

### Author: ![dwayne](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dwayne/32/5478_2.png) [@dwayne](https://discourse.elm-lang.org/u/dwayne)
#### Post date: [October 19, 2022, 4:45pm UTC](https://discourse.elm-lang.org/t/2048-a-complete-clone-of-the-classic-game/8717/4 "2022-10-19T16:45:09Z")

</div>

@ViniciusAtaide Thanks for the glowing review.

> [@ViniciusAtaide](#):
>
> please I beg you to go on YouTube and share some good tips or general idea how you build an elm application or even this game.

I won’t be able to do that anytime soon but I can give you a quick run down of how I approached building this particular application and then you’d have to read the code to understand the rest for now.

Here goes.

My biggest unknown was how to structure the code to handle the animations. So I started by trying to understand how the animations worked in the original version. Then, I tried to recreate them so they worked in Elm. That’s what the [experiments](https://github.com/dwayne/elm-2048/tree/607a1219024d8422263ed3bec5bb1b4bdc040035/experiments) folder is about.

## Experiments

The code in the experiments folder answers all the animation related questions I had.

- How to make the appear, move/merge, score and game over animations work in Elm?
- How to implement the move algorithm so that the animations work as expected?

Basically, I found out that CSS transitions/animations would do for almost everything except the move/merge and score animations.

### The move/merge animation

The problem with the move/merge animation is that it is order dependent, i.e. if I don’t output the tiles in the correct order (where correct order isn’t well defined in all cases) then the animation goes haywire.

I tested the order dependency by hard coding [the before and after configuration of the tiles](https://github.com/dwayne/elm-2048/blob/607a1219024d8422263ed3bec5bb1b4bdc040035/experiments/merge/src/Main.elm#L62-L84). Then, by changing the order of the after configuration I was able to see how the animation was affected.

To play with the move/merge animation you can do the following:

```auto
$ nix-shell
$ ./experiments/merge/bin/build.sh
$ ./experiments/merge/bin/serve.sh

```

Then, open `localhost:8000` in your browser to test it out.

Basically, the results of the experiment got me 99% percent there. I figured out how to structure the code and what would work and what wouldn’t work. To get 100% of the way I had to do the following:

1. Use [mgold/elm-animation](https://package.elm-lang.org/packages/mgold/elm-animation/2.0.0/) to translate the tiles.
2. Use [Html.Keyed](https://package.elm-lang.org/packages/elm/html/1.0.0/Html-Keyed) nodes.

Another thing.

The duration of the tile translation affects when the other animations occur. Since the tile translation was happening in Elm I wanted Elm to be the source of truth for the value of the duration. However, I wanted the CSS to remain the source of truth for the other animations. To accomplish this I used a combination of [CSS variables](https://github.com/dwayne/elm-2048/blob/607a1219024d8422263ed3bec5bb1b4bdc040035/sass/_config.scss#L18-L33) and a [style node](https://github.com/dwayne/elm-2048/blob/607a1219024d8422263ed3bec5bb1b4bdc040035/src/App/View/Grid.elm#L115-L118).

```auto
--grid-tile-move-duration: 100ms;
--tile-appear-duration: 200ms;
--tile-appear-delay: var(--grid-tile-move-duration);
--tile-pop-duration: 200ms;
--tile-pop-delay: var(--grid-tile-move-duration);
--tile-disappear-duration: 0s;
--tile-disappear-delay: calc(var(--grid-tile-move-duration) + var(--tile-pop-duration));

```

```auto
H.node "style" []
  [H.text <| ":root { --grid-tile-move-duration: " ++ String.fromFloat moveDuration ++ "ms;" ++ " }"]

```

### The score animation

The most interesting thing with this is how all updates to the score are shown. The animation doesn’t get cut. If you play the original JS game you’d notice that if you have back to back scores the animation for the first score gets removed and the new score is animated. In my version I store all the changes to the score, which I call deltas, and only when a delta is done animating (which I determine by setting an [animationend event](https://developer.mozilla.org/en-US/docs/Web/API/Element/animationend_event) on the delta) I remove it.

To play with the [score experiment](https://github.com/dwayne/elm-2048/tree/607a1219024d8422263ed3bec5bb1b4bdc040035/experiments/score) you can do the following:

```auto
$ nix-shell
$ ./experiments/score/bin/build.sh
$ ./experiments/score/bin/serve.sh

```

Then, open `localhost:8000` in your browser.

## Prototype

With the experiments out of the way I started to work on everything else.

A new process I’ve been trying out recently is to first build all [my views in HTML & Sass](https://github.com/dwayne/elm-2048/tree/607a1219024d8422263ed3bec5bb1b4bdc040035/prototype). I predominantly use [BEM](https://dev.to/dwayne/my-notes-on-bem-4dni) to help me in this process. During this process I pick a block and figure out the structure of the HTML in all its configurations. Then, I figure out how style each configuration.

Once I’m done with this process translating to Elm is trivial. Furthermore, when I encounter structural or visual problems with the block it’s the prototype I return to to fix the issue.

For example, here’s what the prototype for the grid block (the most complicated block in the application) looks like:

 ![grid-prototype](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/2X/c/cf0ea574611e8779c0059b146801cc4ede2ce047.jpeg)

To play with the prototype you can do the following:

```auto
$ nix-shell
$ ./prototype/bin/build.sh
$ ./prototype/bin/serve.sh

```

Then, open `localhost:8000` in your browser.

## Elm Views

Building the prototype was a bottom-up approach for me. I started with the smallest blocks until I had a prototype of the entire application. Then, I translated the prototype to `elm/html`. A trivial process.

## The game logic

The part where Elm really shines. I wrote the game logic to be independent of the views. It can be found [here](https://github.com/dwayne/elm-2048/tree/607a1219024d8422263ed3bec5bb1b4bdc040035/src/App/Data). I hope when you read it, it [screams](http://blog.cleancoder.com/uncle-bob/2011/09/30/Screaming-Architecture.html): 2048. I made extensive use of opaque types and tried to model the domain so that impossible states remained impossible.

## Tech

Nix, bash scripts, Caddy, Sass, elm-format, elm-review, elm-optimize-level-2.

---

<div class="post-metadata">

### Author: ![eberfreitas](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/eberfreitas/32/2741_2.png) [@eberfreitas](https://discourse.elm-lang.org/u/eberfreitas)
#### Post date: [October 20, 2022, 1:54pm UTC](https://discourse.elm-lang.org/t/2048-a-complete-clone-of-the-classic-game/8717/5 "2022-10-20T13:54:10Z")

</div>

This looks and works amazing. Also, thank you for mentioning Threes there!

---

<div class="post-metadata">

### Author: ![tlentz](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/tlentz/32/2499_2.png) [@tlentz](https://discourse.elm-lang.org/u/tlentz)
#### Post date: [October 25, 2022, 6:58am UTC](https://discourse.elm-lang.org/t/2048-a-complete-clone-of-the-classic-game/8717/6 "2022-10-25T06:58:44Z")

</div>

this is awesome, thanks for sharing! I played a whole lot of 2048 during college

---

<div class="post-metadata">

### Author: ![jigargosar](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/jigargosar/32/79_2.png) [@jigargosar](https://discourse.elm-lang.org/u/jigargosar)
#### Post date: [October 31, 2022, 6:15am UTC](https://discourse.elm-lang.org/t/2048-a-complete-clone-of-the-classic-game/8717/7 "2022-10-31T06:15:05Z")

</div>

Thats some great work.

It reminded me of my old project in elm in 2020 😉  
Looking at my old code, I feel I have come a long way since then.

Here are the links if anyone wants to know how an elm noob coded in 2020.

> **[GitHub - jigargosar/elm-2048](https://github.com/jigargosar/elm-2048)**
>
> Contribute to jigargosar/elm-2048 development by creating an account on GitHub.

[https://elm-2048.surge.sh/](https://elm-2048.surge.sh/)

---

<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: [November 10, 2022, 6:15am UTC](https://discourse.elm-lang.org/t/2048-a-complete-clone-of-the-classic-game/8717/8 "2022-11-10T06:15:06Z")

</div>

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