Just $ A sandbox

プログラミングとかPCとかの技術的なメモ

Printf実装を通して学ぶGADTs, DataKinds, ConstraintKinds, TypeFamilies

問題

問. Haskellで以下のようなCライクなprintf函数を実装してください。

> printf ["Hello, ", _s, "\n", "an integer:", _d, "\n", "a float:", _f] ["World!", (10 :: Int), (3.1415 :: Float)] 

-- 出力結果:
> Hello, World!
> an integer:10
> a float:3.1415

Cのprinfとは異なり、%dや%fが文字列に直接埋め込まれていません。よって当然型が合わなければ、すなわち printf [_d] [(3.1 :: Float)] などとかくとコンパイルエラーになってほしいわけです。

解答

私が実装したものが以下にあります。

https://gist.github.com/myuon/9084939

以下ではこのコードについて解説していきたいと思います。

解説

まず、printfの第一引数も第二引数もそれぞれの元の型はバラバラです。今はまだ_d_fの型をどうするかについては考えていませんが、とにかく型が違うということだけ分かればまずはヘテロリストの実装からスタートすることになります。

GADTs, DataKinds

ヘテロリストは全ての型がバラバラなので型を保持するのではなくてカインドを保持するようにします。つまり、普通のリストは型がaのデータを並べたものですが、今回実装するヘテロリストはカインドが*のデータを並べたものと考えます。

data HList (as :: [*]) where
  Nil :: HList '[]
  (:::) :: a -> HList as -> HList (a ': as)
infixr :::

HListの定義にはGADTとDataKindsが使われています。GADTはデータコンストラクタ函数のように定義できる*1もので、確かに上ではdata ... whereという書き方がされています。

DataKindsは定義したデータコンストラクタを型に、型をカインドに同じ名前のまま持ち上げるものです。分かりやすい例を挙げてみましょう。以下は簡単な型レベル自然数の例です。

data Nat = Zero | Succ Nat

-- DataKinds拡張を使うと、以下のようなものも自動で生成される
-- data Zero
-- data Succ (n :: Nat)
-- ただし Zero :: Nat, Succ n :: Nat である

これによって、標準的なリスト型は[]カインドに持ち上げられ、'[], (':)というデータコンストラクタを昇格してできた型が作られ、*カインドをもつ型のリストが使えるようになります。 このことを踏まえれば上のコードは、HListというデータ型の定義であって、asという変数はカインド*をもつ型のリストになっていることが分かります。(:::) :: a -> HList as -> HList (a ': as)によって、与えられた型をそのままリストに追加しているのが分かります。さらに実際に動かして型を調べてみるとよいでしょう。

> :t Nil
Nil :: HList ('[] *)

> :t "100" ::: (10 :: Int) ::: Nil
"100" ::: (10 :: Int) ::: Nil :: HList ((':) * [Char] ((':) * Int ('[] *)))

TypeFamilies, ConstraintKinds

さて、HListShowインスタンスにできるでしょうか。HListに登場する全ての型がShowインスタンスであればできそうです。このように型への制限を表すのがConstraintで、それをカインドのレベルで扱えるようにするのがConstraintKinds拡張です。

import GHC.Prim (Constraint)

type family All (cx :: * -> Constraint) (xs :: [*]) :: Constraint
type instance All cx '[] = ()
type instance All cx (x ': xs) = (cx x, All cx xs)

TypeFamiliesを使えば型を直接扱う函数を定義できます。これによってAllという型への制限を表す函数を定義します。cx :: * -> Constraintの部分に適当な型クラスがきます。例えばAll Showは、カインド*のリストxsに対し、その全ての元がShowインスタンスになっているという制限を表すことができるわけです。

コレを使ってHListShowインスタンスにしてしまいます。

instance (All Show as) => Show (HList as) where
  show Nil = "[]"
  show (x ::: xs) = show x ++ ":" ++ show xs

-- 実行例
-- > "100" ::: 4.2 ::: 10 ::: [1..5] ::: Nil
-- "100":4.2:10:[1,2,3,4,5]:[]

TextFの実装

以上でヘテロリストが実装できました。これを使えば我々の目的であるprintf函数の型も大体見当がつくでしょう。

printf :: HList as -> HList bs -> IO ()
printf x y = undefined

printfは2つのHListxとyを受け取って、もしもxの先頭がString型であれば、それをそのまま出力します。そうでなければ、xの先頭とyの先頭の型があっているか(xの先頭が_dだったらyの先頭はInt型であるか)を判断して、合っていればそれを出力します。このようにprintfは型によって実装が変わるので、型クラスの出番です。

class TextF as bs where
  textf :: HList as -> HList bs -> String

また、printfはIO ()になって少し扱いにくいので、textf :: HList as -> HList bs -> Stringという純粋函数を定義して、それを使ってprintfを実装することにしました。

いよいよ実装です。まずは、それぞれのリストas, bsがともに空である場合。

instance TextF '[] '[] where
  textf _ _ = ""

そして、asの先頭がString型かどうかでパターンマッチを行います。

instance (TextF as bs) => TextF (String ': as) bs where
  textf (x ::: xs) y' = x ++ textf xs y'
 
instance (TextF as bs) => TextF ((x -> String) ': as) (x ': bs) where
  textf (x ::: xs) (y ::: ys) = x y ++ textf xs ys

asString型のデータか、x -> String型の函数が入っているということにしました。つまり、_d_f_d :: Int -> String, _f :: Float -> Stringの型であって、さらに_d 10_f 3.1415などの値が画面に出力されることになります。 よって、このような_d_fshowそのものですから、これを定義してあげることにします。

_d :: Int -> String
_d = show
 
_s :: String -> String
_s = id
 
_f :: Float -> String
_f = show

(String -> Stringだけは、そのままshowするとクォーテーションがついてくるのでそのままにしてあります。)

まとめ

以上で、printfの実装が完全に出来ました。 では実際に動かして遊んでみましょう。

> printf Nil Nil

> printf ("hello!" ::: _s ::: Nil) ("world!" ::: Nil)
hello!world!
> printf ("hoge:" ::: _d ::: Nil) (3.1415 ::: Nil)
*** 型エラー ***
> printf ("hoge:" ::: _d ::: Nil) ((20 :: Int) ::: Nil)
hoge:20
> printf ("hoge:" ::: 10 ::: Nil) (Nil)
*** 型エラー ***

はい、正しく動いているようです。

なお、問ではこれを printf ["hello","world!"] [] のようにリストカッコを用いて書くように言っていましたが、そのようなリストの表記を使えるようにするGHC拡張として、OverloadedListsが提案されているようです。 また、このprintfをCと同じように可変長引数にすることは頑張ればできると思うのでよければやってみてもいいかもしれません。(丸投げ)*2

GADTs, DataKinds, ConstraintKinds, TypeFamiliesなどのGHC拡張を使えば、このような型レベルプログラミングも比較的[要出典]簡単に実装することができます。便利なGHC拡張はドンドン使っていきましょう。

参考

参考にしたページと参考になりそうなページ

*1:各データコンストラクタの像を適当な形に制限するために使うものだと思っていますが正確なところは知りません

*2:型クラスを用いた可変長引数の実装はText.Printfが分かりやすいと思います。

Haskellでもできる!実践・オブジェクト指向

Lensにほとんど触れたことのない人にはこちらの記事がオススメです:Lensで行こう! - Just $ A sandbox

Haskellでもオブジェクト指向をしましょう!
Haskellは直接オブジェクト指向的な機能を提供してはいませんが、我らがLensの力を借りることでオブジェクト指向的な設計を意識したコーディングが可能です。

今回利用するのは主に以下のモジュールです。

Lensのおさらい

Lensを使ったことのある人にはおなじみだと思いますので、特に解説はしません。

type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t  -- Lens型(GetterやSetterの一般形)
type Lens' s a = Lens s s a a  -- Lensの省略形

(^.) :: s -> Getting a s a -> a  -- 値をgetする
(.~) :: ASetter s t a b -> b -> s -> t  -- 値をsetする
(%~) :: Profunctor p => Setting p s t a b -> p a b -> s -> t  -- 函数を適用したものをsetする
(.=) :: MonadState s m => ASetter s s a b -> b -> m () -- 値をMonadStateの文脈でsetする( (.~)のMonadState版 )
(%=) :: (Profunctor p, MonadState s m) => Setting p s s a b -> p a b -> m () -- 函数を適用したものをMonadStateの文脈でsetする( (%~)のMonadState版 )
(<~) :: MonadState s m => ASetter s s a b -> m b -> m ()  -- アクションを実行した結果をsetする

lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b  -- getterとsetterからLensをbuildする

特に最後のlens函数は重要です。例えば適当なdata型をmakeLensesするとき、何が起こっているかを明示すると

data Hoge a b = Hoge { _foo :: a, _baz :: b }
makeLenses ''Hoge

-- 以下が生成される
foo :: Lens' (Hoge a b) a
foo = lens _foo (\h x -> h { _foo = x })

baz :: Lens' (Hoge a b) b
baz = lens _foo (\h x -> h { _baz = x })

となります。 lensに、getter :: Lens' s a -> asetter :: Lens' s a -> a -> Lens' s aを渡すことで、出来上がるlens getter setterは上手くLensとして振る舞ってくれます。

型クラスとオブジェクト

Lensでオブジェクトを定義しましょう。
例として、ブロック崩しゲームのようなものを考えてみましょう。ブロック崩しには当然、ボールやバーが必要ですのでこれを定義してみます。

data Ball = Ball { _posBall :: (Int, Int), _velBall :: (Int, Int), _r :: Int } deriving (Eq, Show)
makeLenses ''Ball

data Bar = Bar { _posBar :: (Int, Int), _velBar :: (Int, Int), _width :: Int, _height :: Int } deriving (Eq, Show)
makeLenses ''Bar

posHogeはオブジェクトの座標にあたります。わざわざ名前を変えているのは、函数の多重定義になってしまうからです。また、オブジェクトは座標を更新して動かしたいので、このようなメソッドを定義します。
Haskellでは、いわゆるメソッドに当たる部分は型クラスのメソッドとして定義します。メソッドはオブジェクトに関する値を戻り値にとる函数というよりもオブジェクト自体を操作する函数であることが多いのでデータ型とは分けて定義するほうが便利です。また、型クラスのメソッドなら、同じ名前でもデータ型によって異なる振る舞いが定義できます。

class Move c where
  update :: State c ()

instance Move Ball where
  update = do
    (vx,vy) <- use velBall
    posBall %= (\(x,y) -> (x+vx, y+vy))

instance Move Bar where
  -- Barは横にしか動けない
  update = do
    (vx,_) <- use velBall
    posBar %= (\(x,y) -> (x+vx,y))

特に意味はないですが、オブジェクトによって函数の振る舞いを変えることが出来ました。
さて、posBallposBarが同じ座標を表しているのに名前が違うのは何かと面倒なので、これも型クラスを使ってまとめてしまいます。

class HasPos c where
  pos :: Lens' c (Int, Int)

instance HasPos Ball where
  pos = posBall
instance HasPos Bar where
  pos = posBar

はい、これでposBallposBarを区別する必要はありません。これを使えば、posをもつ任意の要素に対するメソッドが定義できます。

reset :: (HasPos c) => State c ()
reset = do
  pos .= (0,0)

resetHasPos型クラスのインスタンスならなんでも適用できます。つまり、BallBarはどちらもresetで操作をすることができるわけです。便利ですね!

Classyとサブクラス

さて、上ではHasPosを定義して名前の衝突を防ぎましたが、これを共通する全ての函数について行わなければならないのは面倒です。そこで、posvelなどの共通するメンバをもつ抽象クラスを作り、BallBarはこれらを内部に含む(ある意味ではサブクラス・継承したクラスのようなもの)クラスとして定義することにしましょう。

data GameObj = GameObj { _pos :: (Int, Int), _vel :: (Int, Int) }
makeClassy ''GameObj

-- 以下が自動生成される
class HasGameObj c where
  gameObj :: Lens' c GameObj

instance HasGameObj GameObj where
  gameObj = id

pos :: (HasGameObj c) => Lens' c (Int, Int)
vel :: (HasGameObj c) => Lens' c (Int, Int)

makeClassyGameObjからHasGameObj型クラスを生成します。これによって、HasGameObjインスタンスになったものは自由にposvelを使えるようになります。
どういうことか見てみましょう。

data Ball = Ball { _ballObj :: GameObj, _r :: Int } deriving (Eq, Show)
makeLenses ''Ball

instance HasGameObj Ball where
  gameObj = ballObj

これでBallHasGameObjインスタンスになったので、b :: Ballに対してb ^. posとかb & vel .~ (1,0)などとかけるようになったわけです。

また、少しデザインパターン的ですが、自分自身を更新する函数を内部に持つオブジェクトの定義も出来ます。
同じ型を持つオブジェクトの実装を変えられるようにしたい -qiita

これは、例えば変な動きをするボールを作りたいときに、ボールの動きをデータの生成時に指定したいみたいな状況で使えると思います。
(上の記事のAutonomieの実装を参照。autoを例えばBall型で、runAutoState Ball ()とすればこのような実装も可能。)

ここまででかなりのことはできるようになったと思います。
今回は記事タイトルの通り、「実践」を意識した解説を行いました。例えば私はChimeraという弾幕シューティングゲームを全てHaskellで実装していますが、Lensはこれくらい本格的なプロジェクトになっても通用するレベルの強力さを秘めています。

Lensは(たぶん)他にも使い方があります。あなたもこの強力なライブラリで快適なHaskellコーディングを楽しみましょう!
さあいますぐインストール! *1

*1:2014/02/02時点でlens-4.0がリリースされています。

2ヶ月くらいでノベルゲームを作った話

発端


で2ヶ月くらいでノベルゲーム(正確にはデジタルノベル、あるいはヴィジュアルノベルと呼ばれるものです)を作りました↓*1

チェイン・コンプレックス

ので、今回のプロジェクトについてまとめます。

製作にあたって

必要になったもの

  • ノベルエンジン: 吉里吉里/KAG
  • 企画・シナリオ
  • ノベルエンジン・スクリプト・フォント
  • キャラクター立ち絵
  • 背景(写真を加工→後述)
  • BGM/SE
  • 宣伝するところ
  • (やる気・根気)

企画・シナリオ・キャラクター立ち絵・スクリプトに関しては自分でなんとかして、あとのものは借りてなんとかしました。 借りた素材やらのリンクはリンクページにリンクを貼っておきました。

製作過程

1. 企画・プロット

今回は一人のプロジェクトだったので企画書のようなものは作ってません。プロットを書きながらあんなことをしたいこんなこともしたいでずっとごにょごにょやってました(後で色々変更したけど)。

モチベーション上げるために好きなノベル作品の思い出に浸ったりもしてました。

今回は作品が作品だっただけに、プロットを書くにあたってグラフエディターのようなものが欲しかったので yEd - Graph Editor を使いました。

2. シナリオ

最終的にシナリオは270KBです(お話に関係のあるシナリオファイルのファイルサイズ)。
シナリオは80%かくのに3週間ほどかかってしまって、なかなか大変でした(主に中だるみ)。書いていてつらい、しんどいなどの症状が出たらそれはシナリオがつまらない可能性があるなと思ってプロットを見直したりもしていました。

今回の作品は前半部分が「恋愛パート」、後半部分が「SFパート」の形になるお話だったので、前半後半で気分も切り替えられて良かったと思います(前半はずっと幼馴染み可愛いしか言ってなくて、後半はずっと頭をひねりながら矛盾が出ないようにプロットをいじってました)。

時間もかかったけど楽しかったです。今もそれなりに満足しています。

アウトラインプロセッサと呼ばれるソフトも導入しました。使ったのはこちら→ Olivine Cafe - OlivineEditor

3. 背景

背景は写真素材を頑張って加工しました。
写真素材を使ったのは、そちらの方が圧倒的に素材の数が多かったからです(「〜のシーン背景が欲しい」などの要望がある場合はイラスト素材だと難しいことがあります)。

加工はそれなりに難しいですが、比較的うまくやると以下のような感じになります(写真はhttp://ruta2.fc2web.com/frame.htmlより)。

f:id:myuon_myon:20131201034227p:plain

加工に際しては 写真を絵のように見せる写真加工技術「PhotoDramatica」トップ - あやえも研究所 を大いに参考にしました。

これだけあればそれなりのことはできます。ただし適度に妥協が必要です。

4. キャラクター立ち絵

リリース3日前に描き直したりと色々ありましたがなにより可愛く描けたと思うのでとても気に入ってます。
チェイン・コンプレックスでサンプルが見れます。

ちなみに枚数は、 幼馴染み表情差分16パターン+クラスメート表情差分16パターン+その他2枚 の立ち絵を用意しました。
ただ「笑い」「泣き」みたいに用意していたんですが、より細かく
「笑い(コミカル)」「笑い(爆笑)」
「泣き(コミカル)」「泣き(大泣き)」
のように、それぞれ喜怒哀楽について2パターンくらい用意しておけばなんとかなったのではないかなと思います。

不思議・困惑などの差分も用意しましたが、シーンによっては合っていないようなところもあったので、分かりやすく「笑いA・B」「泣きA・B」のようにした方が立ち絵の選択にも迷いにくかったかなと思っています。

5. スクリプト

システムボタンプラグインは公式で配布されているものを自分で改造しました(テキストボタンが使えるように)。

さて、YesNoDialogLayerスクリプトはデフォルトだとYes/Noダイアログが開くようになっている部分をレイヤーベースのものに書き換えるプラグインです。これが実は厄介で、ダイアログの場合はYes/Noのどちらを押したかが戻り値として返ってきますがこれはレイヤーベースなので表示する瞬間とYes/Noが確定するタイミングはずれます(ダイアログを表示する瞬間は必ずfalseが返される)。
そしてそこの処理とセーブロードプラグインの「読み込みますか→はい/いいえ」の処理が丁度バッティングするので、「Yesを押した時〜する」という処理をキチンとフックしてあげないといけないわけですが、これに気が付かず、ここに起因するバグフィックスにそれなりに時間を取られました。

よってYesNoDialogLayerスクリプトを入れる際には、既存のプラグインがダイアログボックスを使っていないかどうかしっかり吟味してから導入した方がよいと思います…。

6. BGM/SE

SEは特に言うことないです。ググって良さげなものを探して借りました。

BGMは選定に非常に時間がかかるので、予め片っ端から聴いておき、使えそうな曲には

BGM001.ogg: 主人公が幼馴染みと喧嘩してから一夜明け、なんとなく気まずい雰囲気

みたいに、どういうシチュエーションで使える曲かを細かくメモっておけば時間短縮になります。ただ明るい、暗いだけでは大雑把すぎるので曲に対してシーンを作るとしたらどうなるだろう?と考えてメモを作ると意外と曲の選定で迷いません。

あと、50MB程度あるzipのうち半分はBGMが持っていってるので、調子に乗って色々使いすぎないほうがいい気はします。

7. リリース

リリースは個人サイトの一部に専用ページを作りました。Bootstrap を使ったのでほとんど時間もかかっていません。

zipファイルはSimple File Sharing and Storage.ふりーむ! - フリーゲーム/無料ゲーム 5000本!にアップロードしました。
アップローダーとしてMediaFireを選んだのはうるさい広告がなかったことと、ダウンロードする人への制限があまり厳しくなかったからです。ふりーむ!をはじめとしてフリーゲーム紹介サイトなどには積極的に登録しました。

リリースして少ししてから、レビューを書いていただきました。とても嬉しいです!ありがとうございます。

どちらのレビューにもシステムが不安定と言われていて、実際バグがあったわけなんですが、こういうのをレビュー媒介でしか知れないのは色々アレなので掲示板は作っておいたほうがいいなと思いました(現在は掲示板も設置してあります)。

v2.0でシステムバグはそれなりに解消したつもりですが、v1.0公開から少し間が空いてしまったことは本当に申し訳ないと思っています。

まとめ

2ヶ月という短いスパンでの開発でしたが、考えれば無駄もたくさんあり、この程度の規模のゲームなら慣れれば1ヶ月程度でできるのではないかなと思います。

また、開発にあたっては全てUbuntuとWineを使って行いましたが吉里吉里は全く問題なく動いてくれました。Wineはとても優秀です。
(ただし主に使っていたエディター(KKDE)はWineの上だとそこそこ不安定だったのでWindowsを使ったほうが無難だとは思います。また、テストプレイはWindowsで行なうべきです…当たり前ですが。)

12/1現在でDL数も400を超え、当初予想よりも遥かに色んな人にプレイしてもらえたみたいでとても嬉しかったです(主にレビューのおかげです)。
それなりに色々やらかしたりもしていて、シナリオ修正とかバグフィックスとかあれこれ大変なこともありましたけど何より楽しかったのでとてもいい経験になりました。

思っていたよりも簡単に、それらしいものができたので満足しています。もしも次があれば、今回の経験も踏まえてもっとよいものを作りたいと思います。
このまとめがこれからゲームを作る誰かのお役に立てれば幸いです。

*1:ゲームの内容とチェイン複体は何の関係もありません。なんとなく名前をつけただけです。

Ubuntuがdisk driveエラーで起動しない

Xubuntu 13.10で以下のようなエラーが出て起動できませんでした。

The disk drive for /home is not ready yes or not present.

私のHDDのパーティションは以下のような感じになっています。

/dev/sda2 /dev/sda7 /dev/sda5
Windows7 /home /

/homeに当たる/dev/sda7の部分がマウントできていないようだったので、起動時にマウントするディスクを設定します。

The disk drive for /home is not ready yet or not present.
Continue to wait, or Press S to skip mounting or M for manual recovery.

のメッセージでMを押し、コマンドモードに入ります。

~$ blkid
/dev/sda1:...
...
/dev/sda7: UUID=hogehoge...

blkidで起動したいディスクのUUIDを調べてメモします。

~$ cat /etc/fstab

# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
...
/dev/sda7       /home           ext2    defaults            1      2

のようになっているので、この/dev/sda7のところを

UUID=[さっきメモしたUUID] /home           ext2    errors=remount-ro 0       1 

に編集します。

これで起動することができました。めでたし。

参考

How to mount drive in /media/userName/ like nautilus do using udisks - Ask Ubuntu

LinuxでWindowsアプリケーションを動かしたい人のためのWineの設定

この記事はwineに関して私が蓄積したベストプラクティスとバッドノウハウをまとめたものです。

Wineとは

Wineとは、Linux*1Windowsアプリケーションが動くエミュレータではない何か*2です。

はじめに、Wineで全てのWindowsアプリが動くわけではないことを念頭に置いてください。まるで動かないものもあります。
この記事は、色んなアプリが動くようになる可能性が高くなるように設定をしています。

Wineで動かしたいアプリがあるのであれば

に行くと何か情報が得られるかもしれません。

環境

今回使用したのは以下の環境下です。

Wineのインストールにはなるべくパッケージを使っていれてください。
人柱気質たっぷりならソースからビルドでもよいかもしれません。

Wineで使うdllや必要なフレームワークを手軽にインストールしたり管理してくれるものです。必須です。

注: Wineは、こまめにバージョンを上げればよいというものではありません。特に開発段階のものをインストールするときは注意してください。バージョンがあがって今まで動いていたものが動かなくなってしまうこともあります。

設定

以下では具体的にWineの設定をしてみます。どんなアプリを動かしたいかによって入れるものや順番が変わるので注意してください。

フォントの文字化けを直す

デフォルトでは日本語の部分が豆腐状態(□□□□みたいな)だと思うのでまずはフォントをインストールします。

~$ winetricks allfonts

これで必要なフォントは入ります。

フォントを綺麗にする

お好みのフォント(例えばVL Gothic Font Family)のttfを~/.wine/drive_c/windows/Fontsに入れます。
そして~/.wine/user.regを以下のように編集します。

[Software\\Wine\\Fonts\\Replacements] 1240428288
"MS Gothic"="VL Gothic"
"MS PGothic"="VL PGothic"
"MS Sans Serif"="VL PGothic"
"MS Shell Dlg"="VL Gothic"
"MS UI Gothic"="VL PGothic"
"Tahoma"="VL PGothic"
"\xff2d\xff33 \x30b4\x30b7\x30c3\x30af"="VL Gothic"
"\xff2d\xff33 \xff30\x30b4\x30b7\x30c3\x30af"="VL PGothic"

.NET Framework系アプリを動かす

winetricks > Select the default wineprefix > Install a Windows DLL or componentから

などを入れます。

dotnetは(10->)20->30->35の順番でしか入りません。途中でコケたらその後のものは入りませんが、40だけは別に入れることができます。
ie7,8がないと上手く動かないものがあるようです。
msxml, mfc, vcrun, wshなどは必要ないかもしれません(必要があれば勝手に入る)。

メニューのフォントが化けるor綺麗じゃない場合は

~$ winecfg

デスクトップ統合 > 項目のメニューバーのテキストのフォントなどをいじると変えることができます。

ゲームを動かす

  • d3dx*
  • devenum
  • dinput, dirac
  • directmusic, directplay, dmsynth, dsound
  • dxdiag, ffdshow
  • quartz
  • vcrun*
  • wmp*

などを入れます。

d3dxは9,10,11を入れれば大丈夫だと思います。

上手く動かない

汎用

コマンドラインからwineを実行すると、たくさんメッセージが出ると思います。

err:[name]:[message]

となっているものがあれば、[name]のdllがおかしいことが多いです。
fixme:hogehogeは無視して大丈夫です。

~/.wine/drive_c/windows/system/system32の中に指定されたdllを(どうにかして手に入れて*3 )入れるか置換して、winecfgのライブラリからその名前のものを"ネイティブ版"として追加すると上手く行くことがあります。

kernel32.dll, gdi32.dll, user32.dll, ntdll.dll, ole.dllなどは置き換え無いほうがいいかもしれません。

(ゲームなどの)音が鳴らない・割れる

winecfgオーディオのところにドライバがセットされて、音をテストして上手く行くことを確認してください。
また、winetricksで音関係のdllをいれてください(dsoundとかdirectmusicとか)。

音割れが激しい場合はPulseAudioを消して再起動すると直ることがあります(PulseAudio周りのドライバがおかしい場合)。

強制終了する

ゲームであれば、動画の再生の際にコーデックがないなどの理由で強制終了してしまうことがあります。
wmpやその他の動画コーデックをいれてみてください。

詰んだ

以下のようなエラーが出たら詰んでいることが多いです。

  • Unhandled page fault on read accessなどのメモリ関係の強制終了
  • gstreamerのエラー(ディストリ用パッケージを入れると動くかも?)
  • 一部のole関係のエラー
  • 一部のinitialization系エラー(ゲームを動かす要件を満たしてないことが多い)
  • 一部のerr:module:import_dll Libraryなどのエラー(dllをネイティブ版で置き換えると上手く行くことも)
  • 何が悪いのかよく分からないエラー(何が悪いのか分かっていない場合は解決手段も分からないので詰んでる)

詰んだら一からやり直しましょう。
~/.wineをリネームして(削除でも構いませんが残しておくほうがあとあと便利だと思います)、全ての設定をしなおすと上手く行くことがあります。
(動いてるという報告があって)どうしても動かない場合は経験上これが早いです。

まとめ

  • Wineは便利だけど闇
  • 特にゲームは闇
  • winetricksで必要なものを揃えよう
  • エラーメッセージを読もう
  • 割とよく詰む

参考

情報が古いものもあるので気をつけてください。

*1:MacにもWineBottlerという名前で移植されています

*2:Wine Is Not an Emulatorの略

*3:dll-filesなどから探すかそれ以外の方法を考えてください。

Pythonでsuper(self.__class__, self)は使うな

[追記:2013/11/02]
この記事ではPython2.xを前提としていますが、Python3以降はsuperの引数を省略することができるようになっています。
[/追記]

次のPythonコードを見てください。

class A(object):
    def method(self):
        pass
 
class B(A):
    def method(self):
        super(self.__class__, self).method()
 
class C(B):
    def method(self):
        super(self.__class__, self).method()
 
>>> c = C()
>>> c.method()

このコードを実行すると、
RuntimeError: maximum recursion depth exceeded while calling a Python object
すなわち再帰の深さが限界に達したと言われてランタイムエラーが発生します。

C.methodの中のsuperによって渡されたselfオブジェクトは、クラスCのインスタンスです。 よってsuperから呼ばれたB.methodの中ではself.__class__がCと解釈されて、

        super(C, self).method() # => B.methodが呼ばれる

となってしまうのでB.methodがB.methodを呼び、無限再帰になってしまうのです。

これを解消するためにはsuper(B, self).methodのようにクラス名をちゃんと明記するか、A.methodのように直接クラスのメソッドにアクセスします。
また、参考として、こういうハックもあります:

問題点

この問題はあまりメジャーではないのか、super(self.__class__, self)と書いているコードはたくさん見かけます。しかし、これは非常に大きな問題だと思います。

super(self.__class__, self)を使っているクラスを継承してsuper(self.__class__, self)と書いてはいけない。ということを意識してPythonを書いている人がどれだけいるのか?
これといって良い代替案がないことも問題でしょう。「スーパークラスのメソッドを呼ぶこと」と「Aクラスのメソッドを呼ぶこと」は全然意味が違います。それなのにsuperが使いにくいからA.methodと書きなさいというのはいくらなんでもひどすぎではないか?

他に方法があれば教えていただけると嬉しいのですが、いずれにせよsuper(self.__class__, self)の利用は控えたほうがよさそうです。

Python製のベストなオープンソースプロジェクト

Best written projects on Python GitHub?

redditで面白いポストをみつけたので日本語で軽くまとめておきます。

http://www.reddit.com/r/Python/comments/1ls7vq/best_written_projects_on_python_github/
エレガントで上手に書かれていて読みやすく練習にも最適なおすすめプロジェクトを探しています。
何かありますか?

Flask

Minecraft

  • Python+Pygletを用いたMinecraft風の簡単なプログラム


Python / Pyglet Minecraft Clone - YouTube

CherryPy

  • 最小限主義者のためのPython製Webフレームワーク

Pyramid

  • Pyramid webフレームワーク

youtube-dl

  • YouTube.comとその他の動画サイトからビデオをダウンロードするシンプルなコマンドラインプログラム

reddit

  • reddit.comを支えるコード

Tornado

  • TornadoはもともとFriendFeedにて開発されていたPython webフレームワークであり、非同期ネットワークライブラリです。

RQ project

  • シンプルなジョブキュー

PRAW