ビットボンバーマンをErlangで(横へな8)

http://qiita.com/Nabetani/items/709d61dff282cff7a890
http://nabetani.sakura.ne.jp/hena/ord8biboma/
「ビットボンバーマン」をErlangでやってみました。

ビット演算でやってみました。数値データーそのまま、爆弾をビットシフトさせながら壁の0と1を反転したものとAND演算をすることで、爆風が壁で止まるようにしています。ビットシフトの方向は-6が上、-1が左、+1が右、+6が下です。上下左右にはみ出さないように、シフト前にマスクとANDをとっています。

```erlang:bitbomb.erl
-module(bitbomb).
-compile(export_all).

%% 問題を解く
solve(Data) ->
  {Wall, Bomb} = list_to_tuple([list_to_integer(X, 16)
    || X <- string:tokens(Data, "/")]),
  DirInfo = [ % シフト方向とシフト時マスクのデーター
    {-6, 2#00000011111111111111111111111100},  % 上
    {-1, 2#01111101111101111101111101111100},  % 左
    {+1, 2#11111011111011111011111011111000},  % 右
    {+6, 2#11111111111111111111111100000000}], % 下
  AllFlare = lists:foldl(fun(XDir, Acc) ->
      Acc bor flare(Wall, Bomb, XDir) end, 0, DirInfo),
  lists:flatten(io_lib:format("~8.16.0b", [AllFlare])).

%% 壁、爆弾、爆風方向から爆風の軌跡を返す
flare(Wall, Bomb, {Dir, DirMask}) ->
  WallMask = 16#FFFFFFFC - Wall,
  {_Flare, FlareTrail} = times(6, fun({Flare, FlareTrail}) ->
    WalledFlair = WallMask band Flare,
    NextFlair = (WalledFlair band DirMask) bsr Dir,
    {NextFlair, WalledFlair bor FlareTrail}
  end, {Bomb, 0}),
  FlareTrail.

%% 関数を指定回数繰り返す
times(0, _Fun, Acc) -> Acc;
times(N, Fun, Acc) -> times(N - 1, Fun, Fun(Acc)).

%% マップを表示する (デバッグ用)
show_map(X) ->
  Show = fun
    (_F, []) -> ok;
    (F, S) ->
      io:fwrite("~s~n", [string:left(S, 6)]),
      F(F, lists:nthtail(6, S))
  end,
  Show(Show, string:right(string:chars($0, 30)
    ++ integer_to_list(X div 4, 2), 30)).

test(Data, Expected) ->
  Result = solve(Data),
  OkNg = case Result =:= Expected of true -> ok; false -> ng end,
  io:fwrite("~s: ~s -> ~s~n", [OkNg, Data, Result]).

tests() ->
  test("802b1200/01400c20", "53c40cfc"), %0
  test("28301068/84080504", "d64fef94"), %1
  test("100a4010/80010004", "e241850c"), %2
  test("81020400/000000fc", "0e3cfbfc"), %3
  test("80225020/7e082080", "7fdd24d0"), %4
  test("01201200/40102008", "fe1861fc"), %5
  test("00201000/01000200", "43c48f08"), %6
  test("00891220/81020408", "ff060c1c"), %7
  test("410033c0/0c300000", "3cf0c000"), %8
  test("00000000/01400a00", "7bf7bf78"), %9
  test("00000000/20000a00", "fca2bf28"), %10
  test("00000000/00000000", "00000000"), %11
  test("00cafe00/00000000", "00000000"), %12
  test("aaabaaaa/50000000", "51441040"), %13
  test("a95a95a8/56a56a54", "56a56a54"), %14
  test("104fc820/80201010", "ea30345c"), %15
  test("4a940214/05000008", "05000008"), %16
  test("00908000/05000200", "ff043f48"), %17
  test("00c48c00/fe1861fc", "ff3873fc"), %18
  test("00000004/81020400", "fffffff0"), %19
  test("111028b0/40021100", "e08fd744"), %20
  test("6808490c/01959000", "17f7b650"), %21
  test("30821004/81014040", "c75de5f8"), %22
  test("0004c810/10003100", "fe4937c4"), %23
  test("12022020/88200000", "edf08208"), %24
  test("2aa92098/01160000", "45165964"), %25
  test("00242940/10010004", "fc43c43c"), %26
  test("483c2120/11004c00", "33c3de10"), %27
  test("10140140/44004a04", "eda3fe3c"), %28
  test("0c901d38/72602200", "f36da280"). %29
```

結果の一部:

<pre>
ok: 802b1200/01400c20 -> 53c40cfc
ok: 28301068/84080504 -> d64fef94
ok: 100a4010/80010004 -> e241850c
ok: 81020400/000000fc -> 0e3cfbfc
...
</pre>

Erlangの勉強を兼ねてのんびりとやっています。今回新たに知ったことがいくつもありました。

# 新たに知ったこと

## 1. ビット演算子は頭に「b」

band (bit AND), bor (bit OR), bsr (bit shift right)など、Erlangのビット演算子には頭に「b」が付きます。

## 2. sprintfに相当するのはio_lib:format

io:format関数はフォーマットして画面出力しますが、io_lib:format関数はフォーマットした文字列を返します。C言語でいうとprintfがio:format, sprintfがio_lib:formatです。ただしio_lib:formatの結果はリストのリストで返ってきたりするので、lists:flattenをかける必要があります。

## 3. 無名関数でパターンマッチ

Erlangでは関数定義でパターンマッチが使えます。たとえば

<pre>
f(1) -> 1;
f(2) -> 2;
f(3) -> "さぁん！".
</pre>

と書くことで、3の時だけアホになるような関数fを定義できます。

次のように書くことで、無名関数でパターンマッチを使うことができます。

<pre>
AHO = fun
  (1) -> 1;
  (2) -> 2;
  (3) -> "さぁん！"
end.
</pre>

これで3の時だけアホになるような関数(無名関数)が変数AHOに格納(バインド)されました。

無名関数のパターンマッチはshow_map関数関数で使っています。

## 4. 無名関数で再帰

呼び出す際に引数に関数自体を渡すことで、無名関数で再帰することができます。

たとえば階乗n!の計算は

<pre>
factorial(0) -> 1;
factorial(N) -> N * factorial(N - 1).
</pre>

ですが、これを無名関数で書くと次のようになります。

<pre>
FACT = fun
  (_, 0) -> 1;
  (F, N) -> N * F(F, N - 1)
end.
</pre>

呼び出す時には引数にFACTも渡してやります。

<pre>
FACT(FACT, 3). % --> 6
</pre>

無名関数の再帰もshow_map関数関数で使っています。