imHo RSSフィード

2008-09-20

乱数

Haskell で例えばサイコロの目を乱数で作りたいときは getStdRandom を使って

rollDice :: IO Int
rollDice = getStdRandom (randomR (1,6))

とすれば乱数が得られる:

main = do
	rollDice >>= print
	rollDice >>= print

だけどこれってすごく不思議。乱数の種を持ちまわらなくていいの?どうやってんの?

getStdRandom の型は

getStdRandom :: (StdGen -> (a, StdGen)) -> IO a

で、使う StdGen は内部で管理しててそいつが現在のシードとかの状態を持っているんだと思う。

Uses the supplied function to get a value from the current global random generator, and updates the global generator with the new generator returned by the function.

getStdRandom

これと同様のことが Haskell 内でできるんだろうか。 GLUTのキー入力処理 - imHo の通りできないと思ってるんだけど。

光が丘公園

404 Error - FC2ブログ に勧められて、光が丘公園にチャリで行ってきた。

噂に違わず、広々してすばらしい公園でした。バードサンクチュアリでは望遠鏡で鳥がはっきり観察できました。

GLUTのキー入力処理

Haskellでゲームを作れ - imHoにたくさんブクマが付いててビビッた。元記事がキャッチーだったからだろうなぁと思う。特にバンナム大森氏がデバッグデバッグいってるのがおもしろい。

なんにせよこれは頑張らないといけません。C++駆逐のためにも、ゲーム業界の未来のためにも。

Haskell の GLUT でも、キーイベントの関数を登録して何か押されたら処理するようになってる:

import Graphics.UI.GLUT

main = do
	...
	--キーボードやマウスのコールバック
	keyboardMouseCallback $= Just keyboardProc
	...

Haskell の IORef というモナドは値を変更できるので、これを使って現在押されているキーをリストに入れておく:

import Data.IORef
import Data.List (union, delete)

pressedRef = newIORef []  -- 押されてるキーを保持するリスト

keyboardProc key state _ _ = do
	pressed <- pressedRef
	case state of
		-- 押されたら追加
		Down -> modifyIORef pressed (union [key])
		-- 離されたら削除
		Up   -> modifyIORef pressed (delete key)

そしてタイマ割り込みのアップデート処理時に状態を取得して使おうと:

--タイマの間隔
timerInterval = 25	-- 1000 / 40

main = do
	...
	--タイマを作る
	addTimerCallback timerInterval timerProc
	...

getPressedKeys = do
	pressed <- pressedRef
	return $ readIORef pressed

timerProc = do
	pressed <- getPressedKeys
	-- 押されてるキーを見てあれこれ

としたが、これだとうまくいかないことが判明。「入門Haskell」の IORef の説明によれば

値の変化は1つのIO処理のなかでのみ有効

入門Haskell%28p.124%29

だそうなので、pressedRef を地に置いておくことはできない。おとなしく main の中に入れるしかない:

import Graphics.UI.GLUT hiding (Red, Green, Blue, rotate)
import Data.IORef
import Data.List (union, delete)

main = do
	pressedRef <- newIORef []

	-- GLUTの初期化
	...

	keyboardMouseCallback $= Just (keyboardProc pressedRef)
	addTimerCallback timerInterval $ timerProc pressedRef

keyboardProc pressedRef key state _ _ = do
	case state of
		-- 押されたら追加
		Down -> modifyIORef pressedRef (union [key])
		-- 離されたら削除
		Up   -> modifyIORef pressedRef (delete key)

timerProc pressedRef = do
	pressed <- readIORef pressedRef
	...

IORef で値を書き換えられるにしても、グローバル変数みたいには取っておけなくて、IORef を持ちまわらないといけないって事ですね。メンドイ。