三目並べの盤の状態が与えられるからo
(先攻)とx
(後攻)どちらが勝つか判定するもの[1]
盤の状態は9文字の文字列として o
(oの置いたマス) x
(xの置いたマス) s
(何も置かれてないマス) で左上から順に並べたもので表現される
ちゃんとゲーム木の上を探索するプログラムを書いた
// you can this source
// also at: https://gist.github.com/cympfh/8aa0b2aa0d26b8822bed
#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i=0;i<(n);++i)
#define loop for(;;)
#define trace(var) cerr<<">>> "<<#var<<" = "<<var<<endl;
#define inf (1e9)
#define eps (1e-9)
// 盤の空いてるところ集合
inline
vector<int> empties(vector<int>&v) {
vector<int> r;
rep (i, 9) if (v[i] == 0) r.push_back(i);
return r;
}
inline
int num_empty(vector<int>&v) {
return empties(v).size();
}
inline
bool is_full(vector<int>&v) {
return num_empty(v) == 0;
}
// 決着がついてるかどうか
// 0: ついてない
// ついてる場合
// 1: o (Alice) の勝ち
// 2: x (Bob) の勝ち
inline
int is_goal(vector<int>&v) {
for (int t=1; t<3; ++t) {
rep (k, 3) {
if ( v[3 * k + 0] == t
and v[3 * k + 1] == t
and v[3 * k + 2] == t ) return t;
if ( v[k + 0] == t
and v[k + 3] == t
and v[k + 6] == t ) return t;
}
if (v[0] == t
and v[4] == t
and v[8] == t ) return t;
if (v[2] == t
and v[4] == t
and v[6] == t ) return t;
}
return 0;
}
// search のなかで使う探索メモ
map<vector<int>, int> memo;
// v: 盤の状態
// b: Aliceの手版であるか?の
//
// 返り値
// 0: 引き分け
// 1: Aliceの勝ち
// 2: Bobの勝ち
int search(vector<int> v, bool b) {
if (memo.count(v) > 0) return memo[v];
if (is_goal(v)) return memo[v] = is_goal(v);
if (is_full(v)) return memo[v] = 0;
auto es = empties(v);
int t = b ? 1 : 2;
bool drawable = false;
for (int e: es) {
v[e] = t;
// 状態をDFSする
int r = search(v, (not b));
// それを指せば必勝なら勝ち
if (r == t) return memo[v] = t;
// せめて引き分けにもっていけるかどうかメモしておく
if (r == 0) drawable = true;
v[e] = 0;
}
// ここに到達するということは必勝の手は無かったということ
if (drawable) return memo[v] = 0;
// 引き分けにも出来ないのなら必敗
return memo[v] = (3 - t);
}
int solve(string s) {
vector<int> f(9); rep (i, 9) f[i] = s[i] == 's' ? 0 : s[i] == 'o' ? 1 : 2;
bool b = num_empty(f) % 2 == 1; // is o's turn?
return search(f, b);
}
int main() {
string s;
while (getline(cin, s)) {
int res = solve(s);
cout << ((res == 0) ? 'd' : (res == 1) ? 'o' : 'x') << endl;
}
return 0;
}
大した状態数もないだろうから、
実直に工夫なくDFSで全探索させた。
$ cat input
sssssssss
ssssossss
ssssossxs
$ ./a.out < input
d
d
o
すなわち
| | | |
| | | |
| | | |
=> draw
| | | |
| |o| |
| | | |
=> draw
| | | |
| |o| |
| |x| |
=> o wins
三目並べって経験上、よっぽど下手を打たない限り、引き分けになるイメージだ
[1] で、ちょっと他の人の提出も見てみた
AIZU ONLINE JUDGE: Code Review
すごく短い。
ていうかこれは問題の解釈を間違えてた
なんか、読み込まれた盤面の上ですでに、決着が付いてるかどうかだけを判定する問題だったらしい
だから当然、
| | | |
| |o| |
| |x| |
は draw
と判定しないといけなかった
じゃあなんで私のは通ったんだろう
運が良かったのかな (入力が雑魚だったのかな)