Live data from Hacker News

Implementing 2048 in 90 lines of Haskell

gregorulm.com

11–20 of 25 posts

Re: Implementing 2048 in 90 lines of Haskell

#11

Neat! I also cloned 2048 in Haskell [1]. I don't know how many lines of code it is, but the core game logic is remarkably simple. [1]: http://taylor.fausak.me/2014/04/28/cloning-2048-in-haskell/

I noticed that your blog post differed from the actual code in your git repository. In the blog post, you defined `shift` to be:

   shift v = pad
     (map Just (concatMap add (group (catMaybes v)))))
     Nothing
     (length v)
whereas the code gives:

   shift v = take n (v'  empty n)
     where
       n = length v
       v' = group (filter isJust v) >>= go
       go (Just a : Just b : ts) = Just (a + b) : go ts
       go ts = ts
From my understanding of the blog post's logic, the former doesn't handle the case where `v = replicate 4 (Just 2)`, since it returns `[Just 4, Just 2, Just 2, Nothing]`. Am I correct, and if so, why does the latter version fix this problem? For reference, I know little to no Haskell.

Re: Implementing 2048 in 90 lines of Haskell

#12

Neat! I also cloned 2048 in Haskell [1]. I don't know how many lines of code it is, but the core game logic is remarkably simple. [1]: http://taylor.fausak.me/2014/04/28/cloning-2048-in-haskell/

I noticed that your blog post differed from the actual code in your git repository. In the blog post, you defined `shift` to be: shift v = pad (map Just (concatMap add (group (catMaybes v))))) Nothing (length v) whereas the code gives: shift v = take n (v' empty n) where n = length v v' = group (filter isJust v) >>= go go (Just a : Just b : ts) = Just (a + b) : go ts go ts = ts From my understanding of the blog post'…

Wow, excellent catch! I apologize for the discrepancy. I must have made a mistake simplifying the game code for the blog post. I created a Gist [1] to showcase the differences.

The problem in the blog post is the `add` function. It should recurse (like `go` in the Hs2048 package). I fixed the post [2] with this line:

    add (x : y : rest) = x + y : add rest
[1]: https://gist.github.com/tfausak/4401ef0b43b5c1db0570 [2]: https://github.com/tfausak/tfausak.github.io/issues/31

Re: Implementing 2048 in 90 lines of Haskell

#13

Earlier quoted context omitted.

I noticed that your blog post differed from the actual code in your git repository. In the blog post, you defined `shift` to be: shift v = pad (map Just (concatMap add (group (catMaybes v))))) Nothing (length v) whereas the code gives: shift v = take n (v' empty n) where n = length v v' = group (filter isJust v) >>= go go (Just a : Just b : ts) = Just (a + b) : go ts go ts = ts From my understanding of the blog post'…

Wow, excellent catch! I apologize for the discrepancy. I must have made a mistake simplifying the game code for the blog post. I created a Gist [1] to showcase the differences. The problem in the blog post is the `add` function. It should recurse (like `go` in the Hs2048 package). I fixed the post [2] with this line: add (x : y : rest) = x + y : add rest [1]: https://gist.github.com/tfausak/4401ef0b43b5c1db0570 [2]:…

Oh! So `go` is a function that's defined within `shift`. That's the crucial part I was missing. Why is it named "go"?

EDIT: While I have your attention, do you mind also pointing me to some resources to learn about this ">>=" operator? I recall it's something related to monads...

Re: Implementing 2048 in 90 lines of Haskell

#15

Earlier quoted context omitted.

Wow, excellent catch! I apologize for the discrepancy. I must have made a mistake simplifying the game code for the blog post. I created a Gist [1] to showcase the differences. The problem in the blog post is the `add` function. It should recurse (like `go` in the Hs2048 package). I fixed the post [2] with this line: add (x : y : rest) = x + y : add rest [1]: https://gist.github.com/tfausak/4401ef0b43b5c1db0570 [2]:…

Oh! So `go` is a function that's defined within `shift`. That's the crucial part I was missing. Why is it named "go"? EDIT: While I have your attention, do you mind also pointing me to some resources to learn about this ">>=" operator? I recall it's something related to monads...

The bind operator (>>=) is commonly expressed in 'do' notation. The following two code snippets are equivalent:

do x foo >>= \x -> bar >>= \y -> baz x y

Re: Implementing 2048 in 90 lines of Haskell

#16

Earlier quoted context omitted.

Wow, excellent catch! I apologize for the discrepancy. I must have made a mistake simplifying the game code for the blog post. I created a Gist [1] to showcase the differences. The problem in the blog post is the `add` function. It should recurse (like `go` in the Hs2048 package). I fixed the post [2] with this line: add (x : y : rest) = x + y : add rest [1]: https://gist.github.com/tfausak/4401ef0b43b5c1db0570 [2]:…

Oh! So `go` is a function that's defined within `shift`. That's the crucial part I was missing. Why is it named "go"? EDIT: While I have your attention, do you mind also pointing me to some resources to learn about this ">>=" operator? I recall it's something related to monads...

`go` is a very old name for worker functions in haskell. i have been told it originates in glasgow (where ghc was first developed).

Re: Implementing 2048 in 90 lines of Haskell

#17

Earlier quoted context omitted.

Wow, excellent catch! I apologize for the discrepancy. I must have made a mistake simplifying the game code for the blog post. I created a Gist [1] to showcase the differences. The problem in the blog post is the `add` function. It should recurse (like `go` in the Hs2048 package). I fixed the post [2] with this line: add (x : y : rest) = x + y : add rest [1]: https://gist.github.com/tfausak/4401ef0b43b5c1db0570 [2]:…

Oh! So `go` is a function that's defined within `shift`. That's the crucial part I was missing. Why is it named "go"? EDIT: While I have your attention, do you mind also pointing me to some resources to learn about this ">>=" operator? I recall it's something related to monads...

As ibotty said, I named the anonymous inner worker function `go`. From what I can tell, it seems to be pretty common in Haskell.

As for `>>=` (also known as `bind`), I'm using it as a more generic version of `concatMap`. The following expressions are basically the same:

    concat (map (\ x -> [x, -x]) [1, 2, 3])
    concatMap (\ x -> [x, -x]) [1, 2, 3]
    [1, 2, 3] >>= \ x -> [x, -x]
If you want to learn more, check out LYAH [1].

[1]: http://learnyouahaskell.com/a-fistful-of-monads

Re: Implementing 2048 in 90 lines of Haskell

#18
post #9
post #8

This is neat and all, but the smooth animations of the original are probably not something you can easily pull off in few lines of code. And that is a very large fraction of the original, if I recall.

The smooth animations in Gabriele Cirulli's version are due to this API: https://developer.mozilla.org/en/docs/Web/API/window.request... They consist of a few lines of code. Further, the animation code is only a miniscule fraction of that code base.

Not entirely sure why you are getting voted down. (Well, unless you are wrong...) :(

I'll have to dig through the code again. The polish that the end product of the first version exhibited is well beyond that of any of the rewrites I've seen. To the point that it is, in fact, jarring. And makes it frustrating to read how much nicer the code looks in some of them.

Really brings home the point that intrinsic quality is nice, but pails in comparison to the final quality of the overall product. It isn't that I am convinced the sausage making process is always gross. Just that I have not seen that many examples where the clean versions are anything more than just sterilizations that have killed a lot of the original.

Re: Implementing 2048 in 90 lines of Haskell

#19
post #18
post #9

Earlier quoted context omitted.

The smooth animations in Gabriele Cirulli's version are due to this API: https://developer.mozilla.org/en/docs/Web/API/window.request... They consist of a few lines of code. Further, the animation code is only a miniscule fraction of that code base.

Not entirely sure why you are getting voted down. (Well, unless you are wrong...) :( I'll have to dig through the code again. The polish that the end product of the first version exhibited is well beyond that of any of the rewrites I've seen. To the point that it is, in fact, jarring. And makes it frustrating to read how much nicer the code looks in some of them. Really brings home the point that intrinsic quality is…

Who knows, maybe my comment made some "full stack" developers feel insecure. Just look at the code base of the original, and you'll see that the animation is done through API calls.

Regarding the point you brought up: you are conflating two issues. One is whether the UI is appealing, and the other whether the underlying code is of a high quality. The point of my article was merely to show how nicely the game logic can be expressed in Haskell, compared to many other languages.

Re: Implementing 2048 in 90 lines of Haskell

#20
post #19
post #18

Earlier quoted context omitted.

Not entirely sure why you are getting voted down. (Well, unless you are wrong...) :( I'll have to dig through the code again. The polish that the end product of the first version exhibited is well beyond that of any of the rewrites I've seen. To the point that it is, in fact, jarring. And makes it frustrating to read how much nicer the code looks in some of them. Really brings home the point that intrinsic quality is…

Who knows, maybe my comment made some "full stack" developers feel insecure. Just look at the code base of the original, and you'll see that the animation is done through API calls. Regarding the point you brought up: you are conflating two issues. One is whether the UI is appealing, and the other whether the underlying code is of a high quality. The point of my article was merely to show how nicely the game logic ca…

I'm not necessarily conflating them, though. That is, can you, through interaction with the original app, show that the code is of "low quality"? Even relaxing it, can you show it is of "lower quality" than the Haskel solution?

Because, just using the app, I'd say the original app is much higher quality than this derivative. One I would consider playing for a bit, the other is just kind of neat.

If we are just going off how nicely the logic can be expressed, with no regard to the dirtiness of implementation, free text wins. A simple paragraph describing the game is much more understandable than even the haskel. Especially if you allow examples.

Edit: Also, I'm curious if you could reproduce the UI of the original with just a library in Haskel. That is, sure, most of the code is done though a library. Does that really change much? If so, you should be able to achieve the same fluid and pleasant UI with "just a library" in Haskel, right? (Genuine question.)

Post reply on HN