2016-10-28 8 views
1

Используя Haskell's Gloss library, я пытаюсь имитировать звездное поле. Визуальный аспект (рисование «звезд» с различными скоростями и размерами на экране) работает. Однако по какой-то причине звезды не распределяются случайным образом, что приводит к моделированию, которое имеет шаблон. У меня также есть эта проблема с симуляцией взрыва, но для простоты я оставлю это на время. Это упрощенная версия моего кода до сих пор:Haskell Gloss: случайное звездное поле не случайное?

type Position  = (Float, Float) 
type Velocity  = (Float, Float) 
type Size   = Float 
type Speed   = Float 
type Drag   = Float 
type Life   = Int 

type Particle  = (Position, Velocity, Speed, Drag, Life, Size) 

-- timeHandler is called every frame by the gloss ‘Play’ function. It's being passed 
-- the delta time and the world it needs to update. 
timeHandler dt world = world {rndGen = mkStdGen (timer world), 
           timer = (timer world) + 1, 
           stars = spawnParticle world (rndGen world) : updateParticles (stars world) dt world} 

randomFloat :: StdGen -> Float -> Float -> Float 
randomFloat rand min max = fst $ randomR (min, max) rand 

spawnParticle :: World -> StdGen -> Particle 
spawnParticle world gen = ((pos, (1 * speed, 0), speed, 1, 0, size), snd (split gen)) 
where pos = (px', py') 
     px' = randomFloat gen (-600) (-500) 
     py' = randomFloat gen (-250) 250 
     speed = size * (randomFloat gen 100 300) -- the smaller a particle, the slower 
     size = randomFloat gen 0.1 1.3 

updateParticles :: [Particle] -> Float -> World -> [Particle] 
updateParticles []  _ _  = [] 
updateParticles (x:xs) dt world | fst(posPart x) > 500 = updateParticles xs dt world 
           | otherwise   = updatedPart : updateParticles xs dt world 
    where pos'  = updateParticlePosition dt x world 
      updatedPart = (pos', velPart x, speedPart x, 1, 0, sizePart x) 

Примечание: velPart, speedPart и т.д. являются функциями, чтобы получить свойство из данной частицы. Опять же, рисунок работает отлично, поэтому я оставлю этот код. updateParticlePosition просто добавляет скорость к текущему положению звезды.

Я думаю, что проблема связана с тем, что мои случайные генераторы не прошли должным образом, но я слишком смущен, чтобы придумать решение ... Любая помощь очень ценится!

+0

Помните, что все функции в Haskell являются * чистый *. Имея это в виду, как ожидается, что «randomFloat :: StdGen -> Float -> Float -> Float' даст разные результаты от вызова? – gspr

+0

Я думал, что он должен давать разные результаты, если ему передается уникальный генератор для каждого вызова. По крайней мере, это то, что я пытаюсь сделать. – Felix

+1

Отбрасывает новое состояние генератора, отбрасывая вторую половину значения, возвращаемого из 'randomR'. – gspr

ответ

2

Это не связано с блеском, просто с семантикой чистого случайного числа. Повторная инициализация нового генератора с новым семенем каждый раз делает не дает вам псевдослучайные числа, потому что случайность возникает только из-за того, что новый генератор , созданный, например, randomR или split будут иметь семена, очень отличающиеся от оригинала. У вас просто есть map mkStdGen [0..], так как вы просто добавляете 1 в timer на каждом шагу.

Рассмотрим разницу в следующих дистрибутивах:

map (fst . randomR (1,1000) . mkStdGen) [0..1000] 
take 1000 $ randomRs (1,1000) (mkStdGen 123) 

Первое, что вы делаете, а второй является собственно случайных чисел.

Решение простое, просто использовать split внутри функции обновления времени (у вас уже есть внутри spawnParticle):

timeHandler dt world = 
    let (newRndGen, g) = split (rndGen world) in 
    world { rndGen = newRndGen 
     , timer = (timer world) + 1 , 
     , stars = spawnParticle world g : updateParticles (stars world) dt world 
     } 

Обратите внимание, что в настоящее время timer не используется, и это, вероятно, имеет смысл имеют timer = timer world + dt в любом случае (дельта времени может быть не совсем одинаковой).

Также имейте в виду, вы не должны повторно использовать генераторы, так что если у вас есть много локальных функций, которые принимают генератор в качестве параметра, вы можете что-то вроде:

timeHandler dt world = 
    let (newRndGen:g0:g1:g2:_) = unfoldr (Just . split) (rndGen world) in 
    world { rndGen = newRndGen 
     , timer = (timer world) + 1 , 
     , stars = spawnParticle world g0 : updateParticles (stars world) dt world 
     , stuff0 = randomStuff0 g1 , stuff1 = randomStuff1 g2 } 
+0

Спасибо за ваш ответ! Однако имеет смысл, что звездное поле по-прежнему не является случайным. Примечательно, что уменьшение размера частиц/скорости, от верхней части экрана до нижней ... Это потому, что я повторно использую 'gen' в моей функции' spawnParticle'? – Felix

+0

Я должен был сначала попробовать, прежде чем спрашивать: я применил ту же технику в 'spawnParticle', как вы использовали в' timeHandler', и теперь она работает! – Felix

 Смежные вопросы

  • Нет связанных вопросов^_^