http://qiita.com/Nabetani/items/709d61dff282cff7a890
http://nabetani.sakura.ne.jp/hena/ord8biboma/
「ビットボンバーマン」をErlangでやってみました。
ビット演算でやってみました。数値データーそのまま、爆弾をビットシフトさせながら壁の0と1を反転したものとAND演算をすることで、爆風が壁で止まるようにしています。ビットシフトの方向は-6が上、-1が左、+1が右、+6が下です。上下左右にはみ出さないように、シフト前にマスクとANDをとっています。
-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関数関数で使っています。