<div>--- BEGIN NOSTALGIA ---</div><div><br></div>Well, I have to add to this, that when I coded my first games in assembler in the eighties, I did exactly the same thing: just recording the input of the joystick was enough to get full replays and make auto playing demos.  But on the old computers, this was all so easy, since you had full control over every bit of the system (it was even easy to count exactly how many cycles a routine would take :-), so it was just a matter of starting the system in the same initial state, which was very easy. <div>
<div><br></div></div><div>As Ryan says, this was a blessing for debugging; after a bug was found (but of course when writing in assembler one did not make bugs ;-), the testers just returned a small log file of the input and version number, and this allowed reproducing the problem *before* the bug occurred (aka known as reverse debugging?). </div>
<div><br></div><div>Also since the mutable part of the game was just a couple of kilobytes (most of the memory was used for immutable graphics, sounds and code), taking a snapshot at regular intervals and embedding this into the log-file made it easy to quickly go backwards and forwards in time. </div>
<div><br></div><div>To me, that was the joy of imperative programming: I could reason about those systems as a whole, they behaved in a very predictable way, and since all home computers (and game console) where basically identical, you knew that if it behaved correctly on your system, it would also work on the millions of other systems out there. Now IMO imperative programming is more like playing Russian roulette... </div>
<div><br></div><div><div>--- END NOSTALGIA ---</div><div><br></div></div><div><div>But I find it so much harder or impossible on modern systems to do this, or as Ryan says, it requires a high discipline from coders...</div>
<div><br></div><div><div><div><div><div><div><div><div>So yes, without using IO, Haskell forces you into this safe spot (there&#39;s of course the unsafePerformIO function as backdoor to break all that :). But we still need to see the first commercial games written in Haskell; hope to see those soon, without using IO everywhere of course :)</div>
<div><br></div><div>Actually, it might be interesting to make a special mailing list for Haskell and games? Or even a broader list for applying FP to games and simulations? Or maybe that already exists?</div><div><br></div>
<div>On Sun, Oct 4, 2009 at 7:16 PM, Ryan Ingram <span dir="ltr">&lt;<a href="mailto:ryani.spam@gmail.com">ryani.spam@gmail.com</a>&gt;</span> wrote:</div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>
<div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">And, to go further, once you embrace &quot;determinism&quot; in your randomness, you can do all sorts of really cool things.<br>
<br>From the perspective of a games programmer:<br><br>You can run the same simulation code on two different network nodes, and reliably get the same result, allowing you to just transfer user inputs between the nodes instead of game state.  This has applications in reducing latency as well, as you only need to transfer the input one way across the network.<br>

<br>You can save off the user inputs and initial into a tiny &quot;replay&quot; buffer, allowing you to re-run the game from the beginning without much memory cost.  This is not only a cool end-user feature, but it aids *tremendously* in debugging.  When something goes wrong, you can always just rewind as many times as you want while you narrow down the cause of the problem.<br>

<br>However, we always had problems with determinism failures, where somebody would use the wrong random-number generator, or forget that they aren&#39;t allowed to have the simulation depend on something that came from the graphics RNG.  In Haskell you can encode the purity of the simulation into its type and it won&#39;t break!<br>
<font color="#888888">
<br>  -- ryan</font><div><div></div><div class="h5"><br><br><div class="gmail_quote">On Sun, Oct 4, 2009 at 6:20 AM, Duncan Coutts <span dir="ltr">&lt;<a href="mailto:duncan.coutts@googlemail.com" target="_blank">duncan.coutts@googlemail.com</a>&gt;</span> wrote:<br>
<blockquote class="gmail_quote" style="border-left:1px solid rgb(204, 204, 204);margin:0pt 0pt 0pt 0.8ex;padding-left:1ex">
On Sun, 2009-10-04 at 05:11 -0700, Michael Mossey wrote:<br>
<div>&gt; Duncan Coutts wrote:<br>
<br>
&gt; &gt; Others have already answered but I&#39;d like to suggest that you avoid<br>
&gt; &gt; using IO here. There&#39;s no need for this to be impure.<br>
<br>
</div><div>&gt; Can you point me to a tutorial that covers the basics of randomness in<br>
&gt; Hasell? I find it very confusing.<br>
<br>
<br>
</div><a href="http://en.wikibooks.org/wiki/Haskell/Hierarchical_libraries/Randoms" target="_blank">http://en.wikibooks.org/wiki/Haskell/Hierarchical_libraries/Randoms</a><br>
<br>
<a href="http://learnyouahaskell.com/input-and-output#randomness" target="_blank">http://learnyouahaskell.com/input-and-output#randomness</a><br>
<br>
<br>
The main thing to realise is that random number generators are pure and<br>
predictable. Given the state of a random number generator, if you ask<br>
for a random number, it always gives the same answer. It has to, because<br>
it is pure.<br>
<br>
Let&#39;s make one, and seed it with the starting state 12345<br>
<br>
ghci&gt; :module System.Random<br>
ghci&gt; let g0 = mkStdGen 12345<br>
<br>
Now we can ask for the next random number in the sequence:<br>
<br>
ghci&gt; let (n1, g1) = next g0<br>
ghci&gt; n1<br>
493972152<br>
<br>
Now of course if we asked for the random number from g0 again then we<br>
must get the same result. But notice that when we use &#39;next&#39; it also<br>
gives us back g1 which is the next state of the random number generator.<br>
<br>
ghci&gt; let (n2, g2) = next g1<br>
ghci&gt; n2<br>
335387100<br>
<br>
So this is the basic way that random number generators work in a pure<br>
language. The generator has to be passed around the pure function, for<br>
example from one recursion to the next.<br>
<br>
So you end up with pure functions like:<br>
<br>
shuffle :: RandomGen g =&gt; g -&gt; [x] -&gt; [x]<br>
<br>
Another approach is to hide the &#39;g&#39; inside a monad. That&#39;s what<br>
MonadRandom is all about. eg:<br>
<br>
shuffle :: [x] -&gt; Rand [x]<br>
<br>
The tutorials above explain about the other random functions, for<br>
getting values of different types (not just Int) and restricted ranges<br>
of number etc.<br>
<br>
Of course at some point you want to seed the random number generator<br>
with some initial genuinely random value (not like the 12345 we used<br>
above). That is the only place in your random-handling code that needs<br>
to do IO. All the rest of it can be pure.<br>
<font color="#888888"><br>
Duncan<br>
</font><div><div></div><div><br>
_______________________________________________<br>
Haskell-Cafe mailing list<br>
<a href="mailto:Haskell-Cafe@haskell.org" target="_blank">Haskell-Cafe@haskell.org</a><br>
<a href="http://www.haskell.org/mailman/listinfo/haskell-cafe" target="_blank">http://www.haskell.org/mailman/listinfo/haskell-cafe</a><br>
</div></div></blockquote></div><br>
</div></div><br>_______________________________________________<br>
Haskell-Cafe mailing list<br>
<a href="mailto:Haskell-Cafe@haskell.org">Haskell-Cafe@haskell.org</a><br>
<a href="http://www.haskell.org/mailman/listinfo/haskell-cafe" target="_blank">http://www.haskell.org/mailman/listinfo/haskell-cafe</a><br>
<br></blockquote></div><br></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div>