我需要在不同范围内的多个随机数(可以通过将随机数生成器的结果乘以一个常数来避免)。
我正在Haskell中制作一个小行星克隆,我想产生随机产生的敌人。我想让它们以相同的速度在屏幕边缘生成(所以速度矢量的范数相等)。
其他信息:当实体撞击屏幕边缘时,它再次出现在另一侧,这就是为什么我选择只让敌人在屏幕四个边缘中的两个边缘产生,并让速度真正决定您首先在哪里看到它们出现。
我看着System.Random
遇到了困难。我认为我需要五个随机数:
在游戏开始时,我会生成一个新的StdGen,然后每帧进行一次。
我想到但认为是不好的解决方案:不用每次通过方法传递生成器,而是每次使用newStdGen
创建一个新的生成器。
我还考虑过为需要的所有随机数调用newRand函数。但是,如果我有一个函数需要在相同范围内的两个随机数,则这两个随机数将是相同的,因为相同的输入在Haskell中总是给出相同的输出。
问题:除了对newRand函数(也用于更新每帧生成器)的调用之外,所有随机调用中的类型变量都不明确,因为Haskell不知道要使用哪种数字类型。
示例错误:
src\Controller.hs:45:56: error:
* Ambiguous type variable `a0' arising from the literal `0.0'
prevents the constraint `(Fractional a0)' from being solved.
Relevant bindings include
axis :: a0 (bound at src\Controller.hs:45:25)
Probable fix: use a type annotation to specify what `a0' should be.
These potential instances exist:
instance HasResolution a => Fractional (Fixed a)
-- Defined in `Data.Fixed'
instance Fractional Double -- Defined in `GHC.Float'
instance Fractional Float -- Defined in `GHC.Float'
...plus three instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
* In the expression: 0.0
In the first argument of `randomR', namely `(0.0, 1.0)'
In the second argument of `($)', namely `randomR (0.0, 1.0) gen'
|
45 | axis = signum $ fst $ randomR (0.0, 1.0) gen
| ^^^
我的代码:
newRand :: StdGen -> (Float, Float) -> (Float, StdGen)
newRand gen (a, b) = randomR (a, b) gen
genEnemies :: StdGen -> Float -> [Moving Enemy]
genEnemies gen time | rand > 995 = [Moving (x, y) (vx, vy) defaultEnemy]
| otherwise = []
where rand = fst $ newRand (0, 1000) gen
x | axis < 0.5 = fst $ randomR (0.0, width) gen
| otherwise = 0
y | axis >= 0.5 = fst $ randomR (0.0, height) gen
| otherwise = 0
axis = signum $ fst $ randomR (0.0, 1.0) gen
vx = fst $ randomR (-20.0, 20.0) gen
vy = sgn * sqrt (400 - vx*vx)
sgn = (signum $ fst $ randomR (-1.0, 1.0) gen)
答案 0 :(得分:3)
想要在Haskell中生成多个随机数的通常模式如下:
foo :: StdGen -> (StdGen, (Int, Int, Int))
foo g0 = let
(val1, g1) = randomR (0, 10) g0
(val2, g2) = randomR (0, 10) g1
(val3, g3) = randomR (0, 10) g2
in (g3, (val1, val2, val3))
例如,在ghci中:
System.Random> foo (mkStdGen 0)
(1346387765 2103410263,(7,10,2))
您会看到它返回了三个不同的数字,这与您每次在代码中用randomR
调用g0
时得到的数字不同。
希望您能抓住这种模式:以所需的范围和randomR
调用StdGen
;使用返回的值作为随机数,并在下次调用StdGen
时使用返回的randomR
作为输入。从您的随机函数返回更新后的StdGen
也很重要,这样您就可以在以后的调用中继续使用该模式。
稍后,您可以查看monad,尤其是RandT
,它可以抽象出将最新更新的StdGen
馈入下一次对randomR
的调用的过程。 RandT
样式的示例如下:
foo' :: MonadRandom m => m (Int, Int, Int)
foo' = do
val1 <- getRandomR (0, 10)
val2 <- getRandomR (0, 10)
val3 <- getRandomR (0, 10)
return (val1, val2, val3)
...但是现在,坚持基础知识。一旦您完全理解它们,当您实现(或重用)允许您执行此类操作的抽象时,魔术的感觉就会大大减少。