groshev há 8 meses atrás
commit
cfd8cb163a
16 ficheiros alterados com 549 adições e 0 exclusões
  1. 3 0
      .gitignore
  2. 17 0
      CMakeLists.txt
  3. 50 0
      README.md
  4. BIN
      example.png
  5. 57 0
      main.cpp
  6. BIN
      map.png
  7. 45 0
      ttt_engine.cpp
  8. 63 0
      ttt_engine.h
  9. 29 0
      ttt_meta.cpp
  10. 37 0
      ttt_meta.h
  11. 94 0
      ttt_meta_session.cpp
  12. 44 0
      ttt_meta_session.h
  13. 5 0
      ttt_simple.cpp
  14. 25 0
      ttt_simple.h
  15. 47 0
      ttt_simple_session.cpp
  16. 33 0
      ttt_simple_session.h

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+.git
+.idea
+cmake-build-debug

+ 17 - 0
CMakeLists.txt

@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.25)
+project(ttt)
+
+set(CMAKE_CXX_STANDARD 14)
+
+add_executable(ttt
+        main.cpp
+        ttt_meta_session.cpp
+        ttt_meta_session.h
+        ttt_engine.cpp
+        ttt_engine.h
+        ttt_meta.cpp
+        ttt_meta.h
+        ttt_simple.cpp
+        ttt_simple.h
+        ttt_simple_session.cpp
+        ttt_simple_session.h)

+ 50 - 0
README.md

@@ -0,0 +1,50 @@
+# Мета крестики-нолики
+Проект предоставляет возможность поиграть в игру "крестики нолики" в терминале. На выбор представлено две версии игры: обычная и **мета-версия**, где в поле 3 на 3 каждая клетка представляет собой еще одно поле для игры. 
+
+![map](./map.png)
+## Начало работы
+Получить копию:
+```bash
+git clone http://gogs.ngknn.ru:3000/nositelshtanov/MetaTicTacToe
+```
+### Необходимые условия
+Для сборки проекта необходимо иметь установленный cmake на компьютере. Для windows также понадобидся visual studio.
+
+Установить [cmake](https://cmake.org/download/) на windows
+
+Установить [visual studio](https://visualstudio.microsoft.com/downloads/)
+
+
+На Linux устанавливаем cmake так (debian based):
+```bash
+sudo apt install cmake -y
+```
+### Установка
+#### Linux
+Переходим в корневой каталог проекта и вводим следующее:
+```bash
+mkdir build
+cd build/
+cmake ..
+make
+./ttt
+```
+
+![run example](./example.png)
+
+#### Windows
+```bash
+git clone http://gogs.ngknn.ru:3000/nositelshtanov/MetaTicTacToe
+cd MetaTicTacToe
+git checkout main
+mkdir build
+cd build
+cmake ..
+```
+- Затем открываем решение build/ttt.sln в visual studio
+- Запускаем проект (он сбилдится)
+- Открываем каталог build/Debug в cmd 
+- Запускаем ttt.exe
+
+## Авторы
+Грошев Илья - [gogs profile](http://gogs.ngknn.ru:3000/nositelshtanov)

BIN
example.png


+ 57 - 0
main.cpp

@@ -0,0 +1,57 @@
+//
+// Created by nositelshtanov on 11/11/2023.
+//
+
+#include "ttt_meta_session.h"
+#include "ttt_simple_session.h"
+
+#define TTT_SMPL_VARIANT "1 - play simple tic tac toe"
+#define TTT_META_VARIANT "2 - play meta (super) tic tac toe"
+#define TTT_RULES "coordinate is a number from 0 to 8:\n" \
+                  "(0) (1) (2)\n" \
+                  "(3) (4) (5)\n" \
+                  "(6) (7) (8)\n"
+
+#define TTT_GAME_KIND     "game kind:"
+#define TTT_GAME_KIND_ERR "unknown game type"
+#define TTT_ERROR         "sorry, an error was occured\n"
+
+#define SIMPLE_TTT 1
+#define META_TTT   2
+
+void print_greeting() {
+    std::cout << TTT_RULES        << std::endl;
+    std::cout << TTT_SMPL_VARIANT << std::endl;
+    std::cout << TTT_META_VARIANT << std::endl;
+}
+
+template <typename sess_type>
+void session_start() {
+    sess_type game;
+    game.play();
+}
+
+int main() {
+    print_greeting();
+
+    try {
+        int game_king;
+
+        std::cout << TTT_GAME_KIND;
+        std::cin >> game_king;
+
+        if (game_king == SIMPLE_TTT) {
+            session_start<ttt_simple_session>();
+        } else if (game_king == META_TTT) {
+            session_start<ttt_meta_session>();
+        } else {
+            std::cout << TTT_GAME_KIND_ERR << std::endl;
+            return 1;
+        }
+    } catch (const std::exception& excp) {
+        std::cout << TTT_ERROR << excp.what() << std::endl;
+        return 1;
+    }
+
+    return 0;
+}

BIN
map.png


+ 45 - 0
ttt_engine.cpp

@@ -0,0 +1,45 @@
+//
+// Created by nositelshtanov on 11/11/2023.
+//
+
+#include "ttt_engine.h"
+
+ttt_engine::ttt_engine() : map{
+        mark::nothing, mark::nothing, mark::nothing,
+        mark::nothing, mark::nothing, mark::nothing,
+        mark::nothing, mark::nothing, mark::nothing
+    },
+    winner(mark::nothing),
+    step_count(0),
+    win_combinations{
+           0, 1, 2,
+           3, 4, 5,
+           6, 7, 8,
+           0, 3, 6,
+           1, 4, 7,
+           2, 5, 8,
+           2, 4, 6,
+           0, 4, 8
+    } {}
+
+void ttt_engine::step(mark whose_step, std::size_t field_num) {
+    if (winner != mark::nothing || is_game_end()) { throw step_error{"game is end", step_error::Reason::game_already_end}; }
+    if (map[field_num] != mark::nothing) { throw step_error{step_error::Reason::the_field_is_occupied}; }
+    if (field_num > 8) { throw invalid_coords_error(); }
+
+    map[field_num] = whose_step;
+    ++step_count;
+
+    winner = who_is_winner();
+    if (winner != mark::nothing) { whose_step = mark::nothing; }
+}
+
+mark ttt_engine::who_is_winner() const {
+    for (combination comb : win_combinations) {
+        if (is_same_marks(map[comb[0]], map[comb[1]], map[comb[2]]) && map[comb[0]] != mark::nothing) {
+            return map[comb[0]];
+        }
+    }
+
+    return mark::nothing;
+}

+ 63 - 0
ttt_engine.h

@@ -0,0 +1,63 @@
+//
+// Created by nositelshtanov on 11/11/2023.
+//
+
+#ifndef TTT_TTT_ENGINE_H
+#define TTT_TTT_ENGINE_H
+
+#include <array>
+#include <stdexcept>
+
+enum class mark {
+    nothing,
+    zero,
+    cross
+};
+
+class invalid_coords_error: public std::runtime_error {
+public:
+    invalid_coords_error(): std::runtime_error("") {}
+    explicit invalid_coords_error(const char* msg): std::runtime_error(msg) {}
+};
+
+class step_error: public std::runtime_error {
+public:
+    enum Reason {
+        game_already_end,
+        the_field_is_occupied
+    };
+
+    explicit step_error(Reason reason): std::runtime_error(""), reason(reason) {}
+    step_error(const char* msg, Reason reason): std::runtime_error(msg), reason(reason) {}
+
+    Reason get_reason() const { return reason; }
+private:
+    Reason reason;
+};
+
+class ttt_engine {
+public:
+    using combination = std::array<std::size_t, 3>;
+    using ttt_map = std::array<mark, 9>;
+    const std::array<combination, 8> win_combinations;
+
+    ttt_engine();
+
+    mark get_mark(std::size_t field) const { return map[field]; }
+    mark get_winner() const { return winner; }
+
+    void step(mark whose_step, std::size_t field_num);
+
+    bool is_game_end() const { return step_count >= 9 || winner != mark::nothing; }
+
+protected:
+    static bool is_same_marks(mark m1, mark m2, mark m3) { return m1 == m2 && m1 == m3 && m2 == m3; }
+    mark who_is_winner() const;
+private:
+    ttt_map map;
+    mark winner;
+    int step_count;
+};
+
+
+#endif //TTT_TTT_ENGINE_H

+ 29 - 0
ttt_meta.cpp

@@ -0,0 +1,29 @@
+//
+// Created by nositelshtanov on 11/11/2023.
+//
+
+#include "ttt_meta.h"
+
+void ttt_meta::step(std::size_t map_number, std::size_t field) {
+    if (map_number >= map_size) { throw invalid_coords_error(); }
+    if (next_map != any_map && map_number != next_map) { throw invalid_coords_error(); }
+    if (get_winner() != mark::nothing || is_game_end()) { throw step_error{step_error::Reason::game_already_end}; }
+    if (map[map_number].get_winner() != mark::nothing || map[map_number].get_mark(field) != mark::nothing) { throw step_error{step_error::Reason::the_field_is_occupied}; }
+
+    map[map_number].step(whose_step, field);
+
+    if (map[map_number].is_game_end())
+    {
+        auto winner = map[map_number].get_winner();
+
+        if (winner != mark::nothing)
+        {
+            ttt_engine::step(whose_step, map_number);
+        }
+    }
+
+    whose_step = whose_step == mark::cross ? mark::zero : mark::cross;
+
+    if (map[field].is_game_end()) { next_map = any_map; }
+    else { next_map = field; }
+}

+ 37 - 0
ttt_meta.h

@@ -0,0 +1,37 @@
+//
+// Created by nositelshtanov on 11/11/2023.
+//
+
+#ifndef TTT_TTT_META_H
+#define TTT_TTT_META_H
+
+#include "ttt_engine.h"
+
+class ttt_meta: public ttt_engine {
+public:
+    enum {
+        map_size = 9,
+        any_map  = map_size
+    };
+
+    using ttt_meta_map = std::array<ttt_engine, map_size>;
+
+    explicit ttt_meta(mark whose_step_first) : whose_step(whose_step_first), next_map(any_map) {}
+    ttt_meta() : ttt_meta(mark::cross) {}
+
+    mark get_mark(std::size_t map_number, std::size_t field) const { return map[map_number].get_mark(field); }
+    const ttt_engine& get_mark(std::size_t map_number) const { return map[map_number]; }
+
+    mark get_whose_step() const { return whose_step; }
+
+    void step(std::size_t map_number, std::size_t field);
+
+    std::size_t get_next_map_number() const { return next_map; }
+private:
+    ttt_meta_map map;
+    mark whose_step;
+    std::size_t next_map;
+};
+
+
+#endif //TTT_TTT_META_H

+ 94 - 0
ttt_meta_session.cpp

@@ -0,0 +1,94 @@
+//
+// Created by nositelshtanov on 11/11/2023.
+//
+
+#include "ttt_meta_session.h"
+
+void ttt_meta_session::play() {
+    draw_map();
+
+    while (!game.is_game_end()) {
+        size_t x, y;
+        auto whose_step = game.get_whose_step();
+        auto map_number = game.get_next_map_number();
+
+        try {
+            if (map_number == ttt_meta::any_map) {
+                std::cout << (whose_step == mark::zero ? META_O_INVITATION_TWO_PARAMS : META_X_INVITATION_TWO_PARAMS);
+                std::cin >> x >> y;
+
+                game.step(x, y);
+            }
+            else {
+                std::cout << (whose_step == mark::zero ? META_O_INVITATION_ONE_PARAM : META_X_INVITATION_ONE_PARAM);
+                std::cin >> x;
+
+                game.step(game.get_next_map_number(), x);
+            }
+        } catch (const invalid_coords_error&) {
+            std::cout << META_INVALID_COORS_HNDL << std::endl;
+
+            continue;
+        } catch (const step_error& err) {
+            if (err.get_reason() == step_error::Reason::the_field_is_occupied) { std::cout << META_FIELD_IS_OCCUPIED_HNDL << std::endl; }
+            else { throw; }
+        }
+
+        draw_map();
+    }
+
+    auto winner = game.get_winner();
+
+    if (winner == mark::nothing) { std::cout << META_DRAW << std::endl; }
+    else if (winner == mark::cross) { std::cout << META_CROSS_WIN << std::endl; }
+    else { std::cout << META_ZERO_WIN << std::endl; }
+}
+
+void ttt_meta_session::draw_map() {
+    std::size_t i = 0;
+
+    print_devider();
+
+    while (i < ttt_meta::map_size) {
+        print_ln(i);
+
+        if ((i + 1) % 3 == 0) { print_devider(); }
+        ++i;
+    }
+}
+
+void ttt_meta_session::print_ln(std::size_t fields_row) {
+    const auto mapIdxStart   = 3 * (fields_row / 3);
+    const auto mapIdxEnd     = mapIdxStart + 3;
+    const auto fieldIdxStart = (3 * fields_row) % 9;
+    const auto fieldIdxEnd   = fieldIdxStart + 3;
+
+    std::cout << META_MAP_BORDER;
+
+    // no reason for avoid a nested loop
+    for (std::size_t i{ mapIdxStart }; i < mapIdxEnd; ++i) {
+        for (std::size_t j{ fieldIdxStart }; j < fieldIdxEnd; ++j) {
+
+            auto cur_mark = game.get_mark(i, j);
+            auto winner   = game.get_mark(i).get_winner();
+
+            if (winner == mark::nothing) {
+                std::cout << (
+                        cur_mark == mark::cross ?
+                        META_MAP_CROSS :
+                        cur_mark == mark::zero ?
+                        META_MAP_ZERO :
+                        i == game.get_next_map_number() || game.get_next_map_number() == ttt_meta::any_map ?
+                        META_MAP_MAY_SELECT:
+                        META_MAP_NOTHING
+                );
+            } else if (winner == mark::cross) {
+                std::cout << META_CROSS_FILL;
+            } else {
+                std::cout << META_ZERO_FILL;
+            }
+        }
+        std::cout << META_MAP_BORDER;
+    }
+    std::cout << std::endl;
+}

+ 44 - 0
ttt_meta_session.h

@@ -0,0 +1,44 @@
+//
+// Created by nositelshtanov on 11/11/2023.
+//
+
+#ifndef TTT_TTT_META_SESSION_H
+#define TTT_TTT_META_SESSION_H
+
+#include <iostream>
+#include "ttt_meta.h"
+
+#define META_X_INVITATION_TWO_PARAMS "X step (write two coordinates):"
+#define META_O_INVITATION_TWO_PARAMS "O step (write two coordinates):"
+#define META_X_INVITATION_ONE_PARAM  "X step (write a coordinate):"
+#define META_O_INVITATION_ONE_PARAM  "O step (write a coordinate):"
+
+#define META_INVALID_COORS_HNDL     "Invalid coordinates"
+#define META_FIELD_IS_OCCUPIED_HNDL "the field is alredy occupied"
+
+#define META_CROSS_WIN "X wins!!!"
+#define META_ZERO_WIN  "Draw!!!"
+#define META_DRAW      "Draw!!!"
+
+#define META_DEVIDER " --------- --------- --------- \n"
+#define META_MAP_BORDER     "|"
+#define META_MAP_CROSS      " X "
+#define META_MAP_ZERO       " O "
+#define META_MAP_NOTHING    "   "
+#define META_MAP_MAY_SELECT "( )"
+#define META_CROSS_FILL     "XXX"
+#define META_ZERO_FILL      "OOO"
+
+class ttt_meta_session {
+public:
+    void play();
+    void draw_map();
+private:
+    static void print_devider() { std::cout << META_DEVIDER; }
+    void print_ln(std::size_t fields_row);
+
+    ttt_meta game;
+};
+
+
+#endif //TTT_TTT_META_SESSION_H

+ 5 - 0
ttt_simple.cpp

@@ -0,0 +1,5 @@
+//
+// Created by nositelshtanov on 11/11/2023.
+//
+
+#include "ttt_simple.h"

+ 25 - 0
ttt_simple.h

@@ -0,0 +1,25 @@
+//
+// Created by nositelshtanov on 11/11/2023.
+//
+
+#ifndef TTT_TTT_SIMPLE_H
+#define TTT_TTT_SIMPLE_H
+
+#include "ttt_engine.h"
+
+class ttt_simple: public ttt_engine {
+public:
+    explicit ttt_simple(mark who_step_first) : whose_step(who_step_first) {}
+    ttt_simple() : ttt_simple(mark::cross) {}
+
+    mark get_whose_step() const { return whose_step; }
+
+    void step(std::size_t field_num) {
+        ttt_engine::step(whose_step, field_num);
+        whose_step = whose_step == mark::cross ? mark::zero : mark::cross;
+    }
+private:
+    mark whose_step;
+};
+
+#endif //TTT_TTT_SIMPLE_H

+ 47 - 0
ttt_simple_session.cpp

@@ -0,0 +1,47 @@
+//
+// Created by nositelshtanov on 11/11/2023.
+//
+
+#include <iostream>
+#include "ttt_simple_session.h"
+
+void ttt_simple_session::play() {
+    mark winner;
+
+    while ((winner = game.get_winner()) == mark::nothing && !game.is_game_end()) {
+        size_t field_num;
+        auto whose_step = game.get_whose_step();
+
+        draw_map();
+
+        if (whose_step == mark::cross) { std::cout << SMPL_INVITATION_X; }
+        else { std::cout << SMPL_INVITATION_O; }
+
+        std::cin >> field_num;
+
+        try {
+            game.step(field_num);
+        } catch (const invalid_coords_error& err) {
+            std::cout << SMPL_INVALID_COORDS_HNDL << std::endl;
+        } catch (const step_error& err) {
+            if (err.get_reason() == step_error::Reason::the_field_is_occupied) { std::cout << SMPL_FIELD_IS_OCCUPIED_HNDL << std::endl; }
+            else { throw; }
+        }
+    }
+
+    draw_map();
+
+    if (winner == mark::cross) { std::cout << SMPL_WIN_PHRASE_X << std::endl; }
+    else if (winner == mark::zero) { std::cout << SMPL_WIN_PHRASE_O << std::endl; }
+    else { std::cout << SMPL_DRAW_PHRASE << std::endl; }
+}
+
+void ttt_simple_session::draw_map() {
+    for (std::size_t i{0}; i < 9; ++i) {
+        auto cur = game.get_mark(i);
+
+        std::cout << (cur == mark::cross ? SMPL_CROSS_CELL : cur == mark::zero ? SMPL_ZERO_CELL : SMPL_NOTHING_CELL );
+
+        if ((i+1) % 3 == 0) { std::cout << std::endl; }
+    }
+}

+ 33 - 0
ttt_simple_session.h

@@ -0,0 +1,33 @@
+//
+// Created by nositelshtanov on 11/11/2023.
+//
+
+#ifndef TTT_TTT_SIMPLE_SESSION_H
+#define TTT_TTT_SIMPLE_SESSION_H
+
+#include "ttt_simple.h"
+
+#define SMPL_INVITATION_X "step X:"
+#define SMPL_INVITATION_O "step O:"
+
+#define SMPL_WIN_PHRASE_X "win X!!!"
+#define SMPL_WIN_PHRASE_O "win O!!!"
+#define SMPL_DRAW_PHRASE  "draw!!!"
+
+#define SMPL_NOTHING_CELL "( )"
+#define SMPL_CROSS_CELL   " X "
+#define SMPL_ZERO_CELL    " O "
+
+#define SMPL_INVALID_COORDS_HNDL    "Invalid coords. Try arain"
+#define SMPL_FIELD_IS_OCCUPIED_HNDL "The field is already occupied"
+
+class ttt_simple_session {
+public:
+    void play();
+    void draw_map();
+private:
+    ttt_simple game;
+};
+
+
+#endif //TTT_TTT_SIMPLE_SESSION_H