예제 소스
오목으로 이해하는 자기대국과 모방 학습 · 파일 59개
aiplayer.cpp
#include "aiplayer.h"
#include <array>
#include <limits>
namespace {
Cell opponent(Cell x)
{
return (x == Cell::Black) ? Cell::White : Cell::Black;
}
/** 자기 금수(흑·백 동일: 삼삼·장목)에는 두지 않음. 사사는 금수 아님. */
bool canPlaceForPlayer(const BoardModel &m, int r, int c, Cell who)
{
if (m.cellAt(r, c) != Cell::None)
return false;
if (m.hasKyungsuBan(r, c, who))
return false;
return true;
}
int linePotential(const BoardModel &m, int r, int c, int dr, int dc, Cell color)
{
const int N = m.size();
int streak = 0;
bool blockedNear = false;
int rr = r - dr;
int cc = c - dc;
if (rr >= 0 && rr < N && cc >= 0 && cc < N) {
const Cell oc = m.cellAt(rr, cc);
if (oc != Cell::None && oc != color)
blockedNear = true;
}
rr = r;
cc = c;
for (int k = 0; k < WIN_LENGTH && rr >= 0 && rr < N && cc >= 0 && cc < N;
++k, rr += dr, cc += dc) {
const Cell v = m.cellAt(rr, cc);
if (v == color)
++streak;
else if (v == Cell::None)
;
else
break;
}
int score = streak * streak * 120;
if (streak >= 4)
score += 50000;
if (blockedNear && streak >= 4)
score /= 8;
return score;
}
int scorePlacement(const BoardModel &m, int r, int c, Cell who)
{
static const std::array<int, 4> dr = {1, 0, 1, 1};
static const std::array<int, 4> dc = {0, 1, 1, -1};
int s = 0;
for (int i = 0; i < 4; ++i)
s += linePotential(m, r, c, dr[i], dc[i], who);
return s;
}
} // namespace
namespace AiPlayer {
QPoint suggestMove(const BoardModel &model, Cell aiColor)
{
const int N = model.size();
const Cell opp = opponent(aiColor);
for (int r = 0; r < N; ++r) {
for (int c = 0; c < N; ++c) {
if (!canPlaceForPlayer(model, r, c, aiColor))
continue;
if (model.wouldWinIfPlaced(r, c, aiColor))
return QPoint(c, r);
}
}
for (int r = 0; r < N; ++r) {
for (int c = 0; c < N; ++c) {
if (!canPlaceForPlayer(model, r, c, aiColor))
continue;
if (model.wouldWinIfPlaced(r, c, opp))
return QPoint(c, r);
}
}
int bestScore = std::numeric_limits<int>::min();
QVector<QPoint> candidates;
const int mid = N / 2;
// 벽에 붙은 3/4 자리는 사실상 후보에서 배제 (즉시승수/즉시 차단보다 항상 낮은 우선순위).
constexpr int kWallAttachedPenalty = 200000;
for (int r = 0; r < N; ++r) {
for (int c = 0; c < N; ++c) {
if (!canPlaceForPlayer(model, r, c, aiColor))
continue;
int s = scorePlacement(model, r, c, aiColor) * 12
+ scorePlacement(model, r, c, opp) * 15
- std::abs(r - mid) - std::abs(c - mid);
if (model.formsWallAttachedRun(r, c, aiColor, 3)
|| model.formsWallAttachedRun(r, c, aiColor, 4)) {
s -= kWallAttachedPenalty;
}
if (s > bestScore) {
bestScore = s;
candidates.clear();
candidates.append(QPoint(c, r));
} else if (s == bestScore) {
candidates.append(QPoint(c, r));
}
}
}
if (!candidates.isEmpty())
return candidates.front();
for (int r = 0; r < N; ++r) {
for (int c = 0; c < N; ++c) {
if (canPlaceForPlayer(model, r, c, aiColor))
return QPoint(c, r);
}
}
return QPoint(mid, mid);
}
} // namespace AiPlayer