Created
March 24, 2020 10:48
-
-
Save MikuroXina/019c009b7c2a7c1c75af1082f38f6b85 to your computer and use it in GitHub Desktop.
MVCD で三目並べ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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