Haskellでハトクラを表現する
この記事はHaskell Advent Calendar 17日目の記事です。
What is ハトクラ
ハトクラとは、ドミニオンの変種みたいなカードゲームです。
ただし、カードの連結が続く限りカードをプレイできるというルールが加わっています。
こんな感じ
……
ん……?連結……?
これってモナドになりませんか?(これってトリビアになりませんか?的ノリ)
そこで、今回はハトクラの一部(アクションフェーズ, またはメインフェーズ)を表現するモナドを作ってみようと思います。
やってみる
これはおそらくStateモナドの変種みたいな感じで行けるのではと思いました。ただ、一番後ろのカードに連結マークがない(=もう連結できない)状態をどうにかして表現しないといけません。
まずはなんかと合成できないかと考えてみたりしてたのですが、何と合成しようかなあと考えてました。
@haru2036 残りアクションのカウンタがマイナスになった時点で残りの計算をしなくなるような仕組みが良いんじゃないかなぁ、EitherとStateを変換子で合成するとか。
— ちゅーん (@its_out_of_tune) November 17, 2014
EitherとStateならLeftでも値が持てるけどそのあとの計算がされなくなるのでピッタリですね。ありがとうございます! 一般的には失敗した時の情報とかを持たせるEitherですが、そんな使い方もあるとは。
というわけで、アクションフェーズの型を考えてみました。
data Field = Field { inheritanceRights :: Int, hand :: Cards, deck :: Cards, trash :: Cards } type Cards = [Card] type Card = Coins -> ActionPhase type Coins = Int type ActionPhase = StateT Field (Either (Coins, Field)) Coins
StateTとEitherを合成しました。Fieldはゲーム内の自分に関する大域的な状態で、Coinsは利用できるコインの枚数です。
ただ、なんかこれだと定義がごちゃごちゃしてる気がしてちょっとイケてないなと思ったので、StateとEitherTを使ったバージョンも書いてみたのですが、あんまりどっちもイケてなかったので使いやすそうなこっちのバージョンを採用しました。
とか全然間違ってた事を考えてたのですが、ツッコミをもらえて助かりました。
理解が遅くて申し訳ない、ありがとうございます。
それも含めた版がこちら:
type ActionPhase = EitherT Coins (State Field) Coins
一部のカードを実装してみる
さて、幾つか実際のゲームに登場するカードを実装して試してみましょう。
まずはこれがなきゃ始まらない農園とか都市とか。
farm :: Card farm c = do return (c + 1)
順当 and 単純。当たり前やがなッて感じです。
あとは、コレ単体では存在しませんがよく出てくる次にカードが繋げない終端のカードです。
stop :: Card stop c = do fld <- (lift get) left c
EitherTの機能を使ってその時点での状態をLeftにくるんで返すだけです。 単純です。
他には個人的に好きなカード、錬金術士とかもよいですね。
alchemist :: Card alchemist c = do fld <- lift get lift $ put $ drawCards 2 fld return c
addHands :: Field -> Cards -> Field addHands f cds = setHands f ((f ^. hand) ++ cds) setHands :: Field -> Cards -> Field setHands f cds = f & hand .~ cds drawCard :: Field -> Field drawCard f = let card = head (f^.deck) deckTail = tail (f^.deck) newHand = card : (f^.hand) in (f&hand .~ newHand)&deck .~ deckTail drawCards :: Int -> Field -> Field drawCards count f | count > 0 = drawCard $ drawCards (count - 1) f | count == 0 = f | count < 0 = error "count should be > 0"
2枚山札から手札に引く事ができるカードです。 これまでFieldを触る処理がなかったので全然書きませんでしたが、Lensは更新を簡単に出来てよいですね、なお、今回の処理に使うには本家はでかすぎるのでちゅーんさんのLensを使わせていただきました。
Haskellまだまだ修行の身な上時間が少なくやっつけ気味になってしまいましたが、とりあえずこんなかんじでハトクラをモナドにできそう、という感じはもてました。
ただし、二股になるのとかは考慮していないので完全にはまだまだ程遠いですね…… ちなみに、githubにリポジトリあります。需要全くなさそうだし適当ですが。
haru2036/heart-of-crown-monad · GitHub
余談: Stateモナドの仕組みを理解してから、それを使うためには仕組みまで理解することがないのを知りました。 普通に楽しかったし興味深い仕組みにより実現されてることを知れたのでよいですが、Haskellって全体的にただ使うのはらくらくだけど理解するのは大変ですね……
#ssmjp 2014/10に参加してきました
ssmjp 2014/10に参加してきました。
第一部では運用とセキュリティ関連のディスカッションで、そうそうたる人たちがパネラーとして参加されていました(あんまり良く知らない界隈なのに名前知ってる時点でそうそうたる人たちだなあと)
まあ、セキュリティ関連のかなり戦い的な話だったのですが、AWSの話もあり。AWSってアレちゃんと責任の所在までしっかりされているんですな。
使えるようになっとくべきかもと思いました。
第二部ではLTだったのですが、僕も唯一の本物の普通の大学生としてLTさせていただきました。
他の自称普通の大学生たちはバケモノばかりで恐ろしかった……
内容的にはあんまりうまくいかなかったので微妙だったけど、今度は係り受け解析とかも含めたいなと。
あっぷしました http://t.co/PbQtfFLSqi @SlideShareさんから #ssmjp
— はる (@haru2036) 2014, 10月 27
スライド。
参加された方、主催のみなさま、会場提供してくださったGMOの皆様、本当にありがとうございました。
ConoHaのクーポンで何をしようか考えている
遠隔もくもく
— kumar (@kumar8600) 2014, 10月 11
もくもくとか言いつつ半分くらい遊んでいる (live at http://t.co/IBjL259vYX)
— はる (@haru2036) 2014, 10月 11
OSXでText.RegexにUTF8な文字列使うとエラーでるっぽい
regex - Matching specific unicode char in haskell regexp - Stack Overflow
みたいです。実はこれと同じような状態で、Localeだけja_jp.UTF-8だけどUTF-8担ってるからいいのかなあと思ってたけどダメだった。 今回は特定の文字で文字列を分解するためだけに使ってたので諦めてsplitパッケージを使いました。 これから使うような状況になったら困るから解決法を調べたい
OSXでHaskellとMeCab
ハマったのでメモ。(割りとどうでも良い落ちだったのでアレ)
困った
環境: OSX10.9.4 + GHC7.8.2 + MeCab0.996(HomeBrewでインストール) + cabal1.20.0.2 (問題のプロジェクトはCabal sandboxにインストールしています。)
発生した問題: mecabを使ったプロジェクトでcabal install するとリンク中にエラーが発生する
Linking dist/dist-sandbox-e0def0a9/build/command-line/command-line ... Undefined symbols for architecture x86_64: "_mecab_get_all_morphs", referenced from: _cfSP_info in libHSmecab-0.4.0.a(MeCab.o) "_mecab_get_lattice_level", referenced from: _cfFl_info in libHSmecab-0.4.0.a(MeCab.o) "_mecab_get_partial", referenced from: _cfW9_info in libHSmecab-0.4.0.a(MeCab.o) "_mecab_get_theta", referenced from: _cfCP_info in libHSmecab-0.4.0.a(MeCab.o) "_mecab_set_all_morphs", referenced from: _cgo1_info in libHSmecab-0.4.0.a(MeCab.o) "_mecab_set_lattice_level", referenced from: _cfXT_info in libHSmecab-0.4.0.a(MeCab.o) "_mecab_set_partial", referenced from: _cfU5_info in libHSmecab-0.4.0.a(MeCab.o) "_mecab_set_theta", referenced from: _cfEc_info in libHSmecab-0.4.0.a(MeCab.o) ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) cabal: Error: some packages failed to install:
このエラーがいまいちよくわかってないのでなんとも言えないのですが、いろいろやっているうちにHaskellのMeCabパッケージにあるversionを使ってみると0.97が帰ってきて、意図的にインストールした0.996とは違うのでどこかに別のMeCabがあるのではないかと思っています
なおった
cabal installするときに、使いたいMeCabのあるディレクトリを渡してあげないとダメなようです。とりあえずこれで解決しました。
cabal install mecab --extra-include-dirs=/usr/local/Cellar/mecab/0.996/include/ --extra-lib-dirs=/usr/local/Cellar/mecab/0.996/lib/
これで動作するようになってからversionを見たら0.996となっていたので、どこかに前なんかやった時のゴミが残っててそれを見に行ってしまってたようですが、そのごみがどこにあるのかわからないので気持ち悪いです。 ひとまず解決。よかった。
追記:
でもまあMeCab 0.97なんてインストールした覚えがないんだよなあ、OSXに最初から含まれてるのかな……
— はる(ATMで紙幣が詰まる) (@haru2036) 2014, 8月 16
実際謎なので気持ち悪さが残りまくる。
追記2:まとめ
まとめ
FFIを使って他の言語のコードをリンクするようなプロジェクトの時はできるだけextra-lib-dirsとか渡したほうが面倒は少ないかもね(なげやり)
Yesodで部員管理用Webサービス作った
作ったのは結構前のことですが、書くのがいまさらになったのでメモ。
Yesodはじめてみました - haru2036のブログ
でYesodが意外としっくりきたことを書きましたが、そのままYesodで行ってみました。
私の部活はソフトウェア研究部とかいう名前のくせに部員の管理はExcelでやるとかいうとってもしょぼい感じだったので、それよりはマシかと……部員の登録から所属に合わせたメールの一括送信までを今のところサポートしています。
リポジトリはこちら:
haru2036/Member-Manager · GitHub