Skip to content

Instantly share code, notes, and snippets.

@MikuroXina
Created March 24, 2020 10:48
Show Gist options
  • Select an option

  • Save MikuroXina/019c009b7c2a7c1c75af1082f38f6b85 to your computer and use it in GitHub Desktop.

Select an option

Save MikuroXina/019c009b7c2a7c1c75af1082f38f6b85 to your computer and use it in GitHub Desktop.
MVCD で三目並べ
#include <optional>
class Stone {
public:
// クラスの中にクラス定義
class Pos {
int _x, _y;
Pos(int x, int y) : _x(x), _y(y) {}
public:
Pos(Pos const &) = default;
// coord は x と y が 1 以上 3 以下でないと std::nullopt
static std::optional<Pos> coord(int x, int y) {
if (!(1 <= x && x <= 3 && 1 <= y && y <= 3)) {
return std::nullopt;
}
return Pos(x, y);
}
int x() const { return _x; }
int y() const { return _y; }
bool operator==(Pos const &r) const { return _x == r._x && _y == r._y; }
};
class Side {
bool _isX;
Side(bool isX) : _isX(isX) {}
public:
Side(Side const &) = default;
static Side X() { return Side(true); }
static Side O() { return Side(false); }
bool isX() const { return _isX; }
bool isO() const { return !_isX; }
bool operator==(Side const &r) const { return _isX == r._isX; }
Side opposite() const { return Side(!_isX); }
};
private:
Pos _pos;
Side _side;
public:
Stone(Pos pos, Side side) : _pos(pos), _side(side) {}
Pos pos() const { return _pos; }
Side side() const { return _side; }
};
enum struct Winner {
X,
O,
Draw,
};
#include <ios>
#include <iostream>
#include <limits>
class TictacToeController {
public:
Stone::Pos askPosToPlace() {
using Pos = Stone::Pos;
std::optional<Pos> pos;
while (!pos) {
std::cout << "空白区切りで X座標 Y座標 と入力してね > ";
int x = 0, y = 0;
std::cin >> x >> y;
pos = Pos::coord(x, y);
}
return pos.value();
}
};
class TicTacToeView {
std::optional<Stone::Side> cells[3][3] = {};
void showCells() const {
for (int y = 0; y < 3; ++y) {
if (y != 0) {
std::cout << std::string(11, '-') << "\n";
}
for (int x = 0; x < 3; ++x) {
std::cout << (x == 0 ? " " : " | ")
<< (cells[x][y] ? (cells[x][y].value().isO() ? "O" : "X")
: " ");
}
std::cout << "\n";
}
std::cout << std::endl;
}
public:
void addStone(Stone const &stone) {
auto const pos = stone.pos();
cells[pos.x() - 1][pos.y() - 1] = stone.side();
showCells();
}
void showSide(Stone::Side const &side) {
if (side.isX()) {
std::cout << "X の手番です\n";
} else {
std::cout << "O の手番です\n";
}
}
void showResult(Winner const &winner) {
switch (winner) {
case Winner::X:
std::cout << "X の勝ち!\n";
break;
case Winner::O:
std::cout << "O の勝ち!\n";
break;
case Winner::Draw:
std::cout << "引き分け!\n";
break;
default:
std::cout << "何かが異常なんだ!\n";
}
}
};
#include <algorithm>
#include <vector>
class TicTacToeDomain {
std::vector<Stone> stones;
Winner sideToWinner(Stone::Side const &side) const {
if (side.isX()) {
return Winner::X;
}
return Winner::O;
}
std::optional<Winner> judge() const {
auto matrix = std::vector<std::vector<std::optional<Stone::Side>>>(
3, std::vector<std::optional<Stone::Side>>(3));
for (auto const &e : stones) {
auto pos = e.pos();
matrix[pos.x() - 1][pos.y() - 1] = e.side();
}
// 斜め方向
if (((matrix[0][0] == matrix[1][1] && matrix[1][1] == matrix[2][2]) ||
(matrix[0][2] == matrix[1][1] && matrix[1][1] == matrix[2][0])) &&
matrix[1][1]) {
return sideToWinner(matrix[1][1].value());
}
// 縦方向
for (int x = 0; x < 3; ++x) {
auto head = matrix[x].begin();
if (std::all_of(head + 1, matrix[x].end(),
[head](auto const &e) { return e == *head; }) &&
matrix[x][0]) {
return sideToWinner(matrix[x][0].value());
}
}
// 横方向
for (int y = 0; y < 3; ++y) {
auto head = matrix.begin();
if (std::all_of(
head + 1, matrix.end(),
[head, y](auto const &e) { return e[y] == head[0][y]; }) &&
matrix[0][y]) {
return sideToWinner(matrix[0][y].value());
}
}
if (9 <= stones.size()) {
return Winner::Draw;
}
return std::nullopt;
}
public:
void start(TictacToeController &controller, TicTacToeView &view) {
Stone::Side currentPlayer = Stone::Side::X();
while (true) {
view.showSide(currentPlayer);
Stone::Pos pos = controller.askPosToPlace();
while (std::any_of(stones.begin(), stones.end(),
[pos](auto const &e) { return e.pos() == pos; })) {
pos = controller.askPosToPlace();
}
auto const stone = Stone(pos, currentPlayer);
stones.push_back(stone);
view.addStone(stone);
auto result = judge();
if (result) {
view.showResult(result.value());
return;
}
currentPlayer = currentPlayer.opposite();
}
}
};
int main() {
// メイン関数の中はこれだけで良くなるようにする
TictacToeController controller;
TicTacToeView view;
TicTacToeDomain domain;
domain.start(controller, view);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment