HaskellでPasoriをたたいてみる

結構前にやってみたのですが、放置気味だったのでとりあえずメモ。

libpafeというlibpasoriから派生したPasoriを叩くためのライブラリ(結構古いっぽい)があったので、それのHaskellラッパーを書いてみました。

github.com

Hackageに0.1.0.0が上がっています。0.1.1.0をその内あげたい。

hsc2hsを使ってバインディングをしただけです。とりあえずお手軽に叩いてお手軽に動かすことができたのですが、

  • 型が守ってくれないつらい
  • ここでちゃんと型を定義しないと全部死ぬ

というのが印象的でした。まあ仕方ないかんが。 cabal runしてセグフォとか結構新鮮でした。

あとHackageにパッケージを上げたのも初めてだったんですが、登録した時期が結構前でまだ自動でアップロード権限が付与されてない時期だったので泣きながらメールしました。 完全に意味が伝わらなさそうな英語メールに対応してくださった管理者の方ありがとうございます。本当に助かりました。

Cabal+FFIでビルド時にハマった(とってもしょーもない話)

ハマった。 いまPasori用のライブラリであるlibpafeをHaskellから使えるようにラッパーを書いているのですが、ドはまりしました。 最初リンクに失敗しまくって涙目になっていたので、先人の知恵を借りようということでtanakhさんのhsmecabを参考にしながら書いていました。

github.com

  extra-libraries:  

という項目に外部のライブラリ名を書くことで簡単に使えるっぽいのですが、

  extra-libraries:  mecab

と同じノリで

  extra-libraries:  libpafe

と書いてもうまく動作しません。

そういえばmecabはlibmecabというファイル名でした。 liblibpafeなんて名前のものを探していることに気がつくまで30分くらいかかった…… 正解は

  extra-libraries:  pafe

だったようです。

github.com

(いまさら)個人用Redmineを導入した

これまでやりたいこと、買いたいものやらもろもろを全部Onenoteで管理するとかいうことをやってたのだけれど、いい加減限界がきたのでRedmineを自宅のサーバに導入しました。
最初WindowsServerに直にインストールしようとしたのだけれど、非公開のGitリポジトリでやってるもろもろに対する管理もRedmineに統一しようと思った時に色々面倒だったので、同サーバ上のHyper-VDebianを入れてその上に導入しました。
現状部屋を片付けるとか割とどうでもいいものばかり入ってるけど意外といい感じで進捗管理できそう。やりたいことのネタ帳だけではなくそれを実際にやり始めた時にプロジェクトを追加する形で進捗管理できるのもいいなあと。

あと、MacのTimemachineを保存する先にWindowsServerでSMBな共有フォルダを指定してたのですが、すぐぶっ壊れてくれるのでHyper-V上で割りと適当な感じにNetatalkを使ってAFP共有フォルダ作ってやるようにしました。
勝手にマウントしてバックアップとってくれるのは便利。

よりよいハトクラモナド

この記事はHaskellAdventCalendarの記事から派生したスピンアウト記事です。 ハトクラモナドの記事を上げたらちゅーんさんから愛のマサカリこっちのほうがいいのではって提案を頂いたので、それを踏まえてまだたりないものが何かを考えてみたり、自分でちょっとわからなかったことを聞いたのをメモったりします。

その1:モナドの中身を多相型にするべき?(という名の半分言い訳)

まず、ちゅーんさんの記事でも指摘してもらったとおり、モナドの中身が多相型じゃなくてCoins (=Int)になってます。
僕としてはハトクラに限定するんだったら絞っとかないと「ハトクラのモナドです!」って言い貼れなくなっちゃうのではないかという考えで、多相型ではなくCoinsを入れてしまったのですが、よくよく考えたら実用考えてなかったわ……(元から半分ネタというのは抜きにしても) 実用的には他のMonadインスタンスががそうしているように多相型にすべきでした。

その2:すでにそのフェーズでプレイされたカードが遡れない

ハトクラのカードの中にはその手番ですでに使われたカードに振る舞いが影響されるカードがあります。少なくとも>>=でただ単につなげただけじゃそれを遡ることはできないので、状態に持ってるFieldに持たせるといいのでは(by ちゅーんさん)という話を聞きました。

その3:カードを連結できない状態に陥った後の挙動が若干イケてない

というのも、カードが連結できない状態になったとしても、Left(またはNothing)で後続のカードが無視されるだけで、連結自体はできちゃうという状態になってるというのが問題です。 なんかそうなってくるとモナドの仕組み以外にもなんか色々必要な気がしてきちゃったのですが、どうしましょう。

Haskellでハトクラを表現する

この記事はHaskell Advent Calendar 17日目の記事です。

What is ハトクラ

ハトクラとは、ドミニオンの変種みたいなカードゲームです。
ただし、カードの連結が続く限りカードをプレイできるというルールが加わっています。

f:id:haru2036:20141202231210p:plain

こんな感じ
……
ん……?連結……?

これってモナドになりませんか?(これってトリビアになりませんか?的ノリ)

そこで、今回はハトクラの一部(アクションフェーズ, またはメインフェーズ)を表現するモナドを作ってみようと思います。

やってみる

これはおそらくStateモナドの変種みたいな感じで行けるのではと思いました。ただ、一番後ろのカードに連結マークがない(=もう連結できない)状態をどうにかして表現しないといけません。
まずはなんかと合成できないかと考えてみたりしてたのですが、何と合成しようかなあと考えてました。


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させていただきました。
他の自称普通の大学生たちはバケモノばかりで恐ろしかった……
内容的にはあんまりうまくいかなかったので微妙だったけど、今度は係り受け解析とかも含めたいなと。

スライド。

参加された方、主催のみなさま、会場提供してくださったGMOの皆様、本当にありがとうございました。
ConoHaのクーポンで何をしようか考えている

遠隔もくもく

意外とUstreamを使ったオウチもくもく会がはかどった、もっと多人数でやってもいいのではないか