1

I am working on an Asteroid game made in Haskell with Graphics.gloss. Now I have defined a datatype for the asteroids like this:

data Asteroid = Asteroid {   asteroidPos:: Point, 
                             asteroidVel   :: Vector,
                             asteroidSize  :: Float }

So that it has a Position defined by a point, a velocity defined by a vector and it's size. Now I want to know how I could write an instance of Random for this datatype so that a new asteroid appears at a random time, on a random place with a random velocity. Does anyone know how I can accomplish this?

chi
  • 111,837
  • 3
  • 133
  • 218
  • 2
    [related](http://hackage.haskell.org/package/QuickCheck-2.10.1/docs/Test-QuickCheck-Arbitrary.html#v:arbitrary). – Willem Van Onsem Nov 06 '17 at 09:21
  • 1
    Did you try to write the instance? What issue did you find? – chi Nov 06 '17 at 11:26
  • Have you had a look at [random: System.Random](https://hackage.haskell.org/package/random-1.1/docs/System-Random.html#g:4)? Can you show your efforts in implementing the `Random` interface for your `Asteroid` data-type. – epsilonhalbe Nov 06 '17 at 11:58
  • 1
    “At a random time” can't very well be expressed as a `Random` instance. Are you writing your game in a Functional Reactive Programming style, or with a traditional “imperative loop”? In the former case, you could just generate an ordered list of equal-distributed entering-time values. Else you'd rather have to pull a _time interval_ out of an exponential distribution whenever an asteroid appears, and then wait for that time before you generate the next. – leftaroundabout Nov 06 '17 at 12:03

1 Answers1

1

There's already a Random instance for Float. Assuming that you also have Random instances for Vector and Point, you can use them to define a Random instance for Asteroid:

instance Random Asteroid where
  randomR (Asteroid pl vl sl, Asteroid ph vh sh) g =
    let (p, g1) = randomR (pl, ph) g
        (v, g2) = randomR (vl, vh) g1
        (s, g3) = randomR (sl, sh) g2
    in (Asteroid p v s, g3)
  random g =
    let (p, g1) = random g
        (v, g2) = random g1
        (s, g3) = random g2
    in (Asteroid p v s, g3)

The randomR function takes a range of values, from minimum (low) to maximum (high). How you define a meaningful range of a complex value like Asteroid, I'll leave up to you; here, I'm simply assuming that you can pass a low and high Asteroid value.

The first step uses the underlying Random instance for Point by calling randomR. p is a randomly generated Point value, and g1 is the next random generator value to use.

Likewise, v is a randomly generated Vector value, generated by the underlying instance for Vector.

Finally, s is a Float generated by randomR for Float.

The return value is a new Asteroid value composed from p, v, and s, plus the most recent generator g3.

The implementation of random follows the same template.

You could write this code in a prettier form, using e.g. using MonadRandom, but for starters, I left the code in its raw form to show how it works.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736