んーと、これでちっとはC++らしくなったかな。
#include <iostream>
#include <string>
#include <array>
#include <vector>
#include <algorithm>
#include <utility>
#include <iterator>
using namespace std;
const int N = 3;
// N桁の数
typedef array<int,N> number;
ostream& operator<<(ostream& stream, const number& n) {
for_each(begin(n),end(n),[&](int n) { stream << n;});
return stream;
}
// Hit/Blowの組
typedef pair<int,int> hb;
ostream& operator<<(ostream& stream, const hb& h) {
return stream << h.first << "Hit / " << h.second << "Blow";
}
// 出題者
class contributor {
friend class referee;
public:
contributor(number a) : answer(a) {}
private:
number answer;
};
// 審判員
class referee {
public:
// 重複のないことを確認する
static bool isnot_duplicated(number n) {
sort(begin(n),end(n));
return unique(begin(n),end(n)) == end(n);
}
// hit/blow数を勘定する
static hb judge(const number& actual, const number& guess) {
int hit = 0;
int blow = 0;
for ( int i = 0; i < N ; ++i ) {
for ( int j = 0; j < N; ++j ) {
if ( actual[i] == guess[j] ) {
if ( i == j ) ++hit; else ++blow;
}
}
}
return hb(hit,blow);
}
static hb judge(contributor& con, const number& guess) {
return judge(con.answer, guess);
}
};
// 解答者
class solver {
private:
typedef pair<number,hb> hint;
public:
solver() {
init_candidates();
}
// hintを追加する
void add_hint(number n, hb h) {
hints.push_back(hint(n,h));
}
// 候補の中から答案をひとつ返す
number submit() {
think();
random_shuffle(begin(candidates),end(candidates));
return candidates.back();
}
// 候補の数を返す
int n_candidates() const {
return candidates.size();
}
private:
// 1~9から重複なくN個選び、候補を列挙する。
void init_candidates() {
array<bool,9> bits;
fill(begin(bits),end(bits),false);
fill_n(begin(bits), N, true);
do {
int c = 0;
number tmp;
for ( int i = 0; i < 9; ++i ) {
if ( bits[i] ) tmp[c++] = i+1;
}
do {
candidates.push_back(tmp);
} while ( next_permutation(begin(tmp),end(tmp)) );
} while ( prev_permutation(begin(bits),end(bits)) );
}
void think() {
// 候補の中から、これまでに得られたhintと矛盾があるものを除外する
candidates.erase(
remove_if(begin(candidates),end(candidates),
[&](number n) {
return any_of(begin(hints),end(hints),
[&](hint h) {
return referee::judge(n,h.first) != h.second;
});
}),
end(candidates));
}
vector<number> candidates; // 候補
vector<hint> hints; // もらったヒント
};
int main() {
number answer;
while ( true ) {
cout << "各桁が1~9である" << N << "桁の数を入力してほしい。各桁で数が重複するのは避けてくれ" << endl;
string str; cin >> str;
const string digits = "123456789";
if ( str.size() == N &&
all_of(begin(str),end(str),[&](char c) { return digits.find(c) != string::npos;}) ) {
transform(begin(str), end(str), begin(answer), [](char c) { return c - '0';});
if ( referee::isnot_duplicated(answer) ) break;
}
}
cout << "君が選んだのは " << answer << " だね。";
contributor con(answer);
solver sol;
cout << "では僕がその数を推理しよう。" << endl;
int trial = 0;
while ( true ) {
// 候補の中からひとつ選ぶ
cout << ++trial << "回目:"
<< " 正解となる数の候補は " << sol.n_candidates() << " あるが... ";
number candidate = sol.submit();
cout << candidate << " ではないかな?" << endl;
// 結果を判定。 正解ならばloopを抜ける
hb result = referee::judge(con, candidate);
cout << result << endl;
if ( result.first == N ) break;
// 判定結果をhintsに追加する
sol.add_hint(candidate,result);
}
}