Help us understand the problem. What is going on with this article?

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

More than 5 years have passed since last update.

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

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

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

結果の一部:

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

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では関数定義でパターンマッチが使えます。たとえば

f(1) -> 1;
f(2) -> 2;
f(3) -> "さぁん!".

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

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

AHO = fun
  (1) -> 1;
  (2) -> 2;
  (3) -> "さぁん!"
end.

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

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

4. 無名関数で再帰

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

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

factorial(0) -> 1;
factorial(N) -> N * factorial(N - 1).

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

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

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

FACT(FACT, 3). % --> 6

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

pazworld
ヘボプログラマです。Erlangがマイブームです。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away