http://nabetani.sakura.ne.jp/hena/ord2/
ビットで表現されたテトリスの揃った行を消すという問題です。
Ruby
module BitTetris
module_function
def solve(input)
field = input.split("-").map { _1.to_i(16) }
to_be_erased = to_a(field.inject(:&))
field.map { "%02x" % erase(_1, to_be_erased) }.join("-")
end
def erase(i, to_be_erased)
to_a(i).zip(to_be_erased).map { |s, t| t == "1" ? "" : s }.join.to_i(2)
end
def to_a(i)
sprintf("%08b", i).chars
end
end
if __FILE__ == $0
require 'minitest/autorun'
describe 'BitTetris' do
[
["ff-2f-23-f3-77-7f-3b", "1f-03-00-1c-0d-0f-06"],
["01", "00"],
["00", "00"],
["7a-4e", "0c-02"],
["56-b6", "08-14"],
["12-12-12", "00-00-00"],
["de-ff-7b", "0a-0f-05"],
["95-be-d0", "05-1e-20"],
["7c-b0-bb", "1c-20-2b"],
["7a-b6-31-6a", "3a-56-11-2a"],
["32-0e-23-82", "18-06-11-40"],
["ff-7f-bf-df-ef", "0f-07-0b-0d-0e"],
["75-df-dc-6e-42", "35-5f-5c-2e-02"],
["62-51-ef-c7-f8", "22-11-6f-47-78"],
["0c-47-8e-dd-5d-17", "04-23-46-6d-2d-0b"],
["aa-58-5b-6d-9f-1f", "52-28-2b-35-4f-0f"],
["ff-55-d5-75-5d-57", "0f-00-08-04-02-01"],
["fe-fd-fb-f7-ef-df-bf", "7e-7d-7b-77-6f-5f-3f"],
["fd-fb-f7-ef-df-bf-7f", "7e-7d-7b-77-6f-5f-3f"],
["d9-15-b5-d7-1b-9f-de", "69-05-55-67-0b-4f-6e"],
["38-15-fd-50-10-96-ba", "18-05-7d-20-00-46-5a"],
["fe-fd-fb-f7-ef-df-bf-7f", "fe-fd-fb-f7-ef-df-bf-7f"]
].each do |input, expect|
it input do
assert_equal BitTetris.solve(input), expect
end
end
end
end
まずは消すべき行をビット演算で求めて(field.inject(:&)
)、あとはそれぞれの列で消すべきビットをerase
メソッドで消しています。ビットを消すのにどうすべきかなと迷いました。結局、2進表現してから Array に直して(to_a
メソッド)、map
で消しています。String の2進数表現と16進数表現、それに Integer をどう使いわけるかという問題なのかなと思います。
なお、erase
メソッドでワンライナーでやっている ary1.zip(ary2).map {|a, b| ...}
みたいな書き方はわたしのよくやるところで、zip
で Array を作り直しているところは効率がよくないのですが、表現が簡潔になることがあります。愚直に(?)やると、こんな感じかな?
def erase(i, to_be_erased)
a = to_a(i)
result = ""
a.length.times do |j|
result << a[j] if to_be_erased[j] != "1"
end
result.to_i(2)
end
こちらの方が早くて効率がよい筈ですが。