| CARVIEW |
Select Language
HTTP/2 200
cross-origin-resource-policy: cross-origin
etag: W/"47e5a79460a6a2c0c37ba785e39037bff82d048ac3f848d5563823ae8aa1c283"
date: Sat, 17 Jan 2026 12:31:34 GMT
content-type: application/atom+xml; charset=UTF-8
server: blogger-renderd
expires: Sat, 17 Jan 2026 12:31:35 GMT
cache-control: public, must-revalidate, proxy-revalidate, max-age=1
x-content-type-options: nosniff
x-xss-protection: 0
last-modified: Sat, 01 Nov 2025 14:37:43 GMT
content-encoding: gzip
content-length: 39715
x-frame-options: SAMEORIGIN
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
tag:blogger.com,1999:blog-6355249120999252418 2025-11-01T07:37:43.643-07:00 Writing JavaScript games in Haskell Exploring writing browser games with Haskell Nathan Hüsken https://www.blogger.com/profile/09614845657227846437 noreply@blogger.com Blogger 6 1 25 tag:blogger.com,1999:blog-6355249120999252418.post-8350739618975400074 2012-11-12T04:34:00.000-08:00 2013-08-21T23:08:29.128-07:00 Breakout - Improved and with netwire <p>Hi, welcome to the 6th article of this blog.</p>
<p>In this blog post, the breakout example from the <a href="https://jshaskell.blogspot.de/2012/09/breakout.html">last Post</a> has been improved, giving it more features:</p>
<ul>
<li>The Paddle and Blocks have rounded edges. The ball bounces of them depending on the surface normal where it hits.</li>
<li>The blocks are fading out when destroyed.</li>
<li>The paddle can shot to destroy blocks. When the game starts one shot is available, shots can be gained by destroying green blocks.</li>
<li>The game is aware when the player lost or won and displays this information when the game ends.</li>
</ul>
<p>But that is not all! Instead of using the simple Coroutines we are now using a full blown FRP library called <a href="https://hackage.haskell.org/package/netwire">netwire</a>. But more about that later, here is the preview. As alwyas you have to click the canvas to get input focus. If you are not viewing this blog article on blogspot and the application does not work, try the original <a href="https://jshaskell.blogspot.de/2012/11/breakout-improved-and-with-netwire.html">aricle page</a>.</p>
<script src="https://rawgithub.com/RudolfVonKrugstein/jshaskell-blog/master/6_BreakoutImproved/code/compiled/BreakoutImproved.js" type="text/javascript"></script>
<canvas height="400" id="canvas4" style="background-color: white;" width="600" tabindex="1"></canvas>
<p>I had a lot of help over the <a href="https://www.haskell.org/mailman/listinfo/beginners">haskell beginners mailing list</a>. I will try to add links to the specific topics whenever I am writing something I had help with.</p>
<p>As a final note before I start: Being a haskell beginner, I might not do everything here the best way. I encourage you to comment if you think something could be done better! Of course, I also encourage you to comment if you have any questions.</p>
<h1 id="about-netwire">About Netwire</h1>
<p><a href="https://hackage.haskell.org/package/netwire">Netwire</a> is a arrowized functional reactive programming (AFRP) library for haskell and the version 4 of the library has recently been released on <a href="https://hackage.haskell.org/package/netwire">hackage</a>. Since it uses Arrows, some of the things we did with Coroutines can be done the same way with netwires, but it has tons of other features. <a href="https://hackage.haskell.org/packages/archive/netwire/4.0.5/doc/html/Control-Wire.html">Here</a> is a short introduction to netwire, but I will try to explain all the features when I use them.</p>
<p>Also I will explain some of <a href="https://hackage.haskell.org/package/netwire">netwires</a> usage here, this is by no means a complete tutorial to <a href="https://hackage.haskell.org/package/netwire">netwire</a>. One obvious reason for this is, that I myself do not (yet?) understand all the features and Ideas of netwire (remember, I am still a haskell beginner doing this for my own education). Maybe some of this will be useful for someone wanting to start with <a href="https://hackage.haskell.org/package/netwire">netwire</a>.</p>
<p>To install netwire, just type</p>
<pre class="sourceCode bash"><code class="sourceCode bash">haste-inst <span class="kw">install</span> netwire</code></pre>
<p>This will most likely fail on lifted-base and time. There is a (potentially dangerous) workaround <a href="https://github.com/valderman/haste-compiler/issues/28">here</a>, that should work for now.</p>
<h1 id="new-javascript-functions">New Javascript functions</h1>
<p>To Draw a rounded rectangle a new function "fillRoundedRect" is defined in <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/6_BreakoutImproved/code/haste/JavaScript.hs">JavaScript.hs</a>. Also a new type for Colors has been added:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span> <span class="dt">Color</span> <span class="fu">=</span> <span class="dt">Color</span> {<span class="ot">red ::</span> <span class="dt">Double</span>,<span class="ot"> green ::</span> <span class="dt">Double</span>,<span class="ot"> blue ::</span> <span class="dt">Double</span>,<span class="ot"> alpha ::</span> <span class="dt">Double</span>}</code></pre>
<p>"jsFillColor" now takes this as argument instead of a string.</p>
<h1 id="collision-detection">Collision detection</h1>
<p>See <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/6_BreakoutImproved/code/Collision.hs">here</a> for the complete code. We want to represent our objects as Circles (the ball, bullets) and rounded rectangles (the paddle, blocks), so we define data structures for this:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="co">-- Information about collision</span>
<span class="kw">type</span> <span class="dt">Vector</span> <span class="fu">=</span> (<span class="dt">Double</span>, <span class="dt">Double</span>) <span class="co">-- thanks to vector-space we can do ^+^ and similar</span>
<span class="kw">data</span> <span class="dt">Collision</span> <span class="fu">=</span> <span class="dt">Collision</span> {<span class="ot"> normal ::</span> <span class="dt">Vector</span> } <span class="kw">deriving</span> (<span class="kw">Show</span>)
<span class="kw">type</span> <span class="dt">Radius</span> <span class="fu">=</span> <span class="dt">Double</span>
<span class="kw">data</span> <span class="dt">Circle</span> <span class="fu">=</span> <span class="dt">Circle</span> {<span class="ot"> circlePos ::</span> <span class="dt">Vector</span>,<span class="ot"> circleRadius ::</span> <span class="dt">Radius</span>}
<span class="kw">data</span> <span class="dt">Rectangle</span> <span class="fu">=</span> <span class="dt">Rectangle</span> <span class="dt">Vector</span> <span class="dt">Vector</span>
<span class="kw">data</span> <span class="dt">RoundedRect</span> <span class="fu">=</span> <span class="dt">RoundedRect</span> {<span class="ot"> rectMin ::</span> <span class="dt">Vector</span>,<span class="ot"> rectMax ::</span> <span class="dt">Vector</span>,<span class="ot"> rectRadius ::</span> <span class="dt">Radius</span>}</code></pre>
<p>The Collision information now contains the normal of the collision. This is needed to correctly bounce the ball.</p>
<p>For convenience in the usage of the collision functions, we will define type classes for the objects having the shape of a circle or a rounded rectangle:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">class</span> <span class="dt">CircleShaped</span> a <span class="kw">where</span>
<span class="ot"> circle ::</span> a <span class="ot">-></span> <span class="dt">Maybe</span> <span class="dt">Circle</span>
<span class="kw">class</span> <span class="dt">RoundedRectShaped</span> a <span class="kw">where</span>
<span class="ot"> roundedRect ::</span> a <span class="ot">-></span> <span class="dt">Maybe</span> <span class="dt">RoundedRect</span>
<span class="kw">instance</span> <span class="dt">CircleShaped</span> <span class="dt">Circle</span> <span class="kw">where</span>
circle c <span class="fu">=</span> <span class="kw">Just</span> c
<span class="kw">instance</span> <span class="dt">RoundedRectShaped</span> <span class="dt">RoundedRect</span> <span class="kw">where</span>
roundedRect r <span class="fu">=</span> <span class="kw">Just</span> r</code></pre>
<p>This allows us to apply the collision functions directly to our game objects (when they are instances of the corresponding class) without always explicitly extracting the shape. In other words, instead of writing:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell">circleRectCollision (ballCircle ball) (blockRect block)</code></pre>
<p>we can write</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell">circleRectCollision ball block</code></pre>
<p>The type classes return maybe types, because some objects might become "shapeless" and should not collide (for example a block that is fading out).</p>
<p>Ok, the first thing we need is a collision between circles</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">import</span> <span class="dt">Control.Monad</span>
<span class="ot">circleCollision ::</span> (<span class="dt">CircleShaped</span> a, <span class="dt">CircleShaped</span> b) <span class="ot">=></span> a <span class="ot">-></span> b <span class="ot">-></span> <span class="dt">Maybe</span> <span class="dt">Collision</span>
circleCollision a b <span class="fu">=</span> <span class="kw">do</span>
(<span class="dt">Circle</span> p1 r1) <span class="ot"><-</span> circle a
(<span class="dt">Circle</span> p2 r2) <span class="ot"><-</span> circle b
<span class="kw">let</span> centerDiff <span class="fu">=</span> p2 <span class="fu">^-^</span> p1
guard (centerDiff <span class="fu"><.></span> centerDiff <span class="fu"><=</span> (r1 <span class="fu">+</span> r2) <span class="fu">*</span> (r1 <span class="fu">+</span> r2))
<span class="fu">return</span> <span class="fu">$</span> <span class="dt">Collision</span> <span class="fu">$</span> normalized centerDiff</code></pre>
<p>It returns a "Maybe Collision" because a collision might not take place. Notice the "do" notation. We are in the "Maybe" monad, which causes the function to automatically return Nothing if one of out circle shapes return Nothing (if you do not understand, see <a href="https://en.wikipedia.org/wiki/Monad_(functional_programming)#The_Maybe_monad">here</a>). So we are getting the vector between the center positions and testing its square against the square of the sums of the radian of the circles. The guard function (from Control.Monad) causes the monad to return with "Nothing" if the circles are not close enough. Then we return the normalized vector as the normal. Notice that the normal always points from the first circle to the second.</p>
<p>As a helper function, we test if a point is inside a rectangle:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">pointInRectangle ::</span> <span class="dt">Vector</span> <span class="ot">-></span> <span class="dt">Rectangle</span> <span class="ot">-></span> <span class="dt">Bool</span>
pointInRectangle (px,py) (<span class="dt">Rectangle</span> (minX,minY) (maxX,maxY))
<span class="fu">|</span> px <span class="fu">></span> maxX <span class="fu">=</span> <span class="kw">False</span>
<span class="fu">|</span> px <span class="fu"><</span> minX <span class="fu">=</span> <span class="kw">False</span>
<span class="fu">|</span> py <span class="fu">></span> maxY <span class="fu">=</span> <span class="kw">False</span>
<span class="fu">|</span> py <span class="fu"><</span> minY <span class="fu">=</span> <span class="kw">False</span>
<span class="fu">|</span> <span class="fu">otherwise</span> <span class="fu">=</span> <span class="kw">True</span></code></pre>
<p>That should be clear.</p>
<p>So how do we test a circle against a rounded rectangle? A rounded rectangle is rectangle where the corners have been replaced by quarter circles. We have do test against these circles or the "inner" rectangle depending on where the colliding circle is, see this picture:</p>
<div class="figure">
<img src="https://raw.github.com/RudolfVonKrugstein/jshaskell-blog/master/6_BreakoutImproved/roundedRect.png" alt="Areas of rounded rectangle" /><p class="caption">Areas of rounded rectangle</p>
</div>
<p>When the center of the colliding circle is in one of the red areas, collision testing is done with the corresponding corner circles. Otherwise collision is done against the "unrounded" rectangle (which is the same as rounded rectangle when we not in one of the red areas). The normal is then determined by the normal of the closest rectangle side. Here is the code:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">circleRoundedRectCollision ::</span> (<span class="dt">CircleShaped</span> a, <span class="dt">RoundedRectShaped</span> b) <span class="ot">=></span> a <span class="ot">-></span> b <span class="ot">-></span> <span class="dt">Maybe</span> <span class="dt">Collision</span>
circleRoundedRectCollision c r <span class="fu">=</span> <span class="kw">do</span>
circle <span class="ot"><-</span> circle c
rect <span class="ot"><-</span> roundedRect r
circleRoundedRectCollision' circle rect
<span class="kw">where</span>
circleRoundedRectCollision' circle<span class="fu">@</span>(<span class="dt">Circle</span> (cx,cy) cr) (<span class="dt">RoundedRect</span> (minX,minY) (maxX,maxY) rr)
<span class="co">--test the corners</span>
<span class="fu">|</span> cx <span class="fu"><=</span> innerMinX <span class="fu">&&</span> cy <span class="fu"><=</span> innerMinY <span class="fu">=</span> circleCollision (<span class="dt">Circle</span> (innerMinX, innerMinY) rr) circle
<span class="fu">|</span> cx <span class="fu">>=</span> innerMaxX <span class="fu">&&</span> cy <span class="fu"><=</span> innerMinY <span class="fu">=</span> circleCollision (<span class="dt">Circle</span> (innerMaxX, innerMinY) rr) circle
<span class="fu">|</span> cx <span class="fu">>=</span> innerMaxX <span class="fu">&&</span> cy <span class="fu">>=</span> innerMaxY <span class="fu">=</span> circleCollision (<span class="dt">Circle</span> (innerMaxX, innerMaxY) rr) circle
<span class="fu">|</span> cx <span class="fu"><=</span> innerMinX <span class="fu">&&</span> cy <span class="fu"><=</span> innerMinY <span class="fu">=</span> circleCollision (<span class="dt">Circle</span> (innerMinX, innerMaxY) rr) circle
<span class="co">-- test if collision with rectangle occured</span>
<span class="fu">|</span> <span class="fu">not</span> <span class="fu">$</span> pointInRectangle (cx,cy) (<span class="dt">Rectangle</span> ((minX<span class="fu">-</span>cr), (minY<span class="fu">-</span>cr)) ((maxX<span class="fu">+</span>cr), (maxY<span class="fu">+</span>cr))) <span class="fu">=</span> <span class="kw">Nothing</span>
<span class="co">-- collision definitly occured, find correct normal</span>
<span class="fu">|</span> <span class="fu">otherwise</span> <span class="fu">=</span> <span class="kw">Just</span> <span class="fu">$</span> <span class="fu">fst</span> <span class="fu">$</span> minimumBy (\(_,a) (_,b) <span class="ot">-></span> <span class="fu">compare</span> a b)
[
(<span class="dt">Collision</span> (<span class="fu">-</span><span class="fl">1.0</span>,<span class="fl">0.0</span>), cx <span class="fu">-</span> minX),
(<span class="dt">Collision</span> (<span class="fl">1.0</span>, <span class="fl">0.0</span>), maxX <span class="fu">-</span> cx),
(<span class="dt">Collision</span> (<span class="fl">0.0</span>,<span class="fu">-</span><span class="fl">1.0</span>), cy <span class="fu">-</span> minY),
(<span class="dt">Collision</span> (<span class="fl">0.0</span>, <span class="fl">1.0</span>), maxY <span class="fu">-</span> cy)
]
<span class="kw">where</span>
innerMinX <span class="fu">=</span> minX <span class="fu">+</span> rr
innerMinY <span class="fu">=</span> minY <span class="fu">+</span> rr
innerMaxX <span class="fu">=</span> maxX <span class="fu">-</span> rr
innerMaxY <span class="fu">=</span> maxY <span class="fu">-</span> rr</code></pre>
<p>I am a bit unhappy that I have to define the inner function "circleRoundedRectCollision'", but I do not know how else I could use this nice pattern guards.</p>
<h1 id="wire-helpers">Wire helpers</h1>
<p>To handle bullets and blocks we need some way to manage a set of objects where objects can be removed. For this I got a lot of help <a href="https://www.haskell.org/pipermail/beginners/2012-November/010901.html">here</a> and <a href="https://www.haskell.org/pipermail/beginners/2012-November/010930.html">here</a>. The code is <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/6_BreakoutImproved/code/WireUtils.hs">here</a>. Let's look at the type of a wire:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span> <span class="dt">Wire</span> e m a b</code></pre>
<p>The m parameter is the underlying monad. We will set it to Identity and be fine with it. "a" is the input type. Quoting from <a href="https://hackage.haskell.org/packages/archive/netwire/4.0.5/doc/html/Control-Wire.html">here</a>: From these inputs it (the wire)</p>
<ul>
<li>either produces an output value of type "b" or inhibits with a value of type "e",</li>
<li>produces a new wire of type Wire e m a b.</li>
</ul>
<p>When a wire produces, it is the same as our Coroutines producing output. The possibility that a wire can inhibit is often used to switch to different wires. See <a href="https://hackage.haskell.org/packages/archive/netwire/4.0.5/doc/html/Control-Wire.html">here</a>. We will explore this possibility a little bit further down.</p>
<h2 id="dynamicset">dynamicSet</h2>
<p>When a wire inhibits, there are several combinators which allows to switch to other wires (permanently or just for one instance). Here inhibiting wires will be removed from the set. To create new wires a creator function and an additional input will be used.</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">dynamicSet ::</span> (<span class="kw">Monad</span> m) <span class="ot">=></span> (c <span class="ot">-></span> <span class="dt">Wire</span> e m a b) <span class="ot">-></span> [<span class="dt">Wire</span> e m a b] <span class="ot">-></span> <span class="dt">Wire</span> e m (a, [c]) [b]
dynamicSet creator ws' <span class="fu">=</span> mkGen <span class="fu">$</span> \dt (i,new) <span class="ot">-></span> <span class="kw">do</span>
res <span class="ot"><-</span> <span class="fu">mapM</span> (\w <span class="ot">-></span> stepWire w dt i) ws'
<span class="kw">let</span> filt (<span class="kw">Right</span> a, b) <span class="fu">=</span> <span class="kw">Just</span> (a,b)
filt _ <span class="fu">=</span> <span class="kw">Nothing</span>
resx <span class="fu">=</span> mapMaybe filt res
<span class="fu">return</span> (<span class="kw">Right</span> <span class="fu">$</span> (<span class="fu">fmap</span> <span class="fu">fst</span> resx), dynamicSet creator <span class="fu">$</span> (<span class="fu">fmap</span> <span class="fu">snd</span> resx) <span class="fu">++</span> (<span class="fu">map</span> creator new))</code></pre>
<p>mkGen is passed a function that is turned into a wire. The parameters for this function are the time delta (dt) and the input (i,new) of the wire. We use the do notation because we are in the inner Monad "m" (of which we know nothing but that it is a monad). After we stepped all wires ("stepWire" steps a wire ,see <a href="https://hackage.haskell.org/packages/archive/netwire/4.0.5/doc/html/Control-Wire.html">netwire tutorial</a>) we filter those that produced (by returning a right value) and return there outputs as list. The new wire is again a dynamics set with the ramaing wires and the newly created ones using the creator function.</p>
<h2 id="dynamicsetmap">dynamicSetMap</h2>
<p>To use dynamic set in the breakout game, we assign each wire in the set a unique key (Int) and change the input to a Map that maps from the key to the input values of the individual wires. Since a map lookup may fail, the input of the wires will be Maybes.</p>
<p>To archive this we define a wire that takes a list as inputs and pairs it with a given (infinite) list (which will be our keys):</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="co">-- queue for the objects in the list given as parameter</span>
<span class="co">-- The Int argument says how many objects should be returned</span>
<span class="ot">staticQueue ::</span> (<span class="kw">Monad</span> m) <span class="ot">=></span> [a] <span class="ot">-></span> <span class="dt">Wire</span> e m <span class="dt">Int</span> [a]
staticQueue set <span class="fu">=</span> unfold give set
<span class="kw">where</span>
give s n <span class="fu">=</span> (<span class="fu">take</span> n s, <span class="fu">drop</span> n s)
<span class="co">-- Pairs the input list with the given list, which is assumed to be infinite</span>
<span class="ot">pairListsWith ::</span> (<span class="kw">Monad</span> m) <span class="ot">=></span> [p] <span class="ot">-></span> <span class="dt">Wire</span> e m [a] [(p,a)]
pairListsWith pairs <span class="fu">=</span> proc <span class="kw">as</span> <span class="ot">-></span> <span class="kw">do</span>
p <span class="ot"><-</span> staticQueue pairs <span class="fu">-<</span> <span class="fu">length</span> <span class="kw">as</span>
returnA <span class="fu">-<</span> <span class="fu">zip</span> p <span class="kw">as</span></code></pre>
<p>using these wires we define dynamicSetMap:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">dynamicSetMap ::</span> (<span class="kw">Monad</span> m) <span class="ot">=></span> (c <span class="ot">-></span> <span class="dt">Wire</span> e m (<span class="dt">Maybe</span> a) b) <span class="ot">-></span> [<span class="dt">Wire</span> e m (<span class="dt">Maybe</span> a) b] <span class="ot">-></span> <span class="dt">Wire</span> e m (<span class="dt">M.Map</span> <span class="dt">Int</span> a, [c]) [(<span class="dt">Int</span>,b)]
dynamicSetMap creator ws <span class="fu">=</span> dynamicSet creator' ws' <span class="fu">.</span> (second <span class="fu">$</span> pairListsWith restKeys)
<span class="kw">where</span>
<span class="ot"> wireWithLookupAndKey ::</span> (<span class="kw">Monad</span> m) <span class="ot">=></span> <span class="dt">Int</span> <span class="ot">-></span> <span class="dt">Wire</span> e m (<span class="dt">Maybe</span> a) b <span class="ot">-></span> <span class="dt">Wire</span> e m (<span class="dt">M.Map</span> <span class="dt">Int</span> a) (<span class="dt">Int</span>,b)
wireWithLookupAndKey i w <span class="fu">=</span> (pure i) <span class="fu">&&&</span> (w <span class="fu">.</span> (arr (M.lookup i)))
keys <span class="fu">=</span> [<span class="dv">0</span>,<span class="dv">1</span><span class="fu">..</span>]
restKeys <span class="fu">=</span> <span class="fu">drop</span> (<span class="fu">length</span> ws) keys
ws' <span class="fu">=</span> <span class="fu">map</span> (<span class="fu">uncurry</span> wireWithLookupAndKey) <span class="fu">$</span> <span class="fu">zip</span> keys ws
creator' (i,c) <span class="fu">=</span> wireWithLookupAndKey i (creator c)</code></pre>
<h2 id="shrinking-and-shrinkingmap">shrinking and shrinkingMap</h2>
<p>Since blocks can not be created, only destroyed, we define a simplified version of dynamicSet and dynamicSetMap where no new wires can be created.</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="co">-- same as dynamicSet, only that it can not grow</span>
<span class="ot">shrinking ::</span> (<span class="kw">Monad</span> m) <span class="ot">=></span> [<span class="dt">Wire</span> e m a b] <span class="ot">-></span> <span class="dt">Wire</span> e m a [b]
shrinking ws <span class="fu">=</span> dynamicSet <span class="fu">undefined</span> ws <span class="fu"><<<</span> arr (\a <span class="ot">-></span> (a,[]))
<span class="co">-- same as dynamicSetMap, only that it can not grow</span>
<span class="ot">shrinkingMap ::</span> (<span class="kw">Monad</span> m) <span class="ot">=></span> [<span class="dt">Wire</span> e m (<span class="dt">Maybe</span> a) b] <span class="ot">-></span> <span class="dt">Wire</span> e m (<span class="dt">M.Map</span> <span class="dt">Int</span> a) [(<span class="dt">Int</span>,b)]
shrinkingMap ws <span class="fu">=</span> dynamicSetMap <span class="fu">undefined</span> ws <span class="fu"><<<</span> arr (\a <span class="ot">-></span> (a,[]))</code></pre>
<p>To conclude these helper wires, I am not sure if these are the best choices but they work for now.</p>
<h1 id="the-game">The Game</h1>
<p>Finally we are ready to define the game itself! The code is <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/6_BreakoutImproved/code/BreakoutImproved.hs">here</a>.</p>
<h2 id="input">Input</h2>
<p>In difference to the <a href="https://jshaskell.blogspot.de/2012/09/breakout.html">last</a> post, we will step the wire on every input event. An input event will be a keyboard event or an "Update" event which causes the main wire to update all game objects.</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span> <span class="dt">InputEvent</span> <span class="fu">=</span> <span class="dt">KeyUp</span> <span class="dt">Int</span> <span class="fu">|</span> <span class="dt">KeyDown</span> <span class="dt">Int</span> <span class="fu">|</span> <span class="dt">Update</span>
<span class="kw">deriving</span> (<span class="kw">Eq</span>)</code></pre>
<h2 id="data-objects">Data objects</h2>
<p>Here are the data objects defining the state of the game:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="co">-- state of game objects</span>
<span class="kw">data</span> <span class="dt">Paddle</span> <span class="fu">=</span> <span class="dt">Paddle</span> {<span class="ot"> xPos ::</span> <span class="dt">Double</span> }
<span class="kw">data</span> <span class="dt">Gun</span> <span class="fu">=</span> <span class="dt">Gun</span> {<span class="ot"> ammo ::</span> <span class="dt">Int</span> }
<span class="kw">data</span> <span class="dt">Ball</span> <span class="fu">=</span> <span class="dt">Ball</span> {<span class="ot"> ballPos ::</span> <span class="dt">Vector</span>,
<span class="ot"> ballSpeed ::</span> <span class="dt">Vector</span>}
<span class="kw">data</span> <span class="dt">Block</span> <span class="fu">=</span> <span class="dt">Block</span> {<span class="ot"> blockType ::</span> <span class="dt">BlockType</span>,<span class="ot"> blockPos ::</span> <span class="dt">Vector</span>,<span class="ot"> blockState ::</span> <span class="dt">BlockState</span>}
<span class="kw">data</span> <span class="dt">BlockState</span> <span class="fu">=</span> <span class="dt">Alive</span> <span class="dt">Int</span> <span class="fu">|</span> <span class="dt">Dying</span> <span class="dt">Double</span>
<span class="kw">data</span> <span class="dt">BlockType</span> <span class="fu">=</span> <span class="dt">NormalBlock</span> <span class="fu">|</span> <span class="dt">PowerBlock</span> <span class="kw">deriving</span> (<span class="kw">Eq</span>)
<span class="kw">data</span> <span class="dt">Bullet</span> <span class="fu">=</span> <span class="dt">Bullet</span> {<span class="ot"> bulletPos ::</span> <span class="dt">Vector</span> }
<span class="kw">data</span> <span class="dt">GameState</span> <span class="fu">=</span> <span class="dt">GameState</span> {
<span class="ot"> paddle ::</span> <span class="dt">Paddle</span>,
<span class="ot"> gun ::</span> <span class="dt">Gun</span>,
<span class="ot"> ball ::</span> <span class="dt">Ball</span>,
<span class="ot"> blocks ::</span> [<span class="dt">Block</span>],
<span class="ot"> bullets ::</span> [<span class="dt">Bullet</span>]}
<span class="fu">|</span> <span class="dt">StartScreen</span> <span class="dt">String</span></code></pre>
<p>The StartScreen constructor of the GameState is to show a message when the game is not running (in the beginning and when the player won or lost). We gave the ball the ballSpeed property (which is not necessary for viewing the game state) because it will be needed outside the balls own wires later. You will see. The Double parameter for a Dying block is the fade level (going from 1.0 to 0.0 as the block is removed). A Block now also as a BlockType. A PowerBlock is a block that gives the player ammo when destroyed.</p>
<h2 id="constants">constants</h2>
<p>A lot of constants follow which define the properties of the game</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="co">-- constants</span>
screenWidth <span class="fu">=</span> <span class="fl">600.0</span>
screenHeight <span class="fu">=</span> <span class="fl">400.0</span>
paddleColor <span class="fu">=</span> <span class="dt">Color</span> <span class="fl">0.0</span> <span class="fl">0.0</span> <span class="fl">0.0</span> <span class="fl">1.0</span>
paddleYPos <span class="fu">=</span> screenHeight <span class="fu">-</span> paddleHeight
paddleHeight <span class="fu">=</span> <span class="fl">15.0</span>
paddleWidth <span class="fu">=</span> <span class="fl">50.0</span>
paddleRadius <span class="fu">=</span> <span class="fl">7.0</span>
paddleSpeed <span class="fu">=</span> <span class="fl">7.0</span>
initPaddleXPos <span class="fu">=</span> (screenWidth <span class="fu">-</span> paddleWidth) <span class="fu">/</span> <span class="fl">2.0</span>
initPaddle <span class="fu">=</span> <span class="dt">Paddle</span> initPaddleXPos
initGun <span class="fu">=</span> <span class="dt">Gun</span> <span class="dv">1</span>
ballColor <span class="fu">=</span> <span class="dt">Color</span> <span class="fl">1.0</span> <span class="fl">0.0</span> <span class="fl">0.0</span> <span class="fl">1.0</span>
ballRadius <span class="fu">=</span> <span class="fl">5.0</span>
initBallSpeed <span class="fu">=</span> (<span class="fl">3.0</span>, <span class="fu">-</span><span class="fl">3.0</span>)
initBallPos <span class="fu">=</span> (screenWidth <span class="fu">/</span> <span class="fl">2.0</span>, screenHeight <span class="fu">-</span> <span class="fl">50.0</span>)
initBall <span class="fu">=</span> <span class="dt">Ball</span> initBallPos initBallSpeed
blockWidth <span class="fu">=</span> <span class="fl">60.0</span>
blockHeight <span class="fu">=</span> <span class="fl">20.0</span>
blockRadius <span class="fu">=</span> <span class="fl">5.0</span>
normalBlockColor <span class="fu">=</span> [<span class="dt">Color</span> <span class="fl">0.0</span> <span class="fl">0.0</span> <span class="fl">1.0</span> <span class="fl">1.0</span>, <span class="dt">Color</span> <span class="fl">0.0</span> <span class="fl">0.0</span> <span class="fl">0.5</span> <span class="fl">1.0</span>]
powerBlockColor <span class="fu">=</span> [<span class="dt">Color</span> <span class="fl">0.0</span> <span class="fl">0.5</span> <span class="fl">0.0</span> <span class="fl">1.0</span>]
initBlocks <span class="fu">=</span> [<span class="dt">Block</span> t (x,y) (<span class="dt">Alive</span> l) <span class="fu">|</span> x <span class="ot"><-</span> [<span class="fl">20.0</span>, <span class="fl">140.0</span>, <span class="fl">240.0</span>, <span class="fl">340.0</span>, <span class="fl">440.0</span>, <span class="fl">520.0</span>], (y,t,l) <span class="ot"><-</span> [(<span class="fl">60.0</span>, <span class="dt">PowerBlock</span>, <span class="dv">1</span>), (<span class="fl">100.0</span>, <span class="dt">NormalBlock</span>,<span class="dv">2</span>), (<span class="fl">140.0</span>,<span class="dt">NormalBlock</span>,<span class="dv">1</span>), (<span class="fl">180.0</span>,<span class="dt">NormalBlock</span>,<span class="dv">2</span>), (<span class="fl">260.0</span>,<span class="dt">NormalBlock</span>,<span class="dv">2</span>)]]
bulletRadius <span class="fu">=</span> <span class="fl">3.0</span>
bulletSpeed <span class="fu">=</span> (<span class="fl">0.0</span>, <span class="fu">-</span><span class="fl">10.0</span>)
bulletColor <span class="fu">=</span> <span class="dt">Color</span> <span class="fl">0.0</span> <span class="fl">0.5</span> <span class="fl">0.0</span> <span class="fl">1.0</span>
<span class="co">-- technical constants</span>
leftKeyCode <span class="fu">=</span> <span class="dv">37</span>
rightKeyCode <span class="fu">=</span> <span class="dv">39</span>
startKeyCode <span class="fu">=</span> <span class="dv">13</span>
fireKeyCode <span class="fu">=</span> <span class="dv">32</span>
canvasName <span class="fu">=</span> <span class="st">"canvas4"</span></code></pre>
<p>The canvas name is the same name as defined in the outer html where the canvas is located.</p>
<h2 id="startup-and-key-events">Startup and key events</h2>
<p>As said earlier, we step the main wire on every key event. But besides that the key event and startup functions look very similar to the <a href="https://jshaskell.blogspot.de/2012/09/breakout.html">last post</a>. Also the drawing function has been extended to draw bullets and fading blocks. To produce the game state to draw, the wire is step with "Update". See <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/6_BreakoutImproved/code/BreakoutImproved.hs">here</a> if you want to see the code.</p>
<h2 id="key-events">Key events</h2>
<p>In netwire an Event is a Wire that behaves as the identity wire when the event occurs and inhibits when the event does not occure. There are many functions to create events in <a href="https://hackage.haskell.org/packages/archive/netwire/4.0.5/doc/html/Control-Wire-Prefab-Event.html">netwire</a>. Most require the inhibition type of the wire to be a monoid. That is very useful for switching on events. For now just accept that, you will see later.</p>
<p>So we create events that produce when the input event is a certain key down or release event:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">keyPress ::</span> (<span class="kw">Monad</span> m, <span class="dt">Monoid</span> e) <span class="ot">=></span> <span class="dt">Int</span> <span class="ot">-></span> <span class="dt">Event</span> e m <span class="dt">InputEvent</span>
keyPress code <span class="fu">=</span> when (<span class="fu">==</span><span class="dt">KeyDown</span> code)
<span class="ot">keyRelease ::</span> (<span class="kw">Monad</span> m, <span class="dt">Monoid</span> e) <span class="ot">=></span> <span class="dt">Int</span> <span class="ot">-></span> <span class="dt">Event</span> e m <span class="dt">InputEvent</span>
keyRelease code <span class="fu">=</span> when (<span class="fu">==</span><span class="dt">KeyUp</span> code)</code></pre>
<p>now we can write a wire that returns a different value depending on if a key is pressed:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Function</span> <span class="kw">as</span> <span class="dt">F</span>
<span class="ot">valueFromKeyDown ::</span> (<span class="kw">Monad</span> m, <span class="dt">Monoid</span> e) <span class="ot">=></span> <span class="dt">Int</span> <span class="ot">-></span> a <span class="ot">-></span> a <span class="ot">-></span> <span class="dt">Wire</span> e m <span class="dt">InputEvent</span> a
valueFromKeyDown code upValue downValue <span class="fu">=</span> F.fix (\start <span class="ot">-></span>
pure upValue <span class="fu">.</span> notE (keyPress code) <span class="fu">--></span>
pure downValue <span class="fu">.</span> notE (keyRelease code) <span class="fu">--></span>
start)</code></pre>
<ul>
<li>The "-->" operator is the infix version of "andThen". It takes two wires and behaves like the first until that inhibits. After that it behaves like the second.</li>
<li>pure takes a value and makes a constant wire from it</li>
<li>if you do not know fix, read <a href="https://en.wikibooks.org/wiki/Haskell/Fix_and_recursion">here</a>. Here it is used to loop back the chain of wires to the beginnig.</li>
</ul>
<h2 id="the-paddle">The paddle</h2>
<p>The speed of the paddle is direct transformation of the input state while the paddle wire integrates the paddle speed bounding it the the limits of the screen.</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">paddleWire ::</span> (<span class="kw">Monad</span> m, <span class="dt">Monoid</span> e) <span class="ot">=></span> <span class="dt">Wire</span> e m <span class="dt">InputEvent</span> <span class="dt">Paddle</span>
paddleWire <span class="fu">=</span> <span class="dt">Paddle</span> <span class="fu"><$></span> (integralLim1_ bound initPaddleXPos <span class="fu"><<<</span> (paddleSpeedWire <span class="fu">&&&</span> pure ()))
<span class="kw">where</span>
bound _ _ pos <span class="fu">=</span> <span class="fu">max</span> <span class="fl">0.0</span> <span class="fu">$</span> <span class="fu">min</span> (screenWidth<span class="fu">-</span>paddleWidth) pos
<span class="ot">paddleSpeedWire ::</span> (<span class="kw">Monad</span> m, <span class="dt">Monoid</span> e) <span class="ot">=></span> <span class="dt">Wire</span> e m <span class="dt">InputEvent</span> <span class="dt">Double</span>
paddleSpeedWire <span class="fu">=</span> (valueFromKeyDown leftKeyCode <span class="fl">0.0</span> (<span class="fu">-</span>paddleSpeed))
<span class="fu">+</span>
(valueFromKeyDown rightKeyCode <span class="fl">0.0</span> paddleSpeed)</code></pre>
<h2 id="the-ball">The ball</h2>
<p>Similar as in the <a href="https://jshaskell.blogspot.de/2012/09/breakout.html">last Post</a>, the ball moves with constant speed and reacts to collision events.</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">accum1Fold ::</span> (<span class="kw">Monad</span> m) <span class="ot">=></span> (b <span class="ot">-></span> a <span class="ot">-></span> b) <span class="ot">-></span> b <span class="ot">-></span> <span class="dt">Wire</span> e m [a] b
accum1Fold f <span class="fu">init</span> <span class="fu">=</span> accum1 step <span class="fu">init</span>
<span class="kw">where</span>
step <span class="fu">last</span> <span class="kw">as</span> <span class="fu">=</span> foldl' f <span class="fu">last</span> <span class="kw">as</span>
<span class="ot">ballSpeedWire ::</span> (<span class="kw">Monad</span> m) <span class="ot">=></span> <span class="dt">Wire</span> e m [<span class="dt">Collision</span>] <span class="dt">Vector</span>
ballSpeedWire <span class="fu">=</span> accum1Fold (collide) initBallSpeed
<span class="kw">where</span>
collide v0 (<span class="dt">Collision</span> n) <span class="fu">=</span> v0 <span class="fu">-</span> (<span class="fl">2.0</span> <span class="fu">*</span> (n <span class="fu"><.></span> v0)) <span class="fu">*^</span> n
<span class="ot">ballWire ::</span> (<span class="kw">Monad</span> m) <span class="ot">=></span> <span class="dt">Wire</span> e m [<span class="dt">Collision</span>] <span class="dt">Ball</span>
ballWire <span class="fu">=</span> (<span class="dt">Ball</span> <span class="fu"><$></span> integral1_ initBallPos) <span class="fu">.</span> ballSpeedWire <span class="fu"><*></span> ballSpeedWire</code></pre>
<p>Notice the use of accum1. In difference to accum, accum1 does not delay its output by one invocation. accum1Fold does the same as accum1 but takes a list as input over which it folds. Here it is used to fold over the incomming collision events.</p>
<p>What happens when the ball collides with an object? Assuming the collision is fully elastic, the velocity along the collision normal is inverted. The velocity (v0) along the collision normal (n) is <n,v0> (the scalar product of n and v0). Expressed with vector space, this is n <.> v0. To invert this part of v0, we have to substract this twice from v0. This gives us: v0 - (2.0 * (n <.> v0)) *^ n.</p>
<h2 id="blocks">Blocks</h2>
<p>A block behaves as its initial state, removing a live whenever it is hit (its input is not Nothing). When the lives are out, the block changes into the Dying state. And fades out in 30.0 "time units". Afterwards the block wire inhibts (so it is removed from the set).</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">blockWire ::</span> (<span class="kw">Monad</span> m, <span class="dt">Monoid</span> e) <span class="ot">=></span> <span class="dt">Block</span> <span class="ot">-></span> <span class="dt">Wire</span> e m (<span class="dt">Maybe</span> <span class="dt">Collision</span>) <span class="dt">Block</span>
blockWire <span class="fu">init</span> <span class="fu">=</span> while blockAlive <span class="fu">.</span> accum1 update <span class="fu">init</span> <span class="fu">--></span>
<span class="dt">Block</span> (blockType <span class="fu">init</span>) (blockPos <span class="fu">init</span>) <span class="fu"><$></span> (<span class="dt">Dying</span> <span class="fu"><$></span> (pure <span class="fl">1.0</span>) <span class="fu">-</span> (time <span class="fu">/</span> (pure <span class="fl">30.0</span>))) <span class="fu">.</span> for <span class="fl">30.0</span>
<span class="kw">where</span>
update old <span class="kw">Nothing</span> <span class="fu">=</span> old
update old<span class="fu">@</span>(<span class="dt">Block</span> _ _ (<span class="dt">Alive</span> l)) _ <span class="fu">=</span> old { blockState <span class="fu">=</span> <span class="dt">Alive</span> (l <span class="fu">-</span> <span class="dv">1</span>) }
blockAlive (<span class="dt">Block</span> _ _ (<span class="dt">Alive</span> l)) <span class="fu">=</span> l <span class="fu">></span> <span class="dv">0</span></code></pre>
<p>Notice the expression "(pure 1.0) - (time / (pure 30.0)))" for the fading level. We can use "-" and "/" because wires are members of the Fractional and Num type classes. We could even leave out the "pure" and write "(1.0) - (time / (30.0)))". At present this does not work with haste because "framRational" needs some not yet supported primOps (see <a href="https://github.com/valderman/haste-compiler/issues/32">here</a>).</p>
<p>When a "PowerBlock" is destroyed, the player is supposed to gain ammo. Therefore there is a blockAmmoWire that returns the number of ammo the player should gain. For a normal block it returns always 0. For a PowerBlock it returns 0 except the moment the block is destoryed (the input is not Nothing).</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">blockAmmoWire ::</span> (<span class="kw">Monad</span> m, <span class="dt">Monoid</span> e) <span class="ot">=></span> <span class="dt">Block</span> <span class="ot">-></span> <span class="dt">Wire</span> e m (<span class="dt">Maybe</span> <span class="dt">Collision</span>) <span class="dt">Int</span>
blockAmmoWire (<span class="dt">Block</span> <span class="dt">PowerBlock</span> _ _) <span class="fu">=</span> (pure <span class="dv">0</span>) <span class="fu">.</span> while (isNothing) <span class="fu">--></span> once <span class="fu">.</span> (pure <span class="dv">1</span>) <span class="fu">--></span> pure <span class="dv">0</span>
blockAmmoWire _ <span class="fu">=</span> (pure <span class="dv">0</span>)
<span class="ot">blockWithAmmoWire ::</span> (<span class="kw">Monad</span> m, <span class="dt">Monoid</span> e) <span class="ot">=></span> <span class="dt">Block</span> <span class="ot">-></span> <span class="dt">Wire</span> e m (<span class="dt">Maybe</span> <span class="dt">Collision</span>) (<span class="dt">Int</span>,<span class="dt">Block</span>)
blockWithAmmoWire b <span class="fu">=</span> blockAmmoWire b <span class="fu">&&&</span> blockWire b</code></pre>
<p>Isn't it nice how easily this can be expressed with "-->"?</p>
<p>Now we create a set of blocks using "shrinkingMap":</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">blocksWire ::</span> (<span class="kw">Monad</span> m, <span class="dt">Monoid</span> e) <span class="ot">=></span> <span class="dt">Wire</span> e m (<span class="dt">M.Map</span> <span class="dt">Int</span> <span class="dt">Collision</span>) (<span class="dt">Int</span>,[(<span class="dt">Int</span>,<span class="dt">Block</span>)])
blocksWire <span class="fu">=</span> (shrinkingMap <span class="fu">$</span> <span class="fu">map</span> blockWithAmmoWire initBlocks) <span class="fu">>>></span> (arr reorder)
<span class="kw">where</span>
reorder <span class="kw">as</span> <span class="fu">=</span> (<span class="fu">sum</span> <span class="fu">$</span> <span class="fu">map</span> (<span class="fu">fst</span> <span class="fu">.</span> <span class="fu">snd</span>) <span class="kw">as</span>, <span class="fu">map</span> (\j <span class="ot">-></span> (<span class="fu">fst</span> j, <span class="fu">snd</span> <span class="fu">$</span> <span class="fu">snd</span> j)) <span class="kw">as</span>)</code></pre>
<p>The output of "(shrinkingMap $ map blockWithAmmoWire initBlocks)" is [(id,(ammo,block))] where "id" is the id of the corresponding block, "ammo" the ammo given by the block and "block" its state.</p>
<p>But what we want is (sumAmmo, [(id,block)]) with sumAmmo being the sum over all ammo. That is what reaorder takes car of.</p>
<h2 id="bullets">Bullets</h2>
<p>Bullets simply move up while</p>
<ul>
<li>They do not collide</li>
<li>They are not out of the screen</li>
</ul>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">bulletWire ::</span> (<span class="kw">Monad</span> m, <span class="dt">Monoid</span> e) <span class="ot">=></span> <span class="dt">Bullet</span> <span class="ot">-></span> <span class="dt">Wire</span> e m (<span class="dt">Maybe</span> <span class="dt">Collision</span>) <span class="dt">Bullet</span>
bulletWire (<span class="dt">Bullet</span> <span class="fu">init</span>) <span class="fu">=</span> while bulletAlive <span class="fu">.</span> (<span class="dt">Bullet</span> <span class="fu"><$></span> (pure bulletSpeed <span class="fu">>>></span> integral1_ <span class="fu">init</span>)) <span class="fu">.</span> while (isNothing)
<span class="kw">where</span>
bulletAlive (<span class="dt">Bullet</span> (x,y)) <span class="fu">=</span> y <span class="fu">></span> <span class="fl">0.0</span>
<span class="ot">bulletsWire ::</span> (<span class="kw">Monad</span> m, <span class="dt">Monoid</span> e) <span class="ot">=></span> <span class="dt">Wire</span> e m (<span class="dt">M.Map</span> <span class="dt">Int</span> <span class="dt">Collision</span>,[<span class="dt">Bullet</span>]) [(<span class="dt">Int</span>,<span class="dt">Bullet</span>)]
bulletsWire <span class="fu">=</span> dynamicSetMap bulletWire []</code></pre>
<h2 id="gun">Gun</h2>
<p>The gun gets a set of bullets as input (these are the fire requests) and an integer with the amount of new ammo. It outputs the bullets that really have been fired and the gun state</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">gunWire ::</span> (<span class="dt">MonadFix</span> m) <span class="ot">=></span> <span class="dt">Wire</span> e m ([<span class="dt">Bullet</span>],<span class="dt">Int</span>) ([<span class="dt">Bullet</span>],<span class="dt">Gun</span>)
gunWire <span class="fu">=</span> proc (bs,new) <span class="ot">-></span> <span class="kw">do</span>
rec
<span class="kw">let</span> fires <span class="fu">=</span> <span class="fu">take</span> ammo bs
ammo <span class="ot"><-</span> accum (<span class="fu">+</span>) (ammo initGun) <span class="fu">-<</span> new <span class="fu">-</span> (<span class="fu">length</span> fires)
returnA <span class="fu">-<</span> (fires,<span class="dt">Gun</span> ammo)</code></pre>
<h2 id="collecting-collision-information">Collecting collision information</h2>
<p>We define some helper functions for collecting collisions betwen the ball, the paddle, the walls, blocks and bullets:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">fromMaybeList ::</span> <span class="kw">Ord</span> a <span class="ot">=></span> [(a,<span class="dt">Maybe</span> b)] <span class="ot">-></span> <span class="dt">M.Map</span> a b
fromMaybeList [] <span class="fu">=</span> M.empty
fromMaybeList ((k,<span class="kw">Nothing</span>)<span class="fu">:</span>xs) <span class="fu">=</span> fromMaybeList xs
fromMaybeList ((k,<span class="kw">Just</span> v)<span class="fu">:</span>xs) <span class="fu">=</span> M.insert k v (fromMaybeList xs)
<span class="ot">calcBallBlockColls ::</span> <span class="dt">Ball</span> <span class="ot">-></span> [(<span class="dt">Int</span>,<span class="dt">Block</span>)] <span class="ot">-></span> <span class="dt">M.Map</span> <span class="dt">Int</span> <span class="dt">Collision</span>
calcBallBlockColls ball <span class="fu">=</span> fromMaybeList <span class="fu">.</span> <span class="fu">map</span> (\(<span class="fu">id</span>,block) <span class="ot">-></span> (<span class="fu">id</span>,circleRoundedRectCollision ball block))
<span class="ot">calcBallWallColls ::</span> <span class="dt">Ball</span> <span class="ot">-></span> [<span class="dt">Collision</span>]
calcBallWallColls (<span class="dt">Ball</span> (bx,by) _) <span class="fu">=</span> <span class="fu">map</span> <span class="fu">snd</span> <span class="fu">$</span> <span class="fu">filter</span> (<span class="fu">fst</span>) <span class="fu">$</span> [
(bx <span class="fu"><=</span> <span class="dv">0</span> , <span class="dt">Collision</span> (<span class="fl">1.0</span> , <span class="fl">0.0</span>)),
(bx <span class="fu">>=</span> screenWidth, <span class="dt">Collision</span> (<span class="fu">-</span><span class="fl">1.0</span>, <span class="fl">0.0</span>)),
(by <span class="fu"><=</span> <span class="dv">0</span> , <span class="dt">Collision</span> (<span class="fl">0.0</span> , <span class="fl">1.0</span>))
]
<span class="ot">calcBallPaddleColls ::</span> <span class="dt">Ball</span> <span class="ot">-></span> <span class="dt">Paddle</span> <span class="ot">-></span> [<span class="dt">Collision</span>]
calcBallPaddleColls b p <span class="fu">=</span>
maybeToList <span class="fu">$</span> circleRoundedRectCollision b p
<span class="ot">pairUp ::</span> [a] <span class="ot">-></span> [b] <span class="ot">-></span> [(a,b)]
pairUp <span class="kw">as</span> bs <span class="fu">=</span> [(a,b) <span class="fu">|</span> a <span class="ot"><-</span> <span class="kw">as</span>, b <span class="ot"><-</span> bs]
<span class="ot">calcBlockBulletColls ::</span> [(<span class="dt">Int</span>,<span class="dt">Block</span>)] <span class="ot">-></span> [(<span class="dt">Int</span>,<span class="dt">Bullet</span>)] <span class="ot">-></span> (<span class="dt">M.Map</span> <span class="dt">Int</span> <span class="dt">Collision</span>,<span class="dt">M.Map</span> <span class="dt">Int</span> <span class="dt">Collision</span>)
calcBlockBulletColls blocks bullets <span class="fu">=</span> foldl' buildColls (M.empty, M.empty) <span class="fu">$</span> pairUp blocks bullets
<span class="kw">where</span>
buildColls (blList, buList) ((blId,block), (buId, bullet)) <span class="fu">=</span> <span class="kw">case</span> circleRoundedRectCollision bullet block <span class="kw">of</span>
<span class="kw">Nothing</span> <span class="ot">-></span> (blList, buList)
<span class="kw">Just</span> c <span class="ot">-></span> (M.insert blId c blList, M.insert buId c buList)</code></pre>
<h1 id="putting-it-all-together-the-main-wire">Putting it all together, the main wire</h1>
<h2 id="switching-game-state">Switching game state</h2>
<p>Let's first look at the outer wire, that manages when the game starts and when to show the start screen. It should behave like this:</p>
<ul>
<li>In the beginnig it shows "Press Enter to start (click canvas to focus)". When the user pressed enter the game is started.</li>
<li>When the player looses, is shows "Sorry, you loose! Press Enter to restart." and lets the user press enter to restart.</li>
<li>Similar when the player wins, with the message "Congratulations, you won! Press Enter to restart."</li>
</ul>
<p>So when the game ends we need to switch differently depending on if the game was lost or won. Remember that the inhibition if wires can be used to switch wires (for example with "-->"). If we want to have different wire we have to encode this in the inhibition Type (see also this <a href="https://www.haskell.org/pipermail/beginners/2012-November/010941.html">thread</a>):</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span> <span class="dt">GameEnd</span> <span class="fu">=</span> <span class="dt">Win</span> <span class="fu">|</span> <span class="dt">Loose</span> <span class="fu">|</span> <span class="dt">None</span>
<span class="kw">instance</span> <span class="dt">Monoid</span> <span class="dt">GameEnd</span> <span class="kw">where</span>
mempty <span class="fu">=</span> <span class="dt">None</span>
mappend x <span class="dt">None</span> <span class="fu">=</span> x
mappend <span class="dt">None</span> x <span class="fu">=</span> x
mappend _ <span class="dt">Win</span> <span class="fu">=</span> <span class="dt">Win</span>
mappend <span class="dt">Win</span> _ <span class="fu">=</span> <span class="dt">Win</span>
mappend _ _ <span class="fu">=</span> <span class="dt">Loose</span>
<span class="kw">type</span> <span class="dt">MainWireType</span> <span class="fu">=</span> <span class="dt">Wire</span> <span class="dt">GameEnd</span> <span class="dt">Identity</span> <span class="dt">InputEvent</span> (<span class="dt">Maybe</span> <span class="dt">GameState</span>)</code></pre>
<p>The inhibition value must be a Monoid, because that is what most switches and events require. Now we can use this with switchBy:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell">mainWire <span class="fu">=</span> switchBy start (start <span class="dt">None</span>)
<span class="kw">where</span>
start <span class="dt">None</span> <span class="fu">=</span> startScreenWire <span class="st">"Press Enter to start (click canvas to focus)"</span> <span class="fu">--></span> mainGameWire
start <span class="dt">Win</span> <span class="fu">=</span> startScreenWire <span class="st">"Congratulations, you won! Press Enter to restart."</span> <span class="fu">--></span> mainGameWire
start <span class="dt">Loose</span> <span class="fu">=</span> startScreenWire <span class="st">"Sorry, you loose! Press Enter to restart."</span> <span class="fu">--></span> mainGameWire</code></pre>
<p>Now the mainGameWire only has to inhibit when the game is lost, or won. This is done with these wires:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">looseWire ::</span> (<span class="kw">Monad</span> m) <span class="ot">=></span> <span class="dt">Wire</span> <span class="dt">GameEnd</span> m <span class="dt">Ball</span> <span class="dt">Ball</span>
looseWire <span class="fu">=</span> unless ballOut <span class="fu">--></span> inhibit <span class="dt">Loose</span>
<span class="kw">where</span>
ballOut (<span class="dt">Ball</span> (x,y) _) <span class="fu">=</span> y <span class="fu">></span> screenHeight
<span class="ot">winWire ::</span> (<span class="kw">Monad</span> m) <span class="ot">=></span> <span class="dt">Wire</span> <span class="dt">GameEnd</span> m [<span class="dt">Block</span>] [<span class="dt">Block</span>]
winWire <span class="fu">=</span> (once <span class="fu">--></span> unless <span class="fu">null</span>) <span class="fu">--></span> inhibit <span class="dt">Win</span></code></pre>
<p>The "once" in the win wire is necessary because in the first invocation of the main wire there are no blocks.</p>
<h2 id="the-main-game">The main game</h2>
<p>This is the only place, where we use arrow syntax:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">mainGameWire ::</span> <span class="dt">MainWireType</span>
mainGameWire <span class="fu">=</span> proc input <span class="ot">-></span> <span class="kw">do</span>
paddle <span class="ot"><-</span> paddleWire <span class="fu">-<</span> input
<span class="kw">let</span> newFR old
<span class="fu">|</span> input <span class="fu">==</span> <span class="dt">Update</span> <span class="fu">=</span> []
<span class="fu">|</span> input <span class="fu">==</span> <span class="dt">KeyDown</span> fireKeyCode <span class="fu">=</span> (createBullet paddle)<span class="fu">:</span>old
<span class="fu">|</span> <span class="fu">otherwise</span> <span class="fu">=</span> old
fireRequests <span class="ot"><-</span> accum (<span class="fu">flip</span> (<span class="fu">$</span>)) [] <span class="fu">-<</span> newFR
<span class="kw">if</span> input <span class="fu">==</span> <span class="dt">Update</span> <span class="kw">then</span> <span class="kw">do</span>
rec
<span class="kw">let</span> validCollDir (<span class="dt">Collision</span> n) <span class="fu">=</span> n <span class="fu"><.></span> ballSpeed oldBall <span class="fu"><</span> <span class="fl">0.0</span>
ballBlockColls <span class="fu">=</span> M.filter validCollDir <span class="fu">$</span> calcBallBlockColls oldBall oldBlocks
ballWallColls <span class="fu">=</span> calcBallWallColls oldBall
ballPaddleColls <span class="fu">=</span> <span class="fu">filter</span> validCollDir <span class="fu">$</span> calcBallPaddleColls oldBall paddle
(blockBulletColls,bulletBlockColls) <span class="fu">=</span> calcBlockBulletColls oldBlocks oldBullets
ball <span class="ot"><-</span> ballWire <span class="fu">-<</span> ballWallColls <span class="fu">++</span> ballPaddleColls <span class="fu">++</span> (M.elems ballBlockColls)
oldBall <span class="ot"><-</span> delay initBall <span class="fu">-<</span> ball
_ <span class="ot"><-</span> looseWire <span class="fu">-<</span> oldBall
(newAmmo,blocks) <span class="ot"><-</span> blocksWire <span class="fu">-<</span> ballBlockColls <span class="ot">`M.union`</span> blockBulletColls
oldBlocks <span class="ot"><-</span> delay <span class="fu">$</span> [] <span class="fu">-<</span> blocks
_ <span class="ot"><-</span> winWire <span class="fu">-<</span> (<span class="fu">map</span> <span class="fu">snd</span> oldBlocks)
(newBullets,gun) <span class="ot"><-</span> gunWire <span class="fu">-<</span> (fireRequests,newAmmo)
bullets <span class="ot"><-</span> bulletsWire <span class="fu">-<</span> (bulletBlockColls,newBullets)
oldBullets <span class="ot"><-</span> delay <span class="fu">$</span> [] <span class="fu">-<</span> bullets
returnA <span class="fu">-<</span> <span class="kw">Just</span> <span class="fu">$</span> <span class="dt">GameState</span> paddle gun ball (<span class="fu">map</span> <span class="fu">snd</span> blocks) (<span class="fu">map</span> <span class="fu">snd</span> bullets)
<span class="kw">else</span>
returnA <span class="fu">-<</span> <span class="kw">Nothing</span></code></pre>
<p>First the paddle is updated using the input. The fireRequests are build by accumulating all presses of the fireing key. These are later filtered in the gun, so that no more bullets are fired than there is ammo. When an Update event is issued the queue is purged. Remember that accum delays by one, so that when the input event is "Update", fireRequests is purged one invocation later.</p>
<p>The rest of the wire is only invoked when the input event is "Update" (otherwise Nothing is returned). Note that we can use "if" to invoke a different wire depending on some condition. Creating the collision data is done using the functions introduced earlier. Note the filter with "validCollDir". Due to the rounded edges, it can happen that the ball collides with a block in a way that the ball is not outside the block the next frame. To prevent "double collisions" all those collision events, that are not directed against the moving direction of the ball are filtered.</p>
<p>If we would have used "accum" instead of "accum1" in a couple of places, the output of all the game objects would be delayed by 1 and we would not need the "old..." objects. This is personal preference, I find the use of the "old.." objects more transparent to what is happening.</p>
<h1 id="conclusion">Conclusion</h1>
<p>I am getting more confortable with haskell and its getting easier for me to read haskell code. Netwire seems to be a nice library, but I feel like I have so far only scratched its surface. I wonder what cool things one could do if one would use the inner monad. Also I wonder how Arrowrized FRP compares with FRP without arrows. Unfortantly <a href="https://www.haskell.org/haskellwiki/Reactive-banana">reactive banana</a> does not yet work with haste. I had a quick peek at <a href="https://hackage.haskell.org/package/elerea">elerea</a> but it also needs some PrimOps not supported by haste.</p>
<p>Again: I encourage you to comment if you think something could be done better. For a lot of things I might not use a better alternative because I am simply not aware of it. After all I am still a haskell beginner.</p>
<a rel="license" href="https://creativecommons.org/licenses/by/3.0/de/deed.en_US"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/3.0/de/88x31.png" /></a><br /><span xmlns:dct="https://purl.org/dc/terms/" href="https://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">Writing JavaScript games in Haskell</span> by <a xmlns:cc="https://creativecommons.org/ns#" href="https://jshaskell.blogspot.de/" property="cc:attributionName" rel="cc:attributionURL">Nathan Hüsken</a> is licensed under a <a rel="license" href="https://creativecommons.org/licenses/by/3.0/de/deed.en_US">Creative Commons Attribution 3.0 Germany License</a>. Nathan Hüsken https://www.blogger.com/profile/09614845657227846437 noreply@blogger.com 9 tag:blogger.com,1999:blog-6355249120999252418.post-7418943627649072433 2012-09-18T07:27:00.001-07:00 2013-08-21T23:08:14.808-07:00 Breakout <p>Hi, welcome to the 5th article of this blog.</p>
<p>I am very exited! In the <a href="https://jshaskell.blogspot.de/2012/09/pong.html">last Post</a> we wrote a Pong like ... well ... let's call it javascript application. In this post I have expanded it by the following properties:</p>
<ul>
<li>The game does not start until you hit enter.</li>
<li>There are blocks that can be hit by the ball and disappear.</li>
<li>There are even blocks with 2 lives (dark blue) that turn into normal blocks on the first hit.</li>
<li>The game stops when the ball leaves the canvas downward.</li>
</ul>
<p>This means, there is a goal and there is a game over situation. So at this point one could actually call it a game. And it even has a start screen ... did I mention that I am exitied? /,anma But, now, as always, here is a preview. As always you have to click the canvas to get input focus. If you are not viewing this blog article on blogspot and the application does not work, try the original <a href="https://jshaskell.blogspot.de/2012/09/breakout.html">article page</a>.</p>
<p><strong>Note:</strong> This currently only works if you are viewing this article only (not in the flow of the complete blog). I am working on the problem ...</p>
<script src="https://rawgithub.com/RudolfVonKrugstein/jshaskell-blog/master/5_Breakout/code/compiled/Breakout.js" type="text/javascript"></script>
<canvas height="400" id="canvas3" style="background-color: white;" width="600" tabindex="1"></canvas>
<p>But let me tell you how I did it :).</p>
<p>By the way, this post assumes that you have read the <a href="https://jshaskell.blogspot.de/2012/09/pong.html">last Post</a>.</p>
<h1 id="more-coroutine-helpers">More Coroutine helpers</h1>
<p>There are two aspects of this game (yes, game!)</p>
<ul>
<li>The blocks are a dynamic set of objects, that disappear as the game progresses</li>
<li>There are different "game states" (the start screen and the actual game)</li>
</ul>
<p>So this has been added to <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/5_Breakout/code/Coroutine.hs">Coroutine.hs</a></p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="co">-- manages a set of coroutines which are deletet when returning Nothing</span>
<span class="ot">manager ::</span> [<span class="dt">Coroutine</span> a (<span class="dt">Maybe</span> b)] <span class="ot">-></span> <span class="dt">Coroutine</span> [a] [b]
manager <span class="fu">cos</span> <span class="fu">=</span> <span class="dt">Coroutine</span> <span class="fu">$</span> \is <span class="ot">-></span>
<span class="kw">let</span> res <span class="fu">=</span> <span class="fu">map</span> (\(co, i) <span class="ot">-></span> runC co i) <span class="fu">$</span> <span class="fu">zip</span> <span class="fu">cos</span> is
res' <span class="fu">=</span> <span class="fu">filter</span> (isJust <span class="fu">.</span> <span class="fu">fst</span>) res
(result, cos') <span class="fu">=</span> <span class="fu">unzip</span> res'
<span class="kw">in</span> (catMaybes result, manager cos')
<span class="co">-- switcher, starts with a specific coroutine and switches whenever a new coroutine is send via an event</span>
<span class="ot">switch ::</span> <span class="dt">Coroutine</span> a b <span class="ot">-></span> <span class="dt">Coroutine</span> (<span class="dt">Event</span> (<span class="dt">Coroutine</span> a b), a) b
switch <span class="fu">init</span> <span class="fu">=</span> <span class="dt">Coroutine</span> <span class="fu">$</span> \(e,i) <span class="ot">-></span>
<span class="kw">let</span> init' <span class="fu">=</span> <span class="fu">last</span> <span class="fu">$</span> <span class="fu">init</span> <span class="fu">:</span> e <span class="co">--the last coroutine sent through</span>
(o, init'') <span class="fu">=</span> runC init' i
<span class="kw">in</span> (o, switch init'')
<span class="co">-- replace the contents of an event</span>
<span class="ot">(<$) ::</span> <span class="dt">Event</span> a <span class="ot">-></span> b <span class="ot">-></span> <span class="dt">Event</span> b
(<span class="fu"><$</span>) events content <span class="fu">=</span> <span class="fu">map</span> (\_ <span class="ot">-></span> content) events</code></pre>
<p><strong>manager:</strong> The manger is for managing the blocks. Every blocks state is produced by a coroutine, and in the beginning there is a set of blocks in the game (first parameter to manager). The manager distributes its input to all its coroutines. So the input list should have the same length. The output if each coroutines are collected in a list which is the output of the manager.</p>
<p>Every block Coroutine returns "Nothing" when the block is destroyed, the manager than removes the block from the set.</p>
<p>Note that at present there is no way of inserting new blocks in the manager, it is not needed in this game.</p>
<p><strong>switch:</strong> Switch allows us to switch between different game states, which all are described by coroutines of the same type.</p>
<p>Initially switch behaves as the init Coroutine (its first parameter) with an extra parameter holding events with other Coroutines. Whenever one of these events occurs, switch switches to the coroutine carried in the event.</p>
<p><strong><$:</strong> This is a operator. When applied to an event it replaces the contents of the event with the second parameter. We need this to replace the content of the KeyDown event with the main Coroutine when the start key is pressed. You will see!</p>
<h1 id="from-pong-to-breakout">From Pong to Breakout</h1>
<p>All very exiting, but the real excitement start now. The main source file <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/5_Breakout/code/Breakout.hs">Breakout.hs</a> is based on the <a href="https://jshaskell.blogspot.de/2012/09/pong.html">last posts</a> <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/4_Pong/code/Pong.hs">Pong.hs</a>. Here I will go over the differences.</p>
<h2 id="definitions">Definitions</h2>
<p>The game state needs to reflect the blocks and the start screen. It has changed to:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span> <span class="dt">PlayerState</span> <span class="fu">=</span> <span class="dt">PlayerState</span> {<span class="ot">xPos ::</span> <span class="dt">Double</span>}
<span class="kw">data</span> <span class="dt">BallState</span> <span class="fu">=</span> <span class="dt">BallState</span> {<span class="ot">ballPos ::</span> <span class="dt">Vector</span>}
<span class="kw">data</span> <span class="dt">BlockState</span> <span class="fu">=</span> <span class="dt">BlockState</span> {<span class="ot">blockPos ::</span> <span class="dt">Vector</span>,<span class="ot"> blockLives ::</span> <span class="dt">Int</span>}
<span class="kw">data</span> <span class="dt">GameState</span> <span class="fu">=</span> <span class="dt">GameState</span> {<span class="ot">player ::</span> <span class="dt">PlayerState</span>,
<span class="ot"> ball ::</span> <span class="dt">BallState</span>,
<span class="ot"> blocks ::</span> [<span class="dt">BlockState</span>]}
<span class="fu">|</span> <span class="dt">StartScreen</span>
<span class="kw">data</span> <span class="dt">BallCollision</span> <span class="fu">=</span> <span class="dt">LeftBounce</span> <span class="fu">|</span> <span class="dt">RightBounce</span> <span class="fu">|</span> <span class="dt">UpBounce</span> <span class="fu">|</span> <span class="dt">DownBounce</span>
<span class="kw">data</span> <span class="dt">BlockCollision</span> <span class="fu">=</span> <span class="dt">BlockCollision</span>
<span class="kw">data</span> <span class="dt">Rect</span> <span class="fu">=</span> <span class="dt">Rect</span> {<span class="ot"> x::</span><span class="dt">Double</span>,<span class="ot"> y::</span><span class="dt">Double</span>,<span class="ot"> width ::</span><span class="dt">Double</span>,<span class="ot"> height::</span><span class="dt">Double</span>}</code></pre>
<p>The BlockState has been added, which contains the block position and the number of lives (1 or 2) of the block. The GameState has been expanded by a list of BlockStates AND can be just the start screen (when the game has not started).</p>
<p>BlockCollision is a type for creating Events where the block collides with the ball. A type synonym to () would also work, but I choose this more verbose way.</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell">blockWidth <span class="fu">=</span> <span class="fl">60.0</span>
blockHeight <span class="fu">=</span> <span class="fl">20.0</span>
blockColor1live <span class="fu">=</span> <span class="st">"blue"</span>
blockColor2live <span class="fu">=</span> <span class="st">"darkblue"</span>
initBlockStates <span class="fu">=</span> [<span class="dt">BlockState</span> (x,y) lives <span class="fu">|</span> x <span class="ot"><-</span> [<span class="fl">20.0</span>, <span class="fl">140.0</span>, <span class="fl">240.0</span>, <span class="fl">340.0</span>, <span class="fl">440.0</span>, <span class="fl">520.0</span>], (y, lives) <span class="ot"><-</span> [(<span class="fl">60.0</span>,<span class="dv">2</span>), (<span class="fl">100.0</span>,<span class="dv">1</span>), (<span class="fl">140.0</span>,<span class="dv">2</span>), (<span class="fl">180.0</span>,<span class="dv">1</span>), (<span class="fl">220.0</span>,<span class="dv">1</span>), (<span class="fl">260.0</span>,<span class="dv">1</span>)]]
restartKeyCode <span class="fu">=</span> <span class="dv">32</span>
canvasName <span class="fu">=</span> <span class="st">"canvas3"</span></code></pre>
<p>The color of the blocks depend if they have 1 or 2 lives. initBlockStates describes the blocks as the game starts. They are evenly spaced, 6 in x and 6 in y directions. 2 of the y rows have 2 lives, the rest 1.</p>
<p>The restartKeyCode is the key code of the enter bar and the canvasName is the name of the canvas in the html code of <a href="https://jshaskell.blogspot.de/2012/09/breakout.html">this</a> blog.</p>
<h2 id="drawing">Drawing</h2>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">draw ::</span> <span class="dt">GameState</span> <span class="ot">-></span> <span class="dt">IO</span> ()
draw <span class="dt">StartScreen</span> <span class="fu">=</span> <span class="kw">do</span>
ctx <span class="ot"><-</span> getContext2d canvasName
clear ctx
<span class="co">-- draw the text</span>
setFillColor ctx <span class="st">"black"</span>
fillText ctx <span class="st">"Press Enter to start --- (click the canvas for input focus)"</span> (screenWidth<span class="fu">/</span><span class="fl">2.0</span> <span class="fu">-</span> <span class="fl">100.0</span>) (screenHeight<span class="fu">/</span><span class="fl">2.0</span>)
draw (<span class="dt">GameState</span> playerState ballState blockStates) <span class="fu">=</span> <span class="kw">do</span>
ctx <span class="ot"><-</span> getContext2d canvasName
clear ctx
<span class="co">-- draw player</span>
setFillColor ctx playerColor
<span class="kw">let</span> pRect <span class="fu">=</span> playerRect playerState
fillRect ctx (x pRect) (y pRect) (width pRect) (height pRect)
<span class="co">--draw blocks</span>
<span class="fu">mapM_</span> (drawBlock ctx) <span class="fu">$</span> blockStates
<span class="co">--draw ball</span>
setFillColor ctx ballColor
<span class="kw">let</span> (x,y) <span class="fu">=</span> ballPos ballState
fillCircle ctx x y ballRadius
<span class="ot">drawBlock ::</span> <span class="dt">Context2D</span> <span class="ot">-></span> <span class="dt">BlockState</span> <span class="ot">-></span> <span class="dt">IO</span> ()
drawBlock ctx bs <span class="fu">=</span> <span class="kw">do</span>
setFillColor ctx (<span class="kw">if</span> blockLives bs <span class="fu">==</span> <span class="dv">1</span> <span class="kw">then</span> blockColor1live <span class="kw">else</span> blockColor2live)
<span class="kw">let</span> r <span class="fu">=</span> blockRect bs
fillRect ctx (x r) (y r) (width r) (height r)</code></pre>
<p>draw pattern matches its argument, to test if it is the start screen. If so, a short message telling the player to press enter is displayed (see <a href="https://www.w3schools.com/html/html5_canvas.asp">fillText</a> in some javascript documentation).</p>
<h2 id="helpers">helpers</h2>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">gameOver ::</span> <span class="dt">GameState</span> <span class="ot">-></span> <span class="dt">Bool</span>
gameOver (<span class="dt">GameState</span> _ (<span class="dt">BallState</span> (_, by)) _) <span class="fu">=</span> by <span class="fu">></span> screenHeight
gameOver _ <span class="fu">=</span> <span class="kw">False</span>
<span class="ot">blockRect ::</span> <span class="dt">BlockState</span> <span class="ot">-></span> <span class="dt">Rect</span>
blockRect (<span class="dt">BlockState</span> (bx,by) _) <span class="fu">=</span> <span class="dt">Rect</span> bx by blockWidth blockHeight</code></pre>
<p>gameOver is a little helper function to test if the ball has left the canvas. It returns False on the start screen.</p>
<p>blockRect returns the rectangle occupied by a block.</p>
<h2 id="main-coroutine">Main coroutine</h2>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">mainCoroutine ::</span> <span class="dt">MainCoroutineType</span>
mainCoroutine <span class="fu">=</span> proc inEvents <span class="ot">-></span> <span class="kw">do</span>
rec
<span class="kw">let</span> startEvent <span class="fu">=</span> <span class="fu">filter</span> (\ke <span class="ot">-></span> ke <span class="fu">==</span> <span class="dt">KeyUp</span> restartKeyCode) inEvents <span class="fu"><$</span> mainGameCoroutine
stopEvent <span class="fu">=</span> <span class="kw">if</span> gameOver oldState <span class="kw">then</span> [mainStartScreenCoroutine] <span class="kw">else</span> []
state <span class="ot"><-</span> switch mainStartScreenCoroutine <span class="fu">-<</span> (startEvent <span class="fu">++</span> stopEvent, inEvents)
oldState <span class="ot"><-</span> delay <span class="dt">StartScreen</span> <span class="fu">-<</span> state
returnA <span class="fu">-<</span> state
<span class="ot">mainStartScreenCoroutine ::</span> <span class="dt">MainCoroutineType</span>
mainStartScreenCoroutine <span class="fu">=</span> arr <span class="fu">$</span> <span class="fu">const</span> <span class="dt">StartScreen</span>
<span class="ot">mainGameCoroutine ::</span> <span class="dt">MainCoroutineType</span>
mainGameCoroutine <span class="fu">=</span> proc inEvents <span class="ot">-></span> <span class="kw">do</span>
plState <span class="ot"><-</span> playerState <span class="fu">-<</span> inEvents
rec
<span class="kw">let</span> (ballBlockColls, blockColls) <span class="fu">=</span> ballBlocksCollisions oldBallState oldBlockStates
<span class="kw">let</span> colls <span class="fu">=</span> (ballWallCollisions oldBallState) <span class="fu">++</span> (ballPlayerCollisions plState oldBallState) <span class="fu">++</span> ballBlockColls
currBallState <span class="ot"><-</span> ballState <span class="fu">-<</span> colls <span class="co">--long names ...</span>
currBlockStates <span class="ot"><-</span> blockStates <span class="fu">-<</span> blockColls
oldBallState <span class="ot"><-</span> delay initBallState <span class="fu">-<</span> currBallState
oldBlockStates <span class="ot"><-</span> delay initBlockStates<span class="fu">-<</span> currBlockStates
returnA <span class="fu">-<</span> <span class="dt">GameState</span> plState currBallState currBlockStates</code></pre>
<p>The original main coroutine has been renamed to mainGameCoroutine. There is a new "main coroutine" mainStartScreenCoroutine which is used while in the start screen. The new mainCoroutine switches between these two coroutine when the player pressed enter, or the game is over.</p>
<p>Remember, the <$ operator replaces the contents of an event with its second parameter (here the mainGameCoroutine) and switch receives events containing coroutines to which it switches.</p>
<p>mainGameCoroutine has been extended by the blocks. ballBlocksCollisions, as we will see later, returns a tuple with the ballCollisions events due to collisions with the blocks, and a list of BlockCollision events. This list has the same length as the list of blocks (in oldBlockStates). The n-th element of this list are the collisions with the n-th block.</p>
<p>The block collisions are than passed to the blockStates arrow while the ballCollisions are added to the collisions passed to ballState.</p>
<p>I dislike the long names like "currBallState" here. I would have called it ballState, but there is already an arrow with the same name. I wonder if there is a less clumsy way of doing this ...</p>
<h2 id="ball-block-collisions">Ball-Block collisions</h2>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">ballBlocksCollisions ::</span> <span class="dt">BallState</span> <span class="ot">-></span> [<span class="dt">BlockState</span>] <span class="ot">-></span> (<span class="dt">Event</span> <span class="dt">BallCollision</span>, [<span class="dt">Event</span> <span class="dt">BlockCollision</span>])
ballBlocksCollisions ballState blockStates <span class="fu">=</span>
<span class="kw">let</span> ballR <span class="fu">=</span> ballRect ballState
foldStep (ballC, blockC) blockState <span class="fu">=</span>
<span class="kw">if</span> rectOverlap ballR (blockRect blockState) <span class="kw">then</span>
(ballRectCollisions ballState (blockRect blockState) <span class="fu">++</span> ballC, blockC <span class="fu">++</span> [[<span class="dt">BlockCollision</span>]])
<span class="kw">else</span>
(ballC, blockC <span class="fu">++</span> [[]])
<span class="kw">in</span> foldl' foldStep ([],[]) blockStates</code></pre>
<p>In my opinion, this is the most complicated function. It takes the ball state and the block states (as a list) and produces ball collisions events, and a list of block collision events, which has the same length as the input block state list.</p>
<p>The foldStep function takes the next block, tests it for collision and updates the list of ball and block collisions. Here the ball collision events are only expanded when a collision happens. The list of block collision events is always expanded. By an empty event (empty list) when no collision happens, and by a BlockCollision event in case of collision. This is because the position in this list reflects the block that will receive it.</p>
<h2 id="updating-the-block-state">Updating the block state</h2>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">blockState ::</span> <span class="dt">BlockState</span> <span class="ot">-></span> <span class="dt">Coroutine</span> (<span class="dt">Event</span> <span class="dt">BlockCollision</span>) (<span class="dt">Maybe</span> <span class="dt">BlockState</span>)
blockState initState <span class="fu">=</span> scanE update (<span class="kw">Just</span> initState)
<span class="kw">where</span>
<span class="ot"> update ::</span> <span class="dt">Maybe</span> <span class="dt">BlockState</span> <span class="ot">-></span> <span class="dt">BlockCollision</span> <span class="ot">-></span> <span class="dt">Maybe</span> <span class="dt">BlockState</span>
update <span class="kw">Nothing</span> _ <span class="fu">=</span> <span class="kw">Nothing</span>
update (<span class="kw">Just</span> bs) _ <span class="fu">=</span> <span class="kw">if</span> (blockLives bs <span class="fu">==</span> <span class="dv">1</span>) <span class="kw">then</span> <span class="kw">Nothing</span> <span class="kw">else</span> <span class="kw">Just</span> <span class="fu">$</span> bs{blockLives<span class="fu">=</span><span class="dv">1</span>}
<span class="ot">blockStates ::</span> <span class="dt">Coroutine</span> ([<span class="dt">Event</span> <span class="dt">BlockCollision</span>]) ([<span class="dt">BlockState</span>])
blockStates <span class="fu">=</span> manager <span class="fu">$</span> <span class="fu">map</span> blockState initBlockStates</code></pre>
<p>Every block has its own coroutine, which receives block collision events. In case of such an event, the number of lives is reduced or the block is removed (if there are no lives left). The coroutines return a Maybe data type, because they are inserted into the manager. Nothing is returned if the block should be deleted.</p>
<p>blockStates uses the manager to manage all "living" blocks.</p>
<h1 id="compiling">Compiling</h1>
<p>The compilation is the same as for Pong int the <a href="https://jshaskell.blogspot.de/2012/09/pong.html">last post</a>.</p>
<h2 id="haste">Haste</h2>
<p>For haste make sure the <a href="https://github.com/valderman/haste-compiler">newest version</a> is installed. Because we use vector-space we need to install it for haste.</p>
<p>vector space is needed, see the <a href="https://jshaskell.blogspot.de/2012/09/pong.html">last post</a>.</p>
<p>Now put <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/5_Breakout/code/Breakout.hs">Breakout.hs</a>, <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/5_Breakout/code/Coroutine.hs">Coroutine.hs</a>, the haste version of <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/5_Breakout/code/haste/JavaScript.hs">JavaScript.hs</a> and the javascript helper functions <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/5_Breakout/code/haste/helpers.js">helpers.js</a> in a directory and compile with</p>
<pre class="sourceCode bash"><code class="sourceCode bash">hastec Breakout.hs --start=asap --with-js=helpers.js</code></pre>
<p>You should receive a file "Breakout.js" which can be included in a html file, like this one: <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/5_Breakout/code/indexHaste.html">haste html</a></p>
<h2 id="uhc">UHC</h2>
<p>With UHC it is a little bit more work. UHC does not support arrow syntax, so we must translate the haskell file with arrowp:</p>
<pre class="sourceCode bash"><code class="sourceCode bash">cabal <span class="kw">install</span> arrowp
arrowp Breakout.hs <span class="kw">></span> BreakoutNA.hs</code></pre>
<p>I choose the name BreakoutNA.hs for "Breakout no arrows". For some reason I also can not get vector space to compile with UHC. Luckily we have not used much of vector space, only the <sup>+</sup> operator. So edit PongNA.hs and replace the line</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">import</span> <span class="dt">Data.VectorSpace</span></code></pre>
<p>with</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">(^+^) ::</span> <span class="kw">Num</span> a <span class="ot">=></span> (a,a) <span class="ot">-></span> (a,a) <span class="ot">-></span> (a,a)
(<span class="fu">^+^</span>) (a1,a2) (b1,b2) <span class="fu">=</span> (a1<span class="fu">+</span>b1, a2<span class="fu">+</span>b2)</code></pre>
<p>Now copy <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/5_Breakout/code/Coroutine.hs">Coroutine.hs</a> and <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/5_Breakout/code/uhc/JavaScript.hs">JavaScript.hs</a> (the UHC version) into the directory and compile with</p>
<pre class="sourceCode bash"><code class="sourceCode bash">uhc -tjs BreakoutNA.hs -iuhc </code></pre>
<p>The canvas needs to be added to the generated html file, so add</p>
<pre class="sourceCode html"><code class="sourceCode html"><span class="kw"><canvas</span><span class="ot"> height=</span><span class="st">"400"</span><span class="ot"> id=</span><span class="st">"canvas3"</span><span class="ot"> style=</span><span class="st">"background-color: white;"</span><span class="ot"> width=</span><span class="st">"600"</span><span class="ot"> tabindex=</span><span class="st">"1"</span><span class="kw">></canvas></span></code></pre>
<p>Since we do not need any additional javascript functions, the generated html page should work!</p>
<h1 id="conclusion">Conclusion</h1>
<p>Well that is it. At places I find it a bit clumpsy and I wonder if another FRP library like <a href="https://www.haskell.org/haskellwiki/Reactive-banana" title="Reactive Banana on Haskell wiki">Reactive Banana</a> or <a href="https://hackage.haskell.org/package/elerea">elerea</a> would help. I will look into these!</p>
<a rel="license" href="https://creativecommons.org/licenses/by/3.0/de/deed.en_US"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/3.0/de/88x31.png" /></a><br /><span xmlns:dct="https://purl.org/dc/terms/" href="https://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">Writing JavaScript games in Haskell</span> by <a xmlns:cc="https://creativecommons.org/ns#" href="https://jshaskell.blogspot.de/" property="cc:attributionName" rel="cc:attributionURL">Nathan Hüsken</a> is licensed under a <a rel="license" href="https://creativecommons.org/licenses/by/3.0/de/deed.en_US">Creative Commons Attribution 3.0 Germany License</a>. Nathan Hüsken https://www.blogger.com/profile/09614845657227846437 noreply@blogger.com 11 tag:blogger.com,1999:blog-6355249120999252418.post-4298995660378811929 2012-09-17T05:19:00.000-07:00 2013-08-21T23:07:16.800-07:00 Pong In the <a href="https://jshaskell.blogspot.de/2012/07/first-interactive-application.html" title="Last blog entry">last Post</a> we wrote the first interactive javascript application in haskell where a paddle on the bottom of the canvas could be moved via keyboard input.<br />
In this next step we will add ball (a moving circle) that can bounce of the paddle and the walls.<br />
Here is a preview (again, click on the canvas to get input focus). If you are not viewing this blog article on blogspot and the application does not work, try the original <a href="https://jshaskell.blogspot.de/" title="Original location of this article">article page</a>.<br />
** Note: ** This currently only works if you are viewing this article only (not in the flow of the complete blog). I am working on the problem ...<br />
<script src="https://rawgithub.com/RudolfVonKrugstein/jshaskell-blog/master/4_Pong/code/compiled/Pong.js" type="text/javascript"></script>
<canvas height="400" id="canvas2" style="background-color: white;" tabindex="1" width="600"></canvas>
<br />
But first we will need some perquisites. I will utilize Functional Reactive Programming (FRP) using the functions defined here: <a href="https://github.com/leonidas/codeblog/blob/master/2012/2012-01-17-declarative-game-logic-afrp.md" title="Purely Functional, Declarative Game Logic Using Reactive Programming">Purely Functional, Declarative Game Logic Using Reactive Programming</a>. I take the terminus "coroutine" from that blog article. I like to think of a coroutine as "state full function". The output of the coroutine does not only depend on its input but also on the input passed to it in previous calls. So make sure you read and understand that blog article. The resulting code can be found here: <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/4_Pong/code/Coroutine.hs" title="Coroutine source file">Coroutine.hs</a>.<br />
So, let us get started!<br />
<h1 id="imports-and-definitions">
Imports and definitions</h1>
I follow the source file <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/4_Pong/code/Pong.hs" title="Main pong source file">Pong.hs</a> and therefor start with the imports and some definitions used later in the game.<br />
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">{-# LANGUAGE Arrows #-}</span>
<span class="kw">module</span> <span class="dt">Main</span> <span class="kw">where</span>
<span class="kw">import</span> <span class="dt">JavaScript</span>
<span class="kw">import</span> <span class="dt">Coroutine</span>
<span class="kw">import</span> <span class="dt">Data.IORef</span>
<span class="kw">import</span> <span class="dt">Control.Arrow</span>
<span class="kw">import</span> <span class="dt">Data.VectorSpace</span>
<span class="co">-- input data</span>
<span class="kw">data</span> <span class="dt">Input</span> <span class="fu">=</span> <span class="dt">KeyUp</span> <span class="dt">Int</span> <span class="fu">|</span> <span class="dt">KeyDown</span> <span class="dt">Int</span> <span class="kw">deriving</span> (<span class="kw">Eq</span>)
<span class="co">-- Game data</span>
<span class="kw">type</span> <span class="dt">Vector</span> <span class="fu">=</span> (<span class="dt">Double</span>, <span class="dt">Double</span>)
<span class="kw">data</span> <span class="dt">PlayerState</span> <span class="fu">=</span> <span class="dt">PlayerState</span> {<span class="ot">xPos ::</span> <span class="dt">Double</span>}
<span class="kw">data</span> <span class="dt">BallState</span> <span class="fu">=</span> <span class="dt">BallState</span> {<span class="ot">pos ::</span> <span class="dt">Vector2D</span>}
<span class="kw">data</span> <span class="dt">GameState</span> <span class="fu">=</span> <span class="dt">GameState</span> {<span class="ot">player ::</span> <span class="dt">PlayerState</span>,
<span class="ot"> ball ::</span> <span class="dt">BallState</span>}
<span class="kw">data</span> <span class="dt">BallCollision</span> <span class="fu">=</span> <span class="dt">LeftBounce</span> <span class="fu">|</span> <span class="dt">RightBounce</span> <span class="fu">|</span> <span class="dt">UpBounce</span> <span class="fu">|</span> <span class="dt">DownBounce</span>
<span class="kw">data</span> <span class="dt">Rect</span> <span class="fu">=</span> <span class="dt">Rect</span> {<span class="ot"> x::</span><span class="dt">Double</span>,<span class="ot"> y::</span><span class="dt">Double</span>,<span class="ot"> width ::</span><span class="dt">Double</span>,<span class="ot"> height::</span><span class="dt">Double</span>}</code></pre>
We will use Arrow Syntax and tell the compiler that we do. Actually UHC does not support Arrow Syntax (yet?), but more about that later.<br />
We import Data.VectorSpace allowing us to use some basic vector operation with tuples of Doubles. Here we only need addition, but if we need more VectorSpace is handy.<br />
The input data will be a series of Keyboard up and down events with corresponding key codes. BallCollision describes a collision of the ball with the wall or the paddle in a certain direction.<br />
The rest is types we need in the game and should be self explaining.<br />
Next we will declare some values defining subtleties of the game.<br />
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="co">-- game values</span>
screenWidth <span class="fu">=</span> <span class="fl">600.0</span>
screenHeight <span class="fu">=</span> <span class="fl">400.0</span>
playerColor <span class="fu">=</span> <span class="st">"black"</span>
ballColor <span class="fu">=</span> <span class="st">"red"</span>
playerYPos <span class="fu">=</span> screenHeight <span class="fu">-</span> playerHeight
playerHeight <span class="fu">=</span> <span class="fl">15.0</span>
playerWidth <span class="fu">=</span> <span class="fl">40.0</span>
ballRadius <span class="fu">=</span> <span class="fl">5.0</span>
initBallState <span class="fu">=</span> <span class="dt">BallState</span> ((screenWidth <span class="fu">/</span> <span class="fl">2.0</span>), (screenHeight <span class="fu">-</span> <span class="fl">50.0</span>))
initBallSpeed <span class="fu">=</span> (<span class="fl">3.0</span>, <span class="fu">-</span><span class="fl">3.0</span>)
initPlayerState <span class="fu">=</span> <span class="dt">PlayerState</span> ((screenWidth <span class="fu">-</span> playerWidth) <span class="fu">/</span> <span class="fl">2.0</span>)
playerSpeed <span class="fu">=</span> <span class="fl">5.0</span>
<span class="co">-- technical values</span>
leftKeyCode <span class="fu">=</span> <span class="dv">37</span>
rightKeyCode <span class="fu">=</span> <span class="dv">39</span>
canvasName <span class="fu">=</span> <span class="st">"canvas2"</span></code></pre>
Again, these should be relatively self explaining. Keycode 37 and 39 correspond to the arrow keys. canvas2 is the name of the canvas defined in the html code of this blog.<br />
<h1 id="entry-point-and-callbacks">
Entry point and callbacks</h1>
In difference to the last blog article we will not use a javascript function to save and store global objects. Instead the objects will be stored in IORefs which are passed to the callbacks.<br />
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="co">-- entry point</span>
main <span class="fu">=</span> setOnLoad initilize
initilize <span class="fu">=</span> <span class="kw">do</span>
state <span class="ot"><-</span> newIORef mainCoroutine
input <span class="ot"><-</span> newIORef ([]<span class="ot"> ::</span> [<span class="dt">Input</span>])
setOnKeyDown canvasName (onKeyDown input)
setOnKeyUp canvasName (onKeyUp input)
setInterval <span class="fl">20.0</span> (update state input)
<span class="co">-- input</span>
<span class="ot">onKeyDown ::</span> <span class="dt">IORef</span> [<span class="dt">Input</span>] <span class="ot">-></span> <span class="dt">Int</span><span class="ot">-></span> <span class="dt">IO</span> ()
onKeyDown input keyCode <span class="fu">=</span> <span class="kw">do</span>
i <span class="ot"><-</span> readIORef input
<span class="kw">let</span> i' <span class="fu">=</span> i <span class="fu">++</span> [<span class="dt">KeyDown</span> keyCode]
writeIORef input i'
<span class="ot">onKeyUp ::</span> <span class="dt">IORef</span> [<span class="dt">Input</span>] <span class="ot">-></span> <span class="dt">Int</span><span class="ot">-></span> <span class="dt">IO</span> ()
onKeyUp input keyCode <span class="fu">=</span> <span class="kw">do</span>
i <span class="ot"><-</span> readIORef input
<span class="kw">let</span> i' <span class="fu">=</span> i <span class="fu">++</span> [<span class="dt">KeyUp</span> keyCode]
writeIORef input i'</code></pre>
So main sets the initilize function to be called then the window is loaded. initilize creates 2 IORefs, one for the main coroutine (which will be defined later) and one for the input stream, which is a list of input events.<br />
The main coroutine is the place where the game logic happens. The output of the main coroutine is the current game state. Because the current main coroutine depends on the previous calls to it, it must be stored between game updates.<br />
onKeyDown and onKeyUp are called when a key is pressed or released and expand the input stream.<br />
update is set to be called every 20 milliseconds with the state and input IORefs passed to it.<br />
<h1 id="updating-and-drawing-the-game-sate">
Updating and drawing the game sate</h1>
Next we will draw the game state (the output of the main coroutine). This is basicly the same as what we did in the last blog article, only that now we also need to draw a circle for the ball.<br />
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="co">-- draw a gamestate</span>
<span class="ot">draw ::</span> <span class="dt">GameState</span> <span class="ot">-></span> <span class="dt">IO</span> ()
draw gs <span class="fu">=</span> <span class="kw">do</span>
ctx <span class="ot"><-</span> getContext2d canvasName
clear ctx
<span class="co">-- draw player</span>
setFillColor ctx playerColor
<span class="kw">let</span> pRect <span class="fu">=</span> playerRect <span class="fu">.</span> player <span class="fu">$</span> gs
fillRect ctx (x pRect) (y pRect) (width pRect) (height pRect)
<span class="co">--draw ball</span>
setFillColor ctx ballColor
<span class="kw">let</span> (x,y) <span class="fu">=</span> pos <span class="fu">.</span> ball <span class="fu">$</span> gs
fillCircle ctx x y ballRadius
<span class="co">-- update function</span>
<span class="ot">update ::</span> <span class="dt">IORef</span> <span class="dt">MainCoroutineType</span> <span class="ot">-></span> <span class="dt">IORef</span> (<span class="dt">Event</span> <span class="dt">Input</span>) <span class="ot">-></span> <span class="dt">IO</span> ()
update state input <span class="fu">=</span> <span class="kw">do</span>
co <span class="ot"><-</span> readIORef state
i <span class="ot"><-</span> readIORef input
writeIORef input ([]<span class="ot"> ::</span> [<span class="dt">Input</span>])
<span class="kw">let</span> (gs, co') <span class="fu">=</span> runC co i
draw gs
writeIORef state co'</code></pre>
The draw function should be self explaining. If not, read my last blog articles. Some javascript functions have been added, but they all follow the same principle as in the last blog article.<br />
The update function reads the current main coroutine and input stream. The coroutine is updated and the new game state is obtained by calling the coroutine with the current input stream. Finally the game state is drawn and the new coroutine is saved.<br />
<h1 id="some-helper-functions">
Some helper functions</h1>
Before the main game logic a few helper functions are defined.<br />
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="co">-- helper functions</span>
<span class="ot">keyDown ::</span> <span class="dt">Int</span> <span class="ot">-></span> <span class="dt">Coroutine</span> (<span class="dt">Event</span> <span class="dt">Input</span>) <span class="dt">Bool</span>
keyDown code <span class="fu">=</span> scanE step <span class="kw">False</span>
<span class="kw">where</span>
step old input
<span class="fu">|</span> input <span class="fu">==</span> <span class="dt">KeyUp</span> code <span class="fu">=</span> <span class="kw">False</span>
<span class="fu">|</span> input <span class="fu">==</span> <span class="dt">KeyDown</span> code <span class="fu">=</span> <span class="kw">True</span>
<span class="fu">|</span> <span class="fu">otherwise</span> <span class="fu">=</span> old
<span class="ot">rectOverlap ::</span> <span class="dt">Rect</span> <span class="ot">-></span> <span class="dt">Rect</span> <span class="ot">-></span> <span class="dt">Bool</span>
rectOverlap r1 r2
<span class="fu">|</span> x r1 <span class="fu">>=</span> x r2 <span class="fu">+</span> width r2 <span class="fu">=</span> <span class="kw">False</span>
<span class="fu">|</span> x r2 <span class="fu">>=</span> x r1 <span class="fu">+</span> width r1 <span class="fu">=</span> <span class="kw">False</span>
<span class="fu">|</span> y r1 <span class="fu">>=</span> y r2 <span class="fu">+</span> height r2 <span class="fu">=</span> <span class="kw">False</span>
<span class="fu">|</span> y r2 <span class="fu">>=</span> y r1 <span class="fu">+</span> height r1 <span class="fu">=</span> <span class="kw">False</span>
<span class="fu">|</span> <span class="fu">otherwise</span> <span class="fu">=</span> <span class="kw">True</span>
<span class="ot">playerRect ::</span> <span class="dt">PlayerState</span> <span class="ot">-></span> <span class="dt">Rect</span>
playerRect (<span class="dt">PlayerState</span> px) <span class="fu">=</span> <span class="dt">Rect</span> px playerYPos playerWidth playerHeight
<span class="ot">ballRect ::</span> <span class="dt">BallState</span> <span class="ot">-></span> <span class="dt">Rect</span>
ballRect (<span class="dt">BallState</span> (bx,by)) <span class="fu">=</span> <span class="dt">Rect</span> (bx <span class="fu">-</span> ballRadius) (by <span class="fu">-</span> ballRadius) (<span class="fl">2.0</span> <span class="fu">*</span> ballRadius) (<span class="fl">2.0</span> <span class="fu">*</span> ballRadius)</code></pre>
keyDown takes a keycode and outputs a coroutine indicating at all times if the given key is down given the input stream (The Event type comes from <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/4_Pong/code/Coroutine.hs" title="Coroutine source file">Coroutine.hs</a>). We will need this because the paddle is supposed to be moving as long as an arrow key is pressed.<br />
Note that this is a little different that what we did in the <a href="https://jshaskell.blogspot.de/2012/07/first-interactive-application.html" title="Last blog entry">last post</a>. Actually there it only worked because javascript fires continuous "keyDown" events when a key is hold down, but that is a platform dependent behavior and we do not want to rely on it. Also this firing of key down events does not immediately start when a key is pressed. There is a short break. If you <a href="https://jshaskell.blogspot.de/2012/07/first-interactive-application.html" title="Last blog entry">go back</a> on that post and try the application, you will note that the paddle does not start moving immediately, but there is a short delay after pressing a key.<br />
rectOverlap tests two rectangles if they overlap (used for collision detection). playerRect and ballRect return the rectangle occupied by the paddle and ball respectively.<br />
<h1 id="the-main-coroutine">
The main Coroutine</h1>
The main coroutine takes input events as input and outputs the game state. The type synonym MainCoroutineType is introduced for verbosity. Earlier it allowed us to create the IORef for the main coroutine in a more readable way (in my opinion).<br />
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="co">-- Game logic</span>
<span class="kw">type</span> <span class="dt">MainCoroutineType</span> <span class="fu">=</span> <span class="dt">Coroutine</span> (<span class="dt">Event</span> <span class="dt">Input</span>) <span class="dt">GameState</span>
<span class="ot">mainCoroutine ::</span> <span class="dt">MainCoroutineType</span>
mainCoroutine <span class="fu">=</span> proc inEvents <span class="ot">-></span> <span class="kw">do</span>
plState <span class="ot"><-</span> playerState <span class="fu">-<</span> inEvents
rec
<span class="kw">let</span> colls <span class="fu">=</span> (ballWallCollisions oldBlState) <span class="fu">++</span> (ballPlayerCollisions plState oldBlState)
blState <span class="ot"><-</span> ballState <span class="fu">-<</span> colls
oldBlState <span class="ot"><-</span> delay initBallState <span class="fu">-<</span> blState
returnA <span class="fu">-<</span> <span class="dt">GameState</span> plState blState</code></pre>
The player state is computed with the input events. The collisions of the ball with player and wall solely depend on the previous ball state. ballWallCollisions and ballPlayerCollisions can therefore be pure functions and not coroutines. That is why "colls" is defined in a let expression. The new ballState is calculated using this collisions information.<br />
The construct with "rec" and "delay" is needed because the ball state from the last frame is required. This construct is explained in <a href="https://github.com/leonidas/codeblog/blob/master/2012/2012-01-17-declarative-game-logic-afrp.md" title="Purely Functional, Declarative Game Logic Using Reactive Programming">Purely Functional, Declarative Game Logic Using Reactive Programming</a>.<br />
<h1 id="the-player">
The Player</h1>
The player is moved with the arrow keys without crossing the bounding of the game.<br />
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">playerState ::</span> <span class="dt">Coroutine</span> (<span class="dt">Event</span> <span class="dt">Input</span>) <span class="dt">PlayerState</span>
playerState <span class="fu">=</span> proc inEvents <span class="ot">-></span> <span class="kw">do</span>
vel <span class="ot"><-</span> playerVelocity <span class="fu">-<</span> inEvents
xPos <span class="ot"><-</span> boundedIntegrate (<span class="fl">0.0</span>,screenWidth<span class="fu">-</span>playerWidth) (xPos initPlayerState) <span class="fu">-<</span> vel
returnA <span class="fu">-<</span> <span class="dt">PlayerState</span> xPos
<span class="ot">playerVelocity ::</span> <span class="dt">Coroutine</span> (<span class="dt">Event</span> <span class="dt">Input</span>) <span class="dt">Double</span>
playerVelocity <span class="fu">=</span> proc inEvents <span class="ot">-></span> <span class="kw">do</span>
leftDown <span class="ot"><-</span> keyDown leftKeyCode <span class="fu">-<</span> inEvents
rightDown <span class="ot"><-</span> keyDown rightKeyCode <span class="fu">-<</span> inEvents
returnA <span class="fu">-<</span> <span class="kw">if</span> leftDown <span class="kw">then</span> <span class="fu">-</span>playerSpeed <span class="kw">else</span> (<span class="kw">if</span> rightDown <span class="kw">then</span> playerSpeed <span class="kw">else</span> <span class="fl">0.0</span>)</code></pre>
boundedIntegrate is a coroutine defined in <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/4_Pong/code/Coroutine.hs" title="Coroutine source file">Coroutine.hs</a> which integrates the input and clips it to a given range.<br />
<h1 id="the-ball-state">
The Ball state</h1>
<h2 id="collisions">
Collisions</h2>
The ball state needs the collision events as input (see the main coroutine).<br />
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">ballWallCollisions ::</span> <span class="dt">BallState</span> <span class="ot">-></span> (<span class="dt">Event</span> <span class="dt">BallCollision</span>)
ballWallCollisions (<span class="dt">BallState</span> (bx,by)) <span class="fu">=</span>
<span class="fu">map</span> <span class="fu">snd</span> <span class="fu">.</span> <span class="fu">filter</span> <span class="fu">fst</span> <span class="fu">$</span> [(bx <span class="fu"><</span> ballRadius, <span class="dt">LeftBounce</span>),
(bx <span class="fu">></span> screenWidth <span class="fu">-</span> ballRadius, <span class="dt">RightBounce</span>),
(by <span class="fu"><</span> ballRadius, <span class="dt">UpBounce</span>)]
<span class="ot">ballRectCollisions ::</span> <span class="dt">BallState</span> <span class="ot">-></span> <span class="dt">Rect</span> <span class="ot">-></span> (<span class="dt">Event</span> <span class="dt">BallCollision</span>)
ballRectCollisions (<span class="dt">BallState</span> (bx, by)) (<span class="dt">Rect</span> rx ry rw rh) <span class="fu">=</span>
<span class="fu">map</span> <span class="fu">snd</span> <span class="fu">.</span> <span class="fu">filter</span> <span class="fu">fst</span> <span class="fu">$</span> [(bx <span class="fu"><=</span> rx, <span class="dt">RightBounce</span>),
(bx <span class="fu">>=</span> rx <span class="fu">+</span> rw, <span class="dt">LeftBounce</span>),
(by <span class="fu"><=</span> ry, <span class="dt">DownBounce</span>),
(by <span class="fu">>=</span> ry <span class="fu">+</span> rh, <span class="dt">UpBounce</span>)]
<span class="ot">ballPlayerCollisions ::</span> <span class="dt">PlayerState</span> <span class="ot">-></span> <span class="dt">BallState</span> <span class="ot">-></span> (<span class="dt">Event</span> <span class="dt">BallCollision</span>)
ballPlayerCollisions playerState ballState <span class="fu">=</span>
<span class="kw">if</span> rectOverlap (playerRect playerState) (ballRect ballState)
<span class="kw">then</span> ballRectCollisions ballState (playerRect playerState)
<span class="kw">else</span> []</code></pre>
<h2 id="updating-the-ball-state">
Updating the ball state</h2>
Using this collisions events the ball is updated by moving and bouncing according to the collision events.<br />
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">ballState ::</span> <span class="dt">Coroutine</span> (<span class="dt">Event</span> <span class="dt">BallCollision</span>) <span class="dt">BallState</span>
ballState <span class="fu">=</span> proc collEvents <span class="ot">-></span> <span class="kw">do</span>
vel <span class="ot"><-</span> ballVelocity <span class="fu">-<</span> collEvents
pos <span class="ot"><-</span> scan (<span class="fu">^+^</span>) (pos initBallState) <span class="fu">-<</span> vel
returnA <span class="fu">-<</span> <span class="dt">BallState</span> pos
<span class="ot">ballVelocity ::</span> <span class="dt">Coroutine</span> (<span class="dt">Event</span> <span class="dt">BallCollision</span>) <span class="dt">Vector2D</span>
ballVelocity <span class="fu">=</span> scanE bounce initBallSpeed
<span class="kw">where</span>
<span class="ot"> bounce ::</span> <span class="dt">Vector2D</span> <span class="ot">-></span> <span class="dt">BallCollision</span> <span class="ot">-></span> <span class="dt">Vector2D</span>
bounce (vx,vy) coll <span class="fu">=</span> <span class="kw">case</span> coll <span class="kw">of</span>
<span class="dt">LeftBounce</span> <span class="ot">-></span> (<span class="fu">abs</span>(vx), vy)
<span class="dt">RightBounce</span> <span class="ot">-></span> (<span class="fu">-abs</span>(vx), vy)
<span class="dt">UpBounce</span> <span class="ot">-></span> (vx, <span class="fu">abs</span>(vy))
<span class="dt">DownBounce</span> <span class="ot">-></span> (vx, <span class="fu">-abs</span>(vy))</code></pre>
The <sup>+</sup> operator is defined in the vector space package and adds two vectors (in our case tuples of doubles).<br />
<h1 id="compiling">
Compiling</h1>
That it. Now we need to compile ...<br />
<h2 id="haste">
haste</h2>
For haste make sure the <a href="https://github.com/valderman/haste-compiler">newest version</a> is installed. Because we use vector-space we need to install it for haste.<br />
First install vector space via cabal:<br />
<pre class="sourceCode bash"><code class="sourceCode bash">cabal <span class="kw">install</span> vector-space</code></pre>
Now unpack vector-space with cabal, and install AdditiveGroup.jsmod.<br />
<pre><code>cabal unpack vector-space
cd vector-space-0.8.2/src
hastec --libinstall -O2 Data.VectorSpace Data.AdditiveGroup</code></pre>
That it! Now put <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/4_Pong/code/Pong.hs" title="Main pong source file">Pong.hs</a>, <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/4_Pong/code/Coroutine.hs" title="Coroutine source file">Coroutine.hs</a>, the haste version of <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/4_Pong/code/haste/JavaScript.hs">JavaScript.hs</a> and the javascript helper functions <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/4_Pong/code/haste/helpers.js">helpers.js</a> in a directory and compile with<br />
<pre class="sourceCode bash"><code class="sourceCode bash">hastec Pong.hs --start=asap --with-js=helpers.js</code></pre>
You should receive a file "Pong.js" which can be included in a html file, like this one: <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/4_Pong/code/indexHaste.html">haste html</a><br />
<h2 id="uhc">
UHC</h2>
With UHC it is a little bit more work. UHC does not support arrow syntax, so we must translate the haskell file with arrowp:<br />
<pre class="sourceCode bash"><code class="sourceCode bash">cabal <span class="kw">install</span> arrowp
arrowp Pong.hs <span class="kw">></span> PongNA.hs</code></pre>
I choose the name PongNA.hs for "Pong no arrows". For some reason I also can not get vector space to compile with UHC. Luckily we have not used much of vector space, only the <sup>+</sup> operator. So edit PongNA.hs and replace the line<br />
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">import</span> <span class="dt">Data.VectorSpace</span></code></pre>
with<br />
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">(^+^) ::</span> <span class="kw">Num</span> a <span class="ot">=></span> (a,a) <span class="ot">-></span> (a,a) <span class="ot">-></span> (a,a)
(<span class="fu">^+^</span>) (a1,a2) (b1,b2) <span class="fu">=</span> (a1<span class="fu">+</span>b1, a2<span class="fu">+</span>b2)</code></pre>
Now copy <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/4_Pong/code/Coroutine.hs" title="Coroutine source file">Coroutine.hs</a> and <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/4_Pong/code/uhc/JavaScript.hs">JavaScript.hs</a> (the UHC version) into the directory and compile with<br />
<pre class="sourceCode bash"><code class="sourceCode bash">uhc -tjs PongNA.hs -iuhc </code></pre>
The canvas needs to be added to the generated html file, so add<br />
<pre class="sourceCode html"><code class="sourceCode html"><span class="kw"><canvas</span><span class="ot"> height=</span><span class="st">"400"</span><span class="ot"> id=</span><span class="st">"canvas2"</span><span class="ot"> style=</span><span class="st">"background-color: white;"</span><span class="ot"> width=</span><span class="st">"600"</span><span class="ot"> tabindex=</span><span class="st">"1"</span><span class="kw">></canvas></span></code></pre>
Since we do not need any additional javascript functions, the generated html page should work!<br />
<h1 id="conclusion">
Conclusion</h1>
I have little experience with FRP (this blog article is my first attempt to write a FRP application). I would have liked to use <a href="https://www.haskell.org/haskellwiki/Reactive-banana" title="Reactive Banana on Haskell wiki">Reactive Banana</a> for this, but at present I am unable to compile Reactive Banana with UHC or haste.<br />
According to <a href="https://github.com/HeinrichApfelmus/reactive-banana/issues/30">this</a> Reactive Banana has been compiled with UHC, but in the new version, support for UHC will be <a href="https://apfelmus.nfshost.com/blog/2012/08/26-frp-banana-0-7.html">dropped</a>.<br />
haste failed to compile Reactive Banana because of missing PrimOps. According to the maintainer of haste, that is a solvable problem and will be fixed in the future.<br />
In the next article, we will add "blocks" that can collide with the ball and disappear to have a breakout like game.<br />
<a href="https://creativecommons.org/licenses/by/3.0/de/deed.en_US" rel="license"><img alt="Creative Commons License" src="https://i.creativecommons.org/l/by/3.0/de/88x31.png" style="border-width: 0;" /></a><br />
<span href="https://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type" xmlns:dct="https://purl.org/dc/terms/">Writing JavaScript games in Haskell</span> by <a href="https://jshaskell.blogspot.de/" property="cc:attributionName" rel="cc:attributionURL" xmlns:cc="https://creativecommons.org/ns#">Nathan Hüsken</a> is licensed under a <a href="https://creativecommons.org/licenses/by/3.0/de/deed.en_US" rel="license">Creative Commons Attribution 3.0 Germany License</a>. Nathan Hüsken https://www.blogger.com/profile/09614845657227846437 noreply@blogger.com 8 tag:blogger.com,1999:blog-6355249120999252418.post-6243064176858179873 2012-07-14T11:43:00.001-07:00 2013-08-30T10:08:34.039-07:00 The first interactive application <p>In the <a href="https://jshaskell.blogspot.de/2012/07/hello-world.html">last Post</a> we wrote the first "Hello, World!" application. We saw how to import javascript functions in UHC and haste.</p>
<p>We now want to do something more game like. Out goal over the next few post will be to write a breackout clone in haskell, running in the browser! But first there are still a few things we need. To explore this we will write a little application displaying the paddle that can be moved with the arrow keys. Here is a preview (you have to click on it so that it gains focus):</p>
<script src="https://rawgithub.com/RudolfVonKrugstein/jshaskell-blog/master/3_FirstInteractive/code/compiled/Main.js" type="text/javascript">
</script>
<canvas height="400" id="canvas1" style="background-color: white;" width="600" tabindex="0"></canvas>
<p><strong>Update:</strong> This should now also work in firefox.</p>
<p>For this we need to learn how to:</p>
<ul>
<li>Set callbacks</li>
<li>Let different callback communicate</li>
<li>Draw on the canvas</li>
</ul>
<h1 id="gameloop-in-javascript">GameLoop in JavaScript</h1>
<p>Before we want to start our game, we have to allow the browser to load the full page and its elements. Otherwise we can not access e.g. the canvas (the drawing area we will use).</p>
<p>The browser tells us, that it is done with loading by invoking the callback window.onLoad. Depending on how we compile with haste, our main will already be set to the window.onLoad (the option --start==asap prevents this), but in UHC we have to set a callback by hand. We will use the --start=asap option in haste so that our main code can be the same for haste and UHC.</p>
<p>As can be read at several places (e. g. <a href="https://nokarma.org/2011/02/02/javascript-game-development-the-game-loop/index.html">here</a> or <a href="https://www.playmycode.com/blog/2011/08/building-a-game-mainloop-in-javascript/">here</a>) we can not just write an infinite loop for our GameLoop in javascript because it would block the browser. The contents of the canvas will only be updated when our code returns.</p>
<p>So we need a function that is called in intervals. Javascript allows us to set the interval with window.setInterval.</p>
<h1 id="setting-callbacks">Setting callbacks</h1>
<p>We need to set haskell functions as callbacks to be invoked from javascript code.</p>
<h2 id="uhc">UHC</h2>
<p>In UHC we import a special function which converts haskell functions to callbacks that can be called from javascript (see <a href="https://www.norm2782.com/improving-uhc-js-report.pdf">Improving UHC js</a>).</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">import</span> <span class="dt">UHC.Ptr</span>
foreign <span class="kw">import</span> js <span class="st">"wrapper"</span><span class="ot"> mkCb ::</span> <span class="dt">IO</span> () <span class="ot">-></span> <span class="dt">IO</span> (<span class="dt">FunPtr</span> (<span class="dt">IO</span> ()))</code></pre>
<p>This converts an IO action to a function pointer that can be passed to javascript.</p>
<p>If we want our callback to have different or more arguments, we have to import wrapper with a different signature. Here we create callbacks that we can pass as key event handlers.</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span> <span class="dt">JSKeyEvent</span>
foreign <span class="kw">import</span> js <span class="st">"wrapper"</span>
<span class="ot"> mkKeyEventCb ::</span> (<span class="dt">JSKeyEvent</span> <span class="ot">-></span> <span class="dt">IO</span> ()) <span class="ot">-></span> <span class="dt">IO</span> (<span class="dt">FunPtr</span> (<span class="dt">JSKeyEvent</span> <span class="ot">-></span> <span class="dt">IO</span> ()))</code></pre>
<p>We now need to import the functions with which we set the callbacks. The interval function is set with "setInterval" while "onLoad" and the key event callbacks can be set with "addEventListener" which must be called on an element of the webpage. This element can be retrieved with "getElementById". We define simplified versions that take care of creating the callback for us.</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span> <span class="dt">Element</span>
foreign <span class="kw">import</span> js <span class="st">"document.getElementById(%1)"</span>
<span class="ot"> jsGetElementById ::</span> <span class="dt">JSString</span> <span class="ot">-></span> <span class="dt">IO</span> <span class="dt">Element</span>
getElementById <span class="fu">=</span> jsGetElementById <span class="fu">.</span> toJS
foreign <span class="kw">import</span> js <span class="st">"%1.keyCode"</span>
<span class="ot"> keyCode ::</span> <span class="dt">JSKeyEvent</span> <span class="ot">-></span> <span class="dt">IO</span> <span class="dt">Int</span>
foreign <span class="kw">import</span> js <span class="st">"%1.addEventListener('keydown',%2,true)"</span>
<span class="ot"> jsSetOnKeyDown ::</span> <span class="dt">Element</span> <span class="ot">-></span> <span class="dt">FunPtr</span> (<span class="dt">JSKeyEvent</span> <span class="ot">-></span> <span class="dt">IO</span> ()) <span class="ot">-></span> <span class="dt">IO</span> ()
<span class="ot">setOnKeyDown ::</span> <span class="dt">String</span> <span class="ot">-></span> (<span class="dt">Int</span> <span class="ot">-></span> <span class="dt">IO</span> ()) <span class="ot">-></span> <span class="dt">IO</span> ()
setOnKeyDown elemName fp <span class="fu">=</span> <span class="kw">do</span>
cb <span class="ot"><-</span> mkKeyEventCb fp'
el <span class="ot"><-</span> getElementById elemName
jsSetOnKeyDown el cb
<span class="kw">where</span>
fp' event <span class="fu">=</span> keyCode event <span class="fu">>>=</span> fp
foreign <span class="kw">import</span> js <span class="st">"%1.addEventListener('keyup',%2,true)"</span>
<span class="ot"> jsSetOnKeyUp ::</span> <span class="dt">Element</span> <span class="ot">-></span> <span class="dt">FunPtr</span> (<span class="dt">JSKeyEvent</span> <span class="ot">-></span> <span class="dt">IO</span> ()) <span class="ot">-></span> <span class="dt">IO</span> ()
<span class="ot">setOnKeyUp ::</span> <span class="dt">String</span> <span class="ot">-></span> (<span class="dt">Int</span> <span class="ot">-></span> <span class="dt">IO</span> ()) <span class="ot">-></span> <span class="dt">IO</span> ()
setOnKeyUp elemName fp <span class="fu">=</span> <span class="kw">do</span>
cb <span class="ot"><-</span> mkKeyEventCb fp'
el <span class="ot"><-</span> getElementById elemName
jsSetOnKeyUp el cb
<span class="kw">where</span>
fp' event <span class="fu">=</span> keyCode event <span class="fu">>>=</span> fp
foreign <span class="kw">import</span> js <span class="st">"window.addEventListener('load', %1, 'false')"</span>
<span class="ot"> jsSetOnLoad ::</span> <span class="dt">FunPtr</span> (<span class="dt">IO</span> ()) <span class="ot">-></span> <span class="dt">IO</span> ()
<span class="ot">setOnLoad ::</span> <span class="dt">IO</span> () <span class="ot">-></span> <span class="dt">IO</span> ()
setOnLoad fp <span class="fu">=</span> mkCb fp <span class="fu">>>=</span> jsSetOnLoad
foreign <span class="kw">import</span> js <span class="st">"setInterval(%1,%2)"</span>
<span class="ot"> jsSetInterval ::</span> <span class="dt">FunPtr</span> (<span class="dt">IO</span> ()) <span class="ot">-></span> <span class="dt">Double</span> <span class="ot">-></span> <span class="dt">IO</span> ()
<span class="ot">setInterval ::</span> <span class="dt">Double</span> <span class="ot">-></span> <span class="dt">IO</span> () <span class="ot">-></span> <span class="dt">IO</span> ()
setInterval time fp <span class="fu">=</span> <span class="kw">do</span>
cb <span class="ot"><-</span> mkCb fp
jsSetInterval cb time</code></pre>
<p>Remember that the >>= operator chains monadic actions. "setOnKeyDown" and "setOnKeyUp" set the event listener on an element defined by the given name. They define wrapper functions that extract the keycode and passes it to our callback functions. This is convenient because the keycode is the information we are really interested in.</p>
<p>We will follow the convention, that functions taking javascript specific parameters (such as JSString) will be prefixed by "js" and have corresponding functions without the "js" prefix.</p>
<h2 id="haste">Haste</h2>
<blockquote>
<p><strong>Update:</strong> The way FFI functions have to writting with haste has changed since the blog post has original been written. At that time returning values from javascript to haskell was a little bit more cumbersome. I have updated this blog post to reflect the new way of doing it. I hope I did not forget something in the process. So if you find an error, please comment.</p>
</blockquote>
<p>Setting callbacks in haste is a little different.</p>
<p>For every callback function a javascript function has to be created which invokes the special function "A()" with the callback. This function can than be used for the callback. Read the section "Callbacks" of <a href="https://github.com/valderman/haste-compiler/blob/master/doc/js-externals.txt">js-externls.txt</a> in the doc subdirectory of the <a href="https://github.com/valderman/haste-compiler">haste github repository</a>.</p>
<p>The arguments for the haskell function are the second argument of "A()" and have to be passed as a list similar to the required return value of javascript function included by the FFI. Again, this is explained in <a href="https://github.com/valderman/haste-compiler/blob/master/doc/js-externals.txt">js-externals.txt</a>.</p>
<pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="kw">function</span> jsSetInterval(msecs, cb) {
<span class="kw">window</span>.<span class="fu">setInterval</span>(<span class="kw">function</span>() {A(cb,[<span class="dv">0</span>]);}, msecs);
<span class="kw">return</span>;
}
<span class="kw">function</span> jsSetOnLoad(cb) {
<span class="kw">window</span>.<span class="fu">addEventListener</span>(<span class="ch">'load'</span>, <span class="kw">function</span>() {A(cb,[<span class="dv">0</span>]);}, <span class="kw">false</span>);
<span class="kw">return</span>;
}</code></pre>
<p>For "setOnKeyUp" and "setOnKeyDown" we do not need to define any haskell function, because we can set them using the haste library in haskell. On the haskell side callbacks have to be created with "mkCallback" and have the Type "JSFun a".</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell">foreign <span class="kw">import</span> ccall<span class="ot"> jsSetInterval ::</span> <span class="dt">Double</span> <span class="ot">-></span> <span class="dt">JSFun</span> (<span class="dt">IO</span> ()) <span class="ot">-></span> <span class="dt">IO</span> ()
<span class="ot">setInterval ::</span> <span class="dt">Double</span> <span class="ot">-></span> <span class="dt">IO</span> () <span class="ot">-></span> <span class="dt">IO</span> ()
setInterval time cb <span class="fu">=</span>
jsSetInterval time (mkCallback <span class="fu">$!</span> cb)
foreign <span class="kw">import</span> ccall<span class="ot"> jsSetOnLoad ::</span> <span class="dt">JSFun</span> (<span class="dt">IO</span> ()) <span class="ot">-></span> <span class="dt">IO</span> ()
setOnLoad cb <span class="fu">=</span> jsSetOnLoad (mkCallback <span class="fu">$!</span> cb)
<span class="ot">setOnKeyDown ::</span> <span class="dt">String</span> <span class="ot">-></span> (<span class="dt">Int</span> <span class="ot">-></span> <span class="dt">IO</span> ()) <span class="ot">-></span> <span class="dt">IO</span> <span class="dt">Bool</span>
setOnKeyDown elementName cb <span class="fu">=</span> withElem elementName <span class="fu">$</span> \e <span class="ot">-></span> setCallback e <span class="dt">OnKeyDown</span> cb
<span class="ot">setOnKeyUp ::</span> <span class="dt">String</span> <span class="ot">-></span> (<span class="dt">Int</span> <span class="ot">-></span> <span class="dt">IO</span> ()) <span class="ot">-></span> <span class="dt">IO</span> <span class="dt">Bool</span>
setOnKeyUp elementName cb <span class="fu">=</span> withElem elementName <span class="fu">$</span> \e <span class="ot">-></span> setCallback e <span class="dt">OnKeyUp</span> cb</code></pre>
<p>The "withElem" functions is defined in the haste library and executes an action with a webpage element defined by the provided name.</p>
<h1 id="letting-callbacks-communicate">Letting callbacks communicate</h1>
<h2 id="uhc-1">UHC</h2>
<p>In a javascript program we would let the onKeyUp, onKeyDown and Interval functions communicate through global variables. In haskell we do not have a mechanism such as global variables (at least non that I am aware of). In a normal situation we do not need it, because the only moment when main exits is when the program ends.</p>
<p>To store global variables we write a few helper functions in javascript:</p>
<pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="kw">var</span> allObjects = {}
<span class="kw">function</span> jsSaveGlobalObject(name, obj) {
allObjects[name] = obj;
}
<span class="kw">function</span> jsLoadGlobalObject(name) {
<span class="kw">return</span> allObjects[name];
}</code></pre>
<p>And include them from haskell:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell">foreign <span class="kw">import</span> ccall<span class="ot"> jsSaveGlobalObject ::</span> <span class="dt">JSString</span> <span class="ot">-></span> a <span class="ot">-></span> <span class="dt">IO</span> ()
foreign <span class="kw">import</span> ccall<span class="ot"> jsLoadGlobalObject ::</span> <span class="dt">JSString</span> <span class="ot">-></span> <span class="dt">IO</span> a
<span class="ot">saveGlobalObject ::</span> <span class="dt">String</span> <span class="ot">-></span> a <span class="ot">-></span> <span class="dt">IO</span> ()
saveGlobalObject name obj <span class="fu">=</span> jsSaveGlobalObject (toJS name) obj
<span class="ot">loadGlobalObject ::</span> <span class="dt">String</span> <span class="ot">-></span> <span class="dt">IO</span> a
loadGlobalObject name <span class="fu">=</span> <span class="kw">do</span>
ptr <span class="ot"><-</span> jsLoadGlobalObject (toJS name)
<span class="fu">return</span> <span class="fu">$</span> ptr</code></pre>
<p>We can now load the current state with</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell">state <span class="ot"><-</span> jsLoadGlobalObject <span class="st">"state"</span><span class="ot"> ::</span> <span class="dt">IO</span> <span class="dt">State</span></code></pre>
<p>When we enter one of our callback functions and save it with a corresponding call to "saveGlobalObject".</p>
<h2 id="haste-1">Haste</h2>
<p>For haste the mechanism works the same way, only that the javascript helper functions have to format their return values the way haste needs them:</p>
<pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="kw">var</span> allObjects = {}
<span class="kw">function</span> jsSaveGlobalObject(name, obj) {
allObjects[name] = obj;
<span class="kw">return</span>;
}
<span class="kw">function</span> jsLoadGlobalObject(name) {
<span class="kw">return</span> allObjects[name];
}</code></pre>
<p>To be able to pass arbitrary objects to the FFI, they have to be converted to a pointer via "fromPtr" and "toPtr".</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell">foreign <span class="kw">import</span> ccall<span class="ot"> jsSaveGlobalObject ::</span> <span class="dt">JSString</span> <span class="ot">-></span> <span class="dt">Ptr</span> a <span class="ot">-></span> <span class="dt">IO</span> ()
foreign <span class="kw">import</span> ccall<span class="ot"> jsLoadGlobalObject ::</span> <span class="dt">JSString</span> <span class="ot">-></span> <span class="dt">IO</span> (<span class="dt">Ptr</span> a)
<span class="ot">saveGlobalObject ::</span> <span class="dt">String</span> <span class="ot">-></span> a <span class="ot">-></span> <span class="dt">IO</span> ()
saveGlobalObject name obj <span class="fu">=</span> jsSaveGlobalObject (toJSStr name) (toPtr obj)
<span class="ot">loadGlobalObject ::</span> <span class="dt">String</span> <span class="ot">-></span> <span class="dt">IO</span> a
loadGlobalObject name <span class="fu">=</span> <span class="kw">do</span>
ptr <span class="ot"><-</span> jsLoadGlobalObject (toJSStr name)
<span class="fu">return</span> <span class="fu">$</span> fromPtr ptr</code></pre>
<h1 id="drawing-on-the-canvas">Drawing on the canvas</h1>
<p>Example code for drawing on the canvas in javascript looks like this:</p>
<pre class="sourceCode javascript"><code class="sourceCode javascript">context = <span class="kw">document</span>.<span class="fu">getElementById</span>(<span class="st">"canvas"</span>).<span class="fu">getContext</span>(<span class="st">"2d"</span>);
<span class="kw">context</span>.<span class="fu">clearRect</span>(<span class="fl">0.0</span>, <span class="fl">0.0</span>, <span class="kw">context.canvas</span>.<span class="fu">width</span>, <span class="kw">context.canvas</span>.<span class="fu">height</span>)
<span class="fu">context</span>.<span class="fu">setFillColor</span>(<span class="st">"green"</span>);
<span class="kw">context</span>.<span class="fu">fillRect</span>(<span class="fl">10.0</span>,<span class="fl">10.0</span>,<span class="fl">100.0</span>,<span class="fl">100.0</span>);</code></pre>
<p>Basically all we have to do is import these functions via the FFI.</p>
<h2 id="uhc-2">UHC</h2>
<p>Getting the context needs several steps:</p>
<ul>
<li>Get the canvas via getElementById</li>
<li>Get the context via getContext</li>
</ul>
<p>So this is what we do:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span> <span class="dt">Context2D</span>
foreign <span class="kw">import</span> js <span class="st">"%1.getContext('2d')"</span>
<span class="ot"> getContext2dFromCanvas ::</span> <span class="dt">Element</span> <span class="ot">-></span> <span class="dt">IO</span> <span class="dt">Context2D</span>
<span class="ot">getContext2d ::</span> <span class="dt">String</span> <span class="ot">-></span> <span class="dt">IO</span> <span class="dt">Context2D</span>
getContext2d canvasName <span class="fu">=</span> <span class="kw">do</span>
c <span class="ot"><-</span> getElementById canvasName
getContext2dFromCanvas c</code></pre>
<p>Importing the rest of the functions is straight forward:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell">foreign <span class="kw">import</span> js <span class="st">"%1.fillRect(%*)"</span>
<span class="ot"> fillRect ::</span> <span class="dt">Context2D</span> <span class="ot">-></span> <span class="dt">Double</span> <span class="ot">-></span> <span class="dt">Double</span> <span class="ot">-></span> <span class="dt">Double</span> <span class="ot">-></span> <span class="dt">Double</span> <span class="ot">-></span> <span class="dt">IO</span> ()
foreign <span class="kw">import</span> js <span class="st">"jsSetFillColor(%*)"</span>
<span class="ot"> jsSetFillColor ::</span> <span class="dt">Context2D</span> <span class="ot">-></span> <span class="dt">JSString</span> <span class="ot">-></span> <span class="dt">IO</span> ()
setFillColor ctx <span class="fu">=</span> jsSetFillColor ctx <span class="fu">.</span> toJS
foreign <span class="kw">import</span> js <span class="st">"%1.clearRect(%2, %3, %4, %5)"</span>
<span class="ot"> clearRect ::</span> <span class="dt">Context2D</span> <span class="ot">-></span> <span class="dt">Double</span> <span class="ot">-></span> <span class="dt">Double</span> <span class="ot">-></span> <span class="dt">Double</span> <span class="ot">-></span> <span class="dt">Double</span> <span class="ot">-></span> <span class="dt">IO</span> ()
foreign <span class="kw">import</span> js <span class="st">"%1.canvas.width"</span><span class="ot"> canvasWidth ::</span> <span class="dt">Context2D</span> <span class="ot">-></span> <span class="dt">IO</span> <span class="dt">Double</span>
foreign <span class="kw">import</span> js <span class="st">"%1.canvas.height"</span><span class="ot"> canvasHeight ::</span> <span class="dt">Context2D</span> <span class="ot">-></span> <span class="dt">IO</span> <span class="dt">Double</span>
<span class="ot">clear ::</span> <span class="dt">Context2D</span> <span class="ot">-></span> <span class="dt">IO</span> ()
clear ctx <span class="fu">=</span> <span class="kw">do</span>
w <span class="ot"><-</span> canvasWidth ctx
h <span class="ot"><-</span> canvasHeight ctx
clearRect ctx <span class="fl">0.0</span> <span class="fl">0.0</span> w h</code></pre>
<p>We have defined "clear" for convenience. It clears the whole canvas.</p>
<p>** Note: ** For some reason, "context.setFillColor" does not work on firefox. Therefor a helper function is defined setting the color via fillStyle.</p>
<pre class="sourceCode JavaScript"><code class="sourceCode javascript"><span class="kw">function</span> jsSetFillColor(context, color) {
<span class="kw">context</span>.<span class="fu">fillStyle</span> = color;
}</code></pre>
<h2 id="haste-2">Haste</h2>
<p>Again, for haste we have to write javascript functions with the correct return type:</p>
<pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="kw">function</span> jsGetContext2d(canvas) {
<span class="kw">return</span> <span class="kw">canvas</span>.<span class="fu">getContext</span>(<span class="st">"2d"</span>);
}
<span class="kw">function</span> jsFillRect(context, x, y, width, height) {
<span class="kw">context</span>.<span class="fu">fillRect</span>(x,y,width,height);
<span class="kw">return</span>;
}
<span class="kw">function</span> jsSetFillColor(context, color) {
<span class="kw">context</span>.<span class="fu">fillStyle</span> = color;
<span class="kw">return</span>;
}
<span class="kw">function</span> jsClear(context) {
<span class="kw">context</span>.<span class="fu">clearRect</span>(<span class="fl">0.0</span>, <span class="fl">0.0</span>, <span class="kw">context.canvas</span>.<span class="fu">width</span>, <span class="kw">context.canvas</span>.<span class="fu">height</span>);
<span class="kw">return</span>;
}</code></pre>
<p>And here the haskell part:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">import</span> <span class="dt">Haste.Prim</span>
<span class="kw">import</span> <span class="dt">Haste.DOM</span>
<span class="kw">newtype</span> <span class="dt">Context2D</span> <span class="fu">=</span> <span class="dt">Context2D</span> <span class="dt">JSAny</span>
foreign <span class="kw">import</span> ccall <span class="st">"jsGetContext2D"</span>
<span class="ot"> jsGetContext2d ::</span> <span class="dt">Elem</span> <span class="ot">-></span> <span class="dt">IO</span> <span class="dt">Context2D</span>
getContext2d name <span class="fu">=</span> withElem name getContext2D
foreign <span class="kw">import</span> ccall <span class="st">"jsFillRect"</span>
<span class="ot"> fillRect ::</span> <span class="dt">Context2D</span> <span class="ot">-></span> <span class="dt">Double</span> <span class="ot">-></span> <span class="dt">Double</span> <span class="ot">-></span> <span class="dt">Double</span> <span class="ot">-></span> <span class="dt">Double</span> <span class="ot">-></span> <span class="dt">IO</span> ()
foreign <span class="kw">import</span> ccall<span class="ot"> jsSetFillColor ::</span> <span class="dt">Context2D</span> <span class="ot">-></span> <span class="dt">JSString</span> <span class="ot">-></span> <span class="dt">IO</span> ()
setFillColor ctx <span class="fu">=</span> jsSetFillColor ctx <span class="fu">.</span> toJSStr
foreign <span class="kw">import</span> ccall <span class="st">"jsClear"</span>
<span class="ot"> clear ::</span> <span class="dt">Context2D</span> <span class="ot">-></span> <span class="dt">IO</span> ()</code></pre>
<p>Again we use "withElem" function from Haskell.DOM which executes an action with the element specified by the provided name.</p>
<h1 id="putting-it-all-together">Putting it all together</h1>
<p>All imported functions are named, so that the main code is the same for UHC and haste. The folloging code should explain itself through the comments:</p>
<pre class="sourceCode haskell numberlines"><code class="sourceCode haskell"><span class="kw">module</span> <span class="dt">Main</span> <span class="kw">where</span>
<span class="kw">import</span> <span class="dt">JavaScript</span>
canvasName <span class="fu">=</span> <span class="st">"canvas1"</span>
playerY <span class="fu">=</span> <span class="fl">380.0</span>
playerWidth <span class="fu">=</span> <span class="fl">60.0</span>
playerHeight <span class="fu">=</span> <span class="fl">20.0</span>
playerSpeed <span class="fu">=</span> <span class="fl">3.0</span>
playerColor <span class="fu">=</span> <span class="st">"green"</span>
<span class="kw">data</span> <span class="dt">State</span> <span class="fu">=</span> <span class="dt">State</span> {<span class="ot">x ::</span> <span class="dt">Double</span>}
initState <span class="fu">=</span> <span class="dt">State</span> <span class="fl">300.0</span>
main <span class="fu">=</span> setOnLoad initilize
<span class="ot">initilize ::</span> <span class="dt">IO</span> ()
initilize <span class="fu">=</span> <span class="kw">do</span>
saveGlobalObject <span class="st">"state"</span> initState
setInterval <span class="fl">30.0</span> update
setOnKeyDown canvasName onKeyDown
<span class="fu">return</span> ()
<span class="ot">onKeyDown ::</span> <span class="dt">Int</span> <span class="ot">-></span> <span class="dt">IO</span> ()
onKeyDown code <span class="fu">=</span> <span class="kw">do</span>
s <span class="ot"><-</span> loadGlobalObject <span class="st">"state"</span><span class="ot"> ::</span> <span class="dt">IO</span> <span class="dt">State</span>
<span class="kw">let</span> s' <span class="fu">=</span> <span class="kw">case</span> code <span class="kw">of</span>
<span class="dv">39</span> <span class="ot">-></span> s {x <span class="fu">=</span> (x s) <span class="fu">+</span> playerSpeed}
<span class="dv">37</span> <span class="ot">-></span> s {x <span class="fu">=</span> (x s) <span class="fu">-</span> playerSpeed}
_ <span class="ot">-></span> s
saveGlobalObject <span class="st">"state"</span> s'
<span class="ot">update ::</span> <span class="dt">IO</span> ()
update <span class="fu">=</span> <span class="kw">do</span>
s <span class="ot"><-</span> loadGlobalObject <span class="st">"state"</span><span class="ot"> ::</span> <span class="dt">IO</span> <span class="dt">State</span>
ctx <span class="ot"><-</span> getContext2d canvasName
clear ctx
setFillColor ctx playerColor
fillRect ctx (x s) playerY playerWidth playerHeight</code></pre>
<p>To compile with UHC you need <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/3_FirstInteractive/code/uhc/JavaScript.hs">JavaScript.hs</a> and <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/3_FirstInteractive/code/uhc/helpers.js">helpers.js</a>. Than run:</p>
<pre class="sourceCode bash"><code class="sourceCode bash">uhc -tjs Main.hs</code></pre>
<p>Edit the resulting HTML page and add</p>
<pre class="sourceCode html"><code class="sourceCode html"><span class="kw"><script</span><span class="ot"> type=</span><span class="st">"text/javascript"</span><span class="ot"> src=</span><span class="st">"helpers.js"</span><span class="kw">></script></span></code></pre>
<p>into the head and</p>
<pre class="sourceCode html"><code class="sourceCode html"><span class="kw"><canvas</span><span class="ot"> id=</span><span class="st">"canvas1"</span><span class="ot"> width=</span><span class="st">600</span><span class="ot"> height=</span><span class="st">500</span><span class="ot"> tabindex=</span><span class="st">"0"</span><span class="kw">></canvas></span></code></pre>
<p>to the body.</p>
<p>For haste you need this <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/3_FirstInteractive/code/haste/JavaScript.hs">JavaScript.hs</a> und <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/3_FirstInteractive/code/haste/helpers.js">helpers.js</a> and compile it with:</p>
<pre class="sourceCode bash"><code class="sourceCode bash">hastec --with-js=helpers.js Main.hs --start=asap</code></pre>
<p>The "--start=asap" parameter is necessary because we set the onLoad function ourself. Than embed it in this HTML page:</p>
<pre class="sourceCode html"><code class="sourceCode html"><span class="dt"><!DOCTYPE </span>html<span class="dt">></span><span class="kw"><html><head><title></span>Main<span class="kw"></title></span>
<span class="kw"><script</span><span class="ot"> type=</span><span class="st">"text/javascript"</span><span class="ot"> src=</span><span class="st">"Main.js"</span><span class="kw">></script></span>
<span class="kw"></head></span>
<span class="kw"><body></span>
<span class="kw"><canvas</span><span class="ot"> id=</span><span class="st">"canvas1"</span><span class="ot"> height=</span><span class="st">"400"</span><span class="ot"> width=</span><span class="st">"600"</span><span class="ot"> tabindex=</span><span class="st">"0"</span><span class="kw">></span>Your browser des not support canvas<span class="kw"></canvas></span>
<span class="kw"></body></span>
<span class="kw"></html></span></code></pre>
<p>The running example can be seen at the beginning of this post.</p>
<h2 id="summary">Summary</h2>
<p>This has been a rather long post, but we have learned a lot. With the tools at hand we can now update out game with an callback function we set via "setInterval" and receive keyboard events. The next post will be another step towards our goal of a breakout clone, we will implement the ball and let it bounce on the paddle and the walls.</p>
<a href="https://creativecommons.org/licenses/by/3.0/de/deed.en_US" rel="license"><img alt="Creative Commons License" src="https://i.creativecommons.org/l/by/3.0/de/88x31.png" style="border-width: 0;" /></a><br />
<span href="https://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type" xmlns:dct="https://purl.org/dc/terms/">Writing JavaScript games in Haskell</span> by <a href="https://jshaskell.blogspot.de/" property="cc:attributionName" rel="cc:attributionURL" xmlns:cc="https://creativecommons.org/ns#">Nathan Hüsken</a> is licensed under a <a href="https://creativecommons.org/licenses/by/3.0/de/deed.en_US" rel="license">Creative Commons Attribution 3.0 Germany License</a>. Nathan Hüsken https://www.blogger.com/profile/09614845657227846437 noreply@blogger.com 12 tag:blogger.com,1999:blog-6355249120999252418.post-6708999046200843854 2012-07-10T14:08:00.003-07:00 2013-08-30T10:02:45.964-07:00 Hello, World <p>The first thing we need on our journey is a compiler that translates out haskell to javascript. On this page: <a href="https://www.haskell.org/haskellwiki/The_JavaScript_Problem">The JavaScript Problem</a> you can find a list of compilers that are able to do that. I will focus on UHC and haste. GHCJS also seems promising, and I might try it later.</p>
<p>So we want to write a little hello world program. The equivalent in javascript would be</p>
<pre class="sourceCode Javascript"><code class="sourceCode javascript"><span class="kw">document</span>.<span class="fu">write</span>(<span class="st">"Hello, World!"</span>);</code></pre>
<p>which replaces the contents of the document with "Hello, World!". Let us get started.</p>
<h1 id="uhc">UHC</h1>
<p>The <a href="https://www.cs.uu.nl/wiki/UHC/">Utrecht Haskell Compiler</a> has a backend allowing to compile haskell to javascript. This page links all the information about the JavaScript backend: <a href="https://uu-computerscience.github.com/uhc-js/">UHC-JS</a></p>
<h2 id="installing-uhc">Installing UHC</h2>
<p>First we have to install UHC. You can find the code for UHC here <a href="https://github.com/UU-ComputerScience/uhc">UHC GitHub</a>. The build instructions can be found in the EHC sub directory.</p>
<p>But first some dependencies are needed. On ubuntu linux I install them with apt-get:</p>
<pre class="sourceCode bash"><code class="sourceCode bash">apt-get <span class="kw">install</span> ghc cabal-install build-essentials libtool uuagc</code></pre>
<p>We also need to install a few haskell packages via cabal:</p>
<pre class="sourceCode bash"><code class="sourceCode bash">cabal update
cabal <span class="kw">install</span> network uulib syb fgl</code></pre>
<p>To install UHC, first clone the repository and change into the EHC directory. Then build and install UHC.</p>
<pre class="sourceCode bash"><code class="sourceCode bash">git clone git://github.com/UU-ComputerScience/uhc.git
<span class="kw">cd</span> uhc/EHC
./configure
<span class="kw">make</span>
<span class="kw">sudo</span> <span class="kw">make</span> <span class="kw">install</span></code></pre>
<p>In blogs it is common to suggesting getting a cup of coffee at a moment like this, because the make command may take a while. So get a cup of coffee!</p>
<p>If everthing worked out, UHC should be installed and you can compile to javascript via</p>
<pre class="sourceCode bash"><code class="sourceCode bash">uhc -tjs Main.hs</code></pre>
<p>(Or however your haskell file is called).</p>
<h2 id="hello-world-with-uhc">Hello World with UHC</h2>
<p>We need to do two things:</p>
<ul>
<li>Get the document.</li>
<li>Call write on the document with "Hello, World!" as parameter.</li>
</ul>
<p>The FFI (ForeignFunctionInterface) of the js backend of UHC is described here <a href="https://www.norm2782.com/improving-uhc-js-report.pdf">Improving UHC js</a> For our purposes, it works like this:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell">foreign <span class="kw">import</span> js <span class="st">"jscommand"</span><span class="ot"> haskellName ::</span> <span class="dt">Type</span></code></pre>
<p>Where "jscommand" is the command in javascript, "haskellName" the name of the haskell function we want to define and "Type" the type of the haskell function. "jscommand" may contain "%N" where N is a number refering to the N-th parameter of the haskell function.</p>
<p>So to get the document we first define a type for it and then import a corresponding javascript command</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span> <span class="dt">Document</span>
foreign <span class="kw">import</span> js <span class="st">"document"</span><span class="ot"> getDocument ::</span> <span class="dt">IO</span> <span class="dt">Document</span></code></pre>
<p>To get the document we just have to call "document" in javascript. This returns us the document in the IO monad. The document is in the IO monad because we a calling a foreign function which might have side effects.</p>
<p>Now we want to call "write" on a document.</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell">foreign <span class="kw">import</span> js <span class="st">"%1.write(%2)"</span><span class="ot"> write ::</span> <span class="dt">Document</span> <span class="ot">-></span> <span class="dt">JSString</span> <span class="ot">-></span> <span class="dt">IO</span> ()</code></pre>
<p>Because the "%1" is in front of the ".write", the first argument to haskell function "write" (which is the document) is the object write is called on (this can all be found in <a href="https://www.norm2782.com/improving-uhc-js-report.pdf">Improving UHC js</a>.</p>
<p>Note that the second argument is of type JSString and not of String. This is because a string in haskell is not the same as a string in javascript. We have to convert a haskell string to a javascript string</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">type</span> <span class="dt">JSString</span> <span class="fu">=</span> <span class="dt">PackedString</span>
foreign <span class="kw">import</span> prim <span class="st">"primStringToPackedString"</span><span class="ot"> toJS ::</span> <span class="dt">String</span> <span class="ot">-></span> <span class="dt">JSString</span></code></pre>
<p>Now we are ready to write our hello world:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">type</span> <span class="dt">JSString</span> <span class="fu">=</span> <span class="dt">PackedString</span>
foreign <span class="kw">import</span> prim <span class="st">"primStringToPackedString"</span><span class="ot"> toJS ::</span> <span class="dt">String</span> <span class="ot">-></span> <span class="dt">JSString</span>
<span class="kw">data</span> <span class="dt">Document</span>
foreign <span class="kw">import</span> js <span class="st">"document"</span><span class="ot"> getDocument ::</span> <span class="dt">IO</span> <span class="dt">Document</span>
foreign <span class="kw">import</span> js <span class="st">"%1.write(%2)"</span><span class="ot"> write ::</span> <span class="dt">Document</span> <span class="ot">-></span> <span class="dt">JSString</span> <span class="ot">-></span> <span class="dt">IO</span> ()
main <span class="fu">=</span> <span class="kw">do</span>
doc <span class="ot"><-</span> getDocument
write doc <span class="fu">$</span> toJS <span class="st">"Hello, World!"</span></code></pre>
<h1 id="haste">Haste</h1>
<h2 id="installing-haste">Installing haste</h2>
<p>Haste can be found here: <a href="https://github.com/valderman/haste-compiler">Haste GitHub</a>. Instructions for installation can also be found on that page (just read the Building section). It requires installation of fursuit, which can be found here: <a href="https://github.com/valderman/fursuit">Fursuit GitHub</a>.</p>
<h2 id="hello-world-with-haste">Hello World with haste</h2>
<p>Again, we need a way to import a function to get the document and to write into the contents of the document. Information on how to import javascript functions can be found in <a href="https://github.com/valderman/haste-compiler/blob/master/doc/js-externals.txt">the doc subdirectory</a> of the haste repository.</p>
<blockquote>
<p><strong>Update:</strong> The way FFI functions have to writting with haste has changed since the blog post has original been written. At that time returning values from javascript to haskell was a little bit more cumbersome. I have updated this blog post to reflect the new way of doing it. I hope I did not forget something in the process. So if you find an error, please comment.</p>
</blockquote>
<p>Haste is not as flexible as UHC when importing JavaScript functions. It does not allow placing the parameters of the haskell function in the javascript code with "%N". It also does not allow the custom type "Document" to be used as a parameter or return type. Instead "JSAny" must be used.</p>
<p>So we create a file "helpers.hs" with out Javascript helper functions:</p>
<pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="kw">function</span> objWrite(obj, text) {
<span class="kw">obj</span>.<span class="fu">write</span>(text); <span class="co">// Call the method</span>
<span class="kw">return</span>; <span class="co">// Return ()</span>
}
<span class="kw">function</span> getDocument() {
<span class="kw">return</span> <span class="kw">document</span>;
}</code></pre>
<p>This now allows us to write hello world in Haste:</p>
<pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">import</span> <span class="dt">Haste</span>
<span class="kw">import</span> <span class="dt">Haste.Prim</span>
<span class="kw">type</span> <span class="dt">Document</span> <span class="fu">=</span> <span class="dt">JSAny</span>
foreign <span class="kw">import</span> ccall <span class="st">"objWrite"</span><span class="ot"> write ::</span> <span class="dt">Document</span> <span class="ot">-></span> <span class="dt">JSString</span> <span class="ot">-></span> <span class="dt">IO</span> ()
foreign <span class="kw">import</span> ccall <span class="st">"getDocument"</span><span class="ot"> getDocument ::</span> <span class="dt">IO</span> <span class="dt">Document</span>
main <span class="fu">=</span> <span class="kw">do</span>
doc <span class="ot"><-</span> getDocument
write doc <span class="fu">$</span> toJSStr <span class="st">"Hello World!"</span></code></pre>
<p>Compile this with:</p>
<pre class="sourceCode bash"><code class="sourceCode bash">hastec Main.hs --with-js=helpers.js</code></pre>
<p>Now all we need to do is embed this file in a html page:</p>
<pre class="sourceCode html"><code class="sourceCode html"><span class="dt"><!DOCTYPE </span>html<span class="dt">></span><span class="kw"><html><head><title></span>Main<span class="kw"></title></span>
<span class="kw"><script</span><span class="ot"> type=</span><span class="st">"text/javascript"</span><span class="ot"> src=</span><span class="st">"Main.js"</span><span class="kw">></script></span>
<span class="kw"></head></span>
<span class="kw"><body></span>
<span class="kw"></body></span>
<span class="kw"></html></span></code></pre>
<p>Open this with a browser of your choice (I only tried chromium) and it should work.</p>
<h1 id="edit-trying-it-out"><em>Edit:</em> Trying it out</h1>
<p>As suggested in a comment, I have uploaded the compiled javascript file (the haste version because the UHC version has several dependencies) here: <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/2_HelloWorld/HelloWorld.js">Hello, World!</a></p>
<p>Download it and <a href="https://github.com/RudolfVonKrugstein/jshaskell-blog/blob/master/2_HelloWorld/HelloWorld.html">this</a> html file into the same directory. Than open the html with a browser of your choice (tested on ubuntu linux, chromium).</p>
<p>Edit: Rewrote with pandoc</p>
<a rel="license" href="https://creativecommons.org/licenses/by/3.0/de/deed.en_US"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/3.0/de/88x31.png" /></a><br /><span xmlns:dct="https://purl.org/dc/terms/" href="https://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">Writing JavaScript games in Haskell</span> by <a xmlns:cc="https://creativecommons.org/ns#" href="https://jshaskell.blogspot.de/" property="cc:attributionName" rel="cc:attributionURL">Nathan Hüsken</a> is licensed under a <a rel="license" href="https://creativecommons.org/licenses/by/3.0/de/deed.en_US">Creative Commons Attribution 3.0 Germany License</a>. Nathan Hüsken https://www.blogger.com/profile/09614845657227846437 noreply@blogger.com 5 tag:blogger.com,1999:blog-6355249120999252418.post-4273514564244133005 2012-07-10T06:06:00.003-07:00 2012-07-17T05:33:21.507-07:00 About this blog <p>This blog will be about my experience with attempting to write javascript games in haskell. So far I have very little experience in javascript and in haskell. From my first attempts in haskell I can already tell that this will not be easy, as haskell has a complete different approach to programming than languages like C++ (with which I have much more experience).</p>
<h1 id="why-javascript-and-why-haskell">Why JavaScript and why Haskell?</h1>
<p>I am not attempting to write huge games, I want to write small games which are fun to play. An javascript enabled browser seems to be the perfect platform. Games running in the browser need no extra installation and are accessible from almost every computer. This way it is convenient for the audience to reach the games.</p>
<p><em>But why Haskell</em>? A while ago I encountered haskell for the first time and I was fascinated. The mathematical approach, the functional programming and the lazy evaluation intrigue me. I tend to have no big difficulties in picking up new programming languages, but with haskell this is a different sroty. Also I have read already quite a lot about haskell, there is still a lot of concepts I do not yet understand. Or when I understand them in principle I have no Idea how they would be implemented.</p>
<p>Now it seems that these would be reasons to abandon haskell, and focus on another language that is more comfortable to learn (javascript for example). But I like the challenge and I feel like there will be a big benefit from learning haskell. What I have already seen (and understood) from haskell I like.</p>
<p>There is a lot of opinions and explanations of why one should use haskell in the internet. Just google <a href="www.google.com/search?q=why+haskell">Why Haskell</a> or look here: <a href="https://www.haskell.org/haskellwiki/Why_Haskell_matters">Why Haskell matters</a></p>
<h1 id="the-intention-of-the-blog">The intention of the blog</h1>
<p>Because I am not an experienced haskell programmer, this blog will not present much of my own ideas (at least not in the beginning). Instead I will document my experience with the attempt to write JavaScript games in haskell.</p>
<p>All of the information (or at least most if it) I found in the internet or by asking people mailing-lists. I will try to always mark the places where I gather my information.</p>
<p>The intent of this blog will be to make it easier for other with the same goal to get started with javascript games written in haskell. It is an attempt of me to give back to the community. I will try to make the posts very tutorial style. Sometimes I will redirect to other pages when I find it unnecessary to repeat the information here that can be found there.</p>
<p>Edit1: Rewrote with pandoc</p>
<a rel="license" href="https://creativecommons.org/licenses/by/3.0/de/deed.en_US"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/3.0/de/88x31.png" /></a><br /><span xmlns:dct="https://purl.org/dc/terms/" href="https://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">Writing JavaScript games in Haskell</span> by <a xmlns:cc="https://creativecommons.org/ns#" href="https://jshaskell.blogspot.de/" property="cc:attributionName" rel="cc:attributionURL">Nathan Hüsken</a> is licensed under a <a rel="license" href="https://creativecommons.org/licenses/by/3.0/de/deed.en_US">Creative Commons Attribution 3.0 Germany License</a>. Nathan Hüsken https://www.blogger.com/profile/09614845657227846437 noreply@blogger.com 2