Мне нужно несколько случайных чисел в разных диапазонах (этого можно избежать, умножив результат генератора случайных чисел на константу).
Я делаю клон Астероидов в Haskell, и я хочугенерировать случайно порожденных врагов.Я хочу, чтобы они появлялись на краю экрана с той же скоростью (чтобы норма вектора скорости была равна).
Дополнительная информация: Когда объект попадает на край экрана, он снова появляется на другой сторонеИменно поэтому я решил позволить врагам появляться только на двух из четырех краев экрана и заставил скорость действительно определять, где вы впервые видите их появление.
Я посмотрел на System.Random
и столкнулся с трудностями.Я подумал, что мне нужно пять случайных чисел:
- Случайное число, чтобы определить, должен ли монстр появиться в этом кадре.
- Случайное положение x или случайное положение y (появляется на краю
- Случайное число, чтобы определить, на какой стороне экрана появляется враг
- Случайное число для скорости x
- Случайное число (1 или -1) для создания вектора скорости сустановить норму.
В начале игры я генерирую новый StdGen, и после этого я делаю это каждый кадр.
Решения, о которых я думал, но считал их плохой практикой: вместопередавая генераторы через методы, каждый раз создавая новый генератор, используя newStdGen
.
Я также думал о вызове функции newRand для всех нужных мне случайных чисел, но если бы у меня была функция, которая требовала двух случайных чиселв одном и том же диапазоне два случайных числа будут идентичны, поскольку идентичный ввод всегда дает одинаковый вывод в Haskell.
Проблема: Неоднозначный тип varможно использовать для всех случайных вызовов, кроме вызова функции 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)