CodeIQ:ヒット・アンド・ブロー

私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「ヒット・アンド・ブロー」(CodeIQ)を参照してください。

問題の概要

問題を引用します。
ヒット・アンド・ブローというゲームがあります。
出題者は、0000~9999の、4桁の数字の中から、秘密の答えを選びます。
解答者は、出題者が選んだ秘密の答えを予想し、答えます。
秘密の答えに対し、予想の答えがどのくらい正しいかを、出題者は解答者に次のヒントを与えます。

[1]数字と、数字の場所(桁)が共に合っている場合はヒット
[2]数字は合っているが、数字の場所が合っていない場合はブロー

たとえば、秘密の答えが1300で、解答者が1234と答えた場合、

1300
1234

最初の桁は完全に一致していますので1ヒット(ヒットの場合はブローのカウントを行いません)、次の桁は一致していませんが、その次の桁に3がありますので、'3'という数が含まれているということで1ブロー、つまりこの場合は、1ヒット・1ブローとなります。

また、同じ数が複数ある場合、たとえば秘密の答えが0111、予想の答えが1000と答えた場合、「1」は1ブロー、「0」も1ブローと考え、全体で2ブローとします。

秘密の答えと予想の答えがピッタリ同じになった場合は、4ヒットと答えることになります。

【問題】
秘密の答えと予想の答えが与えられたとき、適切にヒット・ブローのヒントを出すプログラムを書いてください。

【入力】
●1行目に、データの件数Nが書かれています。整数値(1≦N≦100)
●2行目以降のN行に、秘密の答えと予想の答えが、半角スペース区切りで書かれています

----(例)----
3
1300 1234
0004 1234
1234 1230
----(例)----

【出力】
秘密の答え・予想の答えのセット毎に、改行区切りでヒントを出力してください。
ヒットの場合は'H'、ブローの場合は'B'としてください。
ヒット・ブローがない場合でも、'0H'や'0B'のように出力してください。
また、出力順はヒットを先にしてください。つまり、'mHnB'のような形でということです。

----(例)----
1H1B
1H0B
3H0B
----(例)----

私のプログラム

Rubyで解答しています。

#!/usr/bin/ruby

def solve(a, e)
	h = 0
	b = Array.new(10,0)

	for i in 0...4
		if a[i] == e[i] then h += 1
		elsif a.include?(e[i]) then b[e[i].to_i] = 1
		end
	end

	return h.to_s + 'H' + b.reduce(:+).to_s + 'B'
end

# main
l = 0
while line = gets
	l += 1
	next if l==1

	line.strip!
	next if line.empty?

	a, e = line.split
	puts solve(a, e)
end

解説

★1つだったと思いますが、★一つと言うほど簡単ではないと思います。
と言うか、一般的に実際にコードを書く問題は★★からになっているような気がします。

考え方

ヒットを優先的にチェックし、その後でブローをチェックすればOKです。
問題が少しわかりにくいですがブローの場合に数値の重複がある場合、秘密の答え側でも1回ずつしか数えません。
例えば秘密の答え1100と予想1011の場合、予想の1桁めと2桁目は秘密の答えの1桁目と2桁目と一致しますが1ブローです。
秘密の答え1100と1011だと2H1Bになります。

main

入力値を数値にしてsolve()に渡します。

solve(a, e)

引数aは秘密の答え、eは予想です。
hはヒットのカウント、bはブローのカウントですがブローは数値の重複を排除するため、0〜9どの数値で一致がみられたかどうかだけをチェックします。

引数は文字列なので1文字ずつ比較します。
同じ位置が同じ文字ならhをカウントアップします。
それ以外の場合でe[i]の文字がaに含まれている場合bのe[i]の値の場所を1にします。

ループを抜けたら結果を返します。
この時、bの配列は0か1だけでできているので、全ての値を足し合わせてブローのカウントにします。

雑感

自分で解説を書いた時にわかりやすく書くことができなかったのですが、重複の扱いがわかりにくいです。もう少し、例を増やすべきじゃないかと思います。