/////////////////////////////////////////////////////////// // Проект Task6_3 /////////////////////////////////////////////////////////// // Ship.h #ifndef SHIP_H #define SHIP_H #include #include #include //#include "CyrIOS.h" // for Visual C++ 6.0 #define N 10 // размер поля для размещения флота (N * N клеток) struct Cell; typedef std::set CellSet; // множество клеток // Клетка (ячейка) на игровом поле struct Cell { Cell(int _r = 0, int _c = 0) : row(_r), col(_c) {} bool InSet(const CellSet&) const; // определяет // принадлежность клетки множеству типа CellSet bool operator<(const Cell&) const; int row; // ряд int col; // колонка }; // Прямоугольная область (размещение кораблей и их "оболочек") struct Rect { Rect() {} Rect(Cell _lt, Cell _rb) : lt(_lt), rb(_rb) { FillCset(); } void FillCset(); // наполнить cset клетками данной области bool Intersect(const CellSet& cs) const; // определить наличие // непустого пересечения прямоугольника с множеством cs Cell lt; // left-top клетка Cell rb; // right-bottom клетка CellSet cset; // множество клеток, принадлежащих прямоугольнику }; // Класс Ship (для представления корабля) class Ship { friend class UserNavy; friend class RobotNavy; public: Ship() : nDeck(0), nLiveDeck(0) {} Ship(int, std::string, Rect); protected: Rect place; // координаты размещения std::string name; // имя корабля int nDeck; // количество палуб int nLiveDeck; // количество неповрежденных палуб }; #endif /* SHIP_H */ /////////////////////////////////////////////////////////// // Ship.cpp #include #include #include "Ship.h" using namespace std; /////////////////////////////////////////////////////////// // Класс Cell bool Cell::InSet(const CellSet& cs) const { return (cs.count(Cell(row, col)) > 0); } bool Cell::operator<(const Cell& c) const { return ((row < c.row) || ((row == c.row) && (col < c.col))); } /////////////////////////////////////////////////////////// // Класс Rect void Rect::FillCset() { for (int i = lt.row; i <= rb.row; i++) for (int j = lt.col; j <= rb.col; j++) cset.insert(Cell(i, j)); } bool Rect::Intersect(const CellSet& cs) const { CellSet common_cell; set_intersection(cset.begin(), cset.end(), cs.begin(), cs.end(), inserter(common_cell, common_cell.begin())); return (common_cell.size() > 0); } /////////////////////////////////////////////////////////// // Класс Ship Ship::Ship(int _nDeck, string _name, Rect _place) : place(_place), name(_name), nDeck(_nDeck), nLiveDeck(_nDeck) {} /////////////////////////////////////////////////////////// // Navy.h #include "Ship.h" #define DECK 176 // исправная клетка-палуба #define DAMAGE 'X' // разрушенная клетка-палуба #define MISS 'o' // пустая клетка, в к-рую упал снаряд typedef unsigned char Field[N][N]; // игровое поле typedef std::map ShipMap; // словарь // ассоциаций "клетка - индекс корабля" enum CurrentState { Miss, Damage, Kill }; // результат // попадания в цель // Класс Space - информационное пространство для обмена // информацией между игроками struct Space { public: static Cell u_fire; // огонь от пользователя static Cell r_fire; // огонь от робота (компьютера) static CurrentState u_state; // состояние пользователя static CurrentState r_state; // состояние робота static int step; }; // Базовый класс Navy class Navy : public Space { public: Navy(); void AllocShip(int, int, std::string); // разместить корабль void Show() const; // показать поля ownField и enemyField int GetInt(); // ввод целого числа bool IsLive() { return (nLiveShip > 0); } // мы еще живы? Rect Shell(Rect) const; /* вернуть "оболочку" для заданногопрямоугольника (сам прямоугольник плюс пограничные клетки) */ void AddToVetoSet(Rect); // Добавить клетки прямоугольника // в множество vetoSet. protected: Ship ship[10]; // корабли флота Field ownField; // мое игровое поле Field enemyField; // игровое поле неприятеля ShipMap shipMap; // словарь ассоциаций "клетка - индекс корабля" CellSet vetoSet; // множество "запрещенных" клеток CellSet crushSet; // множество "уничтоженных" клеток int nLiveShip; // количество боеспособных кораблей }; // Класс UserNavy class UserNavy : public Navy { public: UserNavy() { Allocation(); } void Allocation(); void FireOff(); // выстрел по неприятелю void ResultAnalys(); // анализ результатов выстрела void GetFire(); // "прием" огня противника void FillDeadZone(Rect r, Field&); // заполнить // пробелами пограничные клетки для r }; // Класс RobotNavy class RobotNavy : public Navy { public: RobotNavy(); void Allocation(); void FireOff(); // выстрел по неприятелю void ResultAnalys(); // анализ результатов выстрела void GetFire(); // "прием" огня противника private: bool isCrushContinue; // предыдущий выстрел был успешным bool upEmpty; // у поврежденного корабля противника // нет "живых" клеток в верхнем направлении }; /////////////////////////////////////////////////////////// // Navy.cpp #include #include #include #include #include "Navy.h" using namespace std; Cell Space::u_fire; Cell Space::r_fire; CurrentState Space::u_state = Miss; CurrentState Space::r_state = Miss; int Space::step = 1; // Функция gap(n) возвращает строку из n пробелов string gap(int n) { return string(n, ' '); } /////////////////////////////////////////////////////////// // Класс Navy Navy::Navy() : nLiveShip(10) { // Заполняем игровые поля символом "точка" for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) { ownField[i][j] = '.'; enemyField[i][j] = '.'; } } Rect Navy::Shell(Rect r) const { Rect sh(r); sh.lt.row = (--sh.lt.row < 0) ? 0 : sh.lt.row; sh.lt.col = (--sh.lt.col < 0) ? 0 : sh.lt.col; sh.rb.row = (++sh.rb.row > (N - 1)) ? (N - 1) : sh.rb.row; sh.rb.col = (++sh.rb.col > (N - 1)) ? (N - 1) : sh.rb.col; return sh; } void Navy::AddToVetoSet(Rect r) { for (int i = r.lt.row; i <= r.rb.row; i++) for (int j = r.lt.col; j <= r.rb.col; j++) vetoSet.insert(Cell(i, j)); } void Navy::AllocShip(int indShip, int nDeck, string name) { int i, j; Cell lt, rb; // Генерация случайно размещенной начальной клетки корабля // с учетом недопустимости "пересечения" нового корабля // с множеством клеток vetoSet while(1) { lt.row = rand() % (N + 1 - nDeck); lt.col = rb.col = rand() % N; rb.row = lt.row + nDeck - 1; if (!Rect(lt, rb).Intersect(vetoSet)) break; } // Сохраняем данные о новом корабле ship[indShip] = Ship(nDeck, name, Rect(lt, rb)); // Наносим новый корабль на игровое поле (символ DECK). // Добавляем соответствующие элементы в словарь ассоциаций for (i = lt.row; i <= rb.row; i++) for (j = lt.col; j <= rb.col; j++) { ownField[i][j] = DECK; shipMap[Cell(i,j)] = indShip; } // Добавляем в множество vetoSet клетки нового корабля // вместе с пограничными клетками AddToVetoSet(Shell(Rect(lt, rb))); } void Navy::Show() const { char rowName[10] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'}; string colName("1 2 3 4 5 6 7 8 9 10"); int i, j; cout << "---------------------------------------------\n"; cout << gap(3) << "Мой флот" << gap(18) << "Флот неприятеля" <> value; if ('\n' == cin.peek()) { cin.get(); break; } else { cout << "Повторите ввод колонки (ожидается целое число):" << endl; cin.clear(); while (cin.get() != '\n') {}; } } return value; } /////////////////////////////////////////////////////////// // Класс UserNavy void UserNavy::Allocation() { srand((unsigned)time(NULL)); AllocShip(0, 4, "Авианосец 'Варяг'"); AllocShip(1, 3, "Линкор 'Муромец'"); AllocShip(2, 3, "Линкор 'Никитич'"); AllocShip(3, 2, "Крейсер 'Чудный'"); AllocShip(4, 2, "Крейсер 'Добрый'"); AllocShip(5, 2, "Крейсер 'Справедливый'"); AllocShip(6, 1, "Миноносец 'Храбрый'"); AllocShip(7, 1, "Миноносец 'Ушлый'"); AllocShip(8, 1, "Миноносец 'Проворный'"); AllocShip(9, 1, "Миноносец 'Смелый'"); vetoSet.clear(); } void UserNavy::FillDeadZone(Rect r, Field& field) { int i, j; Rect sh = Shell(r); for (i = sh.lt.row, j = sh.lt.col; j <= sh.rb.col; j++) if (sh.lt.row < r.lt.row) field[i][j] = ' '; for (i = sh.rb.row, j = sh.lt.col; j <= sh.rb.col; j++) if (sh.rb.row > r.rb.row) field[i][j] = ' '; for (j = sh.lt.col, i = sh.lt.row; i <= sh.rb.row; i++) if (sh.lt.col < r.lt.col) field[i][j] = ' '; for (j = sh.rb.col, i = sh.lt.row; i <= sh.rb.row; i++) if (sh.rb.col > r.rb.col) field[i][j] = ' '; } void UserNavy::FireOff() { string capital_letter = "ABCDEFGHIJ"; string small_letter = "abcdefghij"; unsigned char rowName; // обозначение ряда (A, B, ... , J) int colName; // обозначение колонки (1, 2, ..., 10) int row; // индекс ряда (0, 1, ... , 9) int col; // индекс колонки (0, 1, ... , 9) bool success = false; while (!success) { cin >> rowName; row = capital_letter.find(rowName); if (-1 == row) row = small_letter.find(rowName); if (-1 == row) { cout << "Ошибка. Повторите ввод.\n"; continue; } colName = GetInt(); col = colName - 1; if ((col < 0) || (col > 9)) { cout << "Ошибка. Повторите ввод.\n"; continue; } success = true; } u_fire = Cell(row, col); } void UserNavy::ResultAnalys() { // r_state - сообщение робота о результате выстрела // пользователя по клетке u_fire switch(r_state) { case Miss: enemyField[u_fire.row][u_fire.col] = MISS; break; case Damage: enemyField[u_fire.row][u_fire.col] = DAMAGE; crushSet.insert(u_fire); break; case Kill: enemyField[u_fire.row][u_fire.col] = DAMAGE; crushSet.insert(u_fire); Rect kill; kill.lt = *crushSet.begin(); kill.rb = *(--crushSet.end()); // Заполняем "обрамление" пробелами FillDeadZone(kill, enemyField); crushSet.clear(); } } void UserNavy::GetFire() { // выстрел робота - по клетке r_fire string capital_letter = "ABCDEFGHIJ"; char rowName = capital_letter[r_fire.row]; int colName = r_fire.col + 1; cout << "\nВыстрел неприятеля: " << rowName << colName << endl; if (DECK == ownField[r_fire.row][r_fire.col]) { cout << "*** Есть попадание! ***"; ownField[r_fire.row][r_fire.col] = DAMAGE; u_state = Damage; // индекс корабля, занимающего клетку r_fire int ind = shipMap[r_fire]; ship[ind].nLiveDeck--; if (!ship[ind].nLiveDeck) { u_state = Kill; cout << gap(6) << "О ужас! Погиб "<< ship[ind].name << " !!!"; nLiveShip--; Rect kill = ship[ind].place; FillDeadZone(kill, ownField); } } else { u_state = Miss; cout << "*** Мимо! ***"; ownField[r_fire.row][r_fire.col] = MISS; } cout << endl; } /////////////////////////////////////////////////////////// // Класс RobotNavy RobotNavy::RobotNavy() { Allocation(); isCrushContinue = false; upEmpty = false; } void RobotNavy::Allocation() { AllocShip(0, 4, "Авианосец 'Алькаида'"); AllocShip(1, 3, "Линкор 'БенЛаден'"); AllocShip(2, 3, "Линкор 'Хусейн'"); AllocShip(3, 2, "Крейсер 'Подлый'"); AllocShip(4, 2, "Крейсер 'Коварный'"); AllocShip(5, 2, "Крейсер 'Злой'"); AllocShip(6, 1, "Миноносец 'Гадкий'"); AllocShip(7, 1, "Миноносец 'Мерзкий'"); AllocShip(8, 1, "Миноносец 'Пакостный'"); AllocShip(9, 1, "Миноносец 'Душный'"); vetoSet.clear(); } void RobotNavy::FireOff() { Cell c, cUp; if (!isCrushContinue) { // случайный выбор координат выстрела while(1) { c.row = rand() % N; c.col = rand() % N; if (!c.InSet(vetoSet)) break; } } else { // "пляшем" от предыдущего попадания c = cUp = r_fire; cUp.row--; if ((!upEmpty) && c.row && (!cUp.InSet(vetoSet))) c.row--; else { c = *(--crushSet.end()); c.row++; } } r_fire = c; vetoSet.insert(r_fire); } void RobotNavy::ResultAnalys() { // u_state - сообщение пользователя о результате // выстрела робота по клетке r_fire switch(u_state) { case Miss: if (isCrushContinue) upEmpty = true; break; case Damage: isCrushContinue = true; crushSet.insert(r_fire); break; case Kill: isCrushContinue = false; upEmpty = false; crushSet.insert(r_fire); Rect kill; kill.lt = *crushSet.begin(); kill.rb = *(--crushSet.end()); AddToVetoSet(Shell(kill)); crushSet.clear(); } } void RobotNavy::GetFire() { // выстрел пользователя - по клетке u_fire if (DECK == ownField[u_fire.row][u_fire.col]) { cout << "*** Есть попадание! ***"; r_state = Damage; // индекс корабля, занимающего клетку u_fire int ind = shipMap[u_fire]; ship[ind].nLiveDeck--; if (!ship[ind].nLiveDeck) { r_state = Kill; cout << gap(6) << "Уничтожен " << ship[ind].name << " !!!"; nLiveShip--; } } else { r_state = Miss; cout << "*** Мимо! ***"; } cout << endl; } ////////////////////////////////////////////////////////// // Main.cpp #include #include "Navy.h" using namespace std; int main() { // Начальная позиция UserNavy userNavy; RobotNavy robotNavy; userNavy.Show(); while (userNavy.IsLive() && robotNavy.IsLive()) { // Выстрел пользователя if (Space::u_state != Miss) { cout << "пропускается...: " << endl; cin.get(); } else { userNavy.FireOff(); robotNavy.GetFire(); userNavy.ResultAnalys(); if (!robotNavy.IsLive()) { userNavy.Show(); break; } } // Выстрел робота if (Space::r_state != Miss) cout << "\nВыстрел неприятеля: пропускается..." << endl; else { robotNavy.FireOff(); userNavy.GetFire(); robotNavy.ResultAnalys(); } userNavy.Show(); } if (userNavy.IsLive()) cout << "\n:-))) Ура! Победа!!! :-)))" << endl; else { cout << "\n:-((( Увы. Непрятель оказался сильнее." << endl; cout << ":-((( Но ничего, в следующий раз мы ему покажем!!!" << endl; } cin.get(); return 0; } //-------------- конец проекта Task6_3 ---------------- //////////////////////////////////////////////////////////