Saved searches
Use saved searches to filter your results more quickly
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.
Консольное приложение на C++ для игры в морской бой
DmT14/SeaBattle
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
Latest commit
Git stats
Files
Failed to load latest commit information.
README.md
SeaBattle — консольное приложение для игры в морской бой
Проект выполнялся в рамках курсовой работы. Цель проекта — разработка консольного приложения для игры в морской бой. Разработка велась на языке C++ с использованием IDE Visual Studio 2017. Оценка по результатам защиты — «отлично».
Данный репозиторий содержит файл с исходным кодом SeaBattle.cpp и исполняемый файл SeaBattle.exe . Для тестирования приложения Вы можете скачать исполняемый exe -файл и запустить его на своём компьютере (может появиться предупреждение от защитника Windows, но его нужно проигнорировать и всё равно запустить файл).
Скриншоты работы приложения
Далее приведены скриншоты работы приложения.
Начальное окно содержит информацию о правилах игры «Морской бой», особенностях данного приложения и авторе приложения. После прочтения пользователю необходимо нажать любую клавишу, после чего происходит переход к следующему окну.
В окне расстановки кораблей пользователю предлагается поочерёдно расставить корабли на своём поле посредством ввода координат начала и конца корабля.
После успешной расстановки кораблей пользователем происходит переход к следующему окну, в котором осуществляется игровой процесс.
По завершении игры появляется окно, в котором пользователь видит свое поле, а также поле компьютера. Если пользователь проиграл, он может посмотреть, где стояли не потопленные корабли компьютера. В этом же окне пользователь может сделать выбор: выйти из приложения или же начать новую игру.
About
Консольное приложение на C++ для игры в морской бой
Источник
морской бой на с++
Недавно написал морской бой, хоть код и рабочий ,но мне кажется ,что он плохой и в нём куча ошибок. Так как я только недавно стал изучать с++ , то сам эти ошибки не увижу. Если вам не трудно , то можете дать несколько советов по этому коду. Заранее огромное спасибо.
#include #include #include #include #include #include using namespace std; void field(string(&a2)[10][10], string(&b2)[10][10]) < cout cout cout cout cout > > void help(short& l, short& s, string(&a2)[10][10], string(&b2)[10][10]) < cout " > l; cout > s; int tu = 10; while (tu != 0) < tu = 0; if (s >9 || l > 9) < tu = 1; cout else if (s < 0 || l < 0) < tu = 1; cout > > int game(string a[10][10], string b[10][10]) < char t; short s, l; string a2[10][10]; string b2[10][10]; // заполнение масивов for (char i = 0; i < size(a2); i++) < for (char j = 0; j < size(a2); j++) < a2[i][j] = "?"; b2[i][j] = "?"; a[i][j] = "0"; b[i][j] = "0"; >> field(a2, b2); help(l, s, a2, b2); //расстановка корабля a[s][l] = "1"; a2[s][l] = '^'; system("cls"); field(a2, b2); help(l, s, a2, b2); cout > t; //расстановка корабля switch (t)< case '^' : if (s - 1 >= 0) < a[s][l] = "1"; a2[s][l] = "O"; a[s - 1][l] = "1"; a2[s - 1][l] = "^"; >break; case '>': if (l + 1 "; > break; case '= 0) < a[s][l] = "1"; a2[s][l] = "O"; a[s][l - 1] = "1"; a2[s][l - 1] = "break; case '!': if (s + 1 > system("cls"); field(a2, b2); help(l, s, a2, b2); cout > t; switch (t)< case '^': if (s - 2 >= 0) < a[s][l] = "1"; a2[s][l] = "O"; a[s - 1][l] = "1"; a2[s - 1][l] = "O"; a[s - 2][l] = "1"; a2[s - 2][l] = "^"; >break; case '>': if (l + 2 "; > break; case '= 0) < a[s][l] = "1"; a2[s][l] = "O"; a[s][l - 1] = "1"; a2[s][l - 1] = "O"; a[s][l - 2] = "1"; a2[s][l - 2] = "break; case '!': if (s + 2 > system("cls"); field(a2, b2); //бот расставляет корабли srand(time(NULL)); int TAB_BOT, LINE_BOT; TAB_BOT = rand() % 10; LINE_BOT = rand() % 10; b[TAB_BOT][LINE_BOT] = "1"; Sleep(200); int u = 0; l = 0; while (u != 1) < TAB_BOT = rand() % 10; LINE_BOT = rand() % 10; if (TAB_BOT - 1 > > Sleep(200); l = 0; while (l != 1) < TAB_BOT = rand() % 10; LINE_BOT = rand() % 10; if (TAB_BOT + 1 > > //бот закончил short bot_heat = 0, person_heat = 0; system("cls"); while (bot_heat < 6 && person_heat < 6) < field(a2, b2); cout > l; cout > s; if (b[s][l] == "1") < person_heat++; b2[s][l] = "*"; cout else < b2[s][l] = "x"; cout system("cls"); field(a2, b2); cout else < a2[TAB_BOT][LINE_BOT] = "x"; l = 0; >> > system("cls"); > if (bot_heat > person_heat) < cout else < cout return 0; > int main()
Ваш код очень сложно понять. Именование переменных явно «хромает». Называйте переменные согласно их предназначению.
2 ответа 2
- Названия переменных плохие, надо переименовать
- Для массивов лучше использовать std::array
- Лучше не использовать using namespace std;
- Лучше не использовать неявные преобразования типов (в индексах, например)
- Вместо endl лучше использовать \n
- Вместо односимвольных строк лучше использовать символы
- Вместо множества явных пробелов для форматирования лучше использовать манипуляторы
- Стоит добавить обработку ошибок, а то непонятно, что вводить игроку
добро пожаловать на Stack Overflow на русском! пожалуйста, постарайтесь оставлять чуть более развёрнутые ответы. дополнить ответ можно, нажав править
обработка ошибок это если введены неправильные координаты и ты выводишь «неверные координаты, впишите заново» ?
В коде кроме всяких мелочей (типа endl\n) есть более глобальные проблемы. Начнем с мелкого, постепенно пофиксим немного.
Первое, что бросается в глаза, это вот это
после того, как присмотришься, стает понятно, что это игровое поле и a и b — это свое и противника, но абсолютно не очевидно, где чье. Сделаем первый шаг — заведем отдельный тип для этого и добавим туда пару функций.
class Field < std::string m_field[10][10]; public: const std::string& get(int x, int y) < return m_field[x][y]; >void set(int x, int y, const std::string& v) < m_field[x][y] = x; >>;
этот класс просто заворачивает массив в себя. Многие на этом моменте скажут, мол, нужно уже на char поменять или использовать вектор векторов. Не нужно спешить, такое делается мелкими шагами.
Теперь в коде заиспользуем этот класс и посмотрим как оно будет.
И сразу видим в функции game вот такое
хм, так это же 4 массива заполняются символами. Более того, просто повезло, что размеры совпали удачно. Но это кандидат на отдельную функцию в Field.
a2.fill("?"); b2.fill("?"); a.fill("0"); b.fill("0");
void fill(const std::string& el) < for (int i = 0; i < width; i++) < for (int j = 0; j < height; j++) < m_field[i][j] = el; >> >
да, ещё не очевидно, что значат знаки вопроса, но уже понятна суть. Двигаемся дальше по функции game.
идем в сигнатуры этих функций и меняем типы.
функция help похоже ошибок не содержит из за замены типа, а вот field не повезло. Первое — там куча size(a2) . Если вдруг поле станет не квадратным — все пойдет кувырком. Но и кода там намешано куча, попробуем понять, что оно делает.
Для начала нам понадобится две функции в наш класс Field
int get_width() const < return width; >int get_height() const
и все эти size(a2) можно поменять. правда я делал это немного наугад.
теперь настало время поменять все a[i][j] на a.get(i,j) или a.set(i,j. ) .
После всех замен выясняется, что да, в массиве m_field могут хранится только символы. И не просто символы, а только определенные символы. Заведем перечисление для этого
enum class CellType < None, Type0, // 0 Type1, // 1 TypeQ, // ? TypeS, // * TypeO, // O Typeo, // o TypeU, // ^ TypeL, // < TypeR, // >TypeX, // x >;
и функцию для вывода. Она нам ещё пригодится.
std::ostream& operator<<(std::ostream& os, const CellType& c) < switch (c) < case CellType::None: os << ' '; break; case CellType::Type0: os << '0'; break; case CellType::Type1: os << '1'; break; case CellType::TypeQ: os << '?'; break; case CellType::TypeS: os << '*'; break; case CellType::TypeO: os << 'O'; break; case CellType::Typeo: os << 'o'; break; case CellType::TypeU: os << '^'; break; case CellType::TypeL: os << '<'; break; case CellType::TypeR: os << '>'; break; case CellType::TypeX: os return os; >
возникает вопрос — а почему такие странные имена? а я делал полуавтоматическую замену. я не до конца понимал суть каждого символа. Это следующий этап.
Теперь смотрим по коду и пытаемся приписать этим элементам перечисления «разумные имена».
enum class CellType < None, Type0, // 0 Ship, // 1 TypeQ, // ? Hit, // * TypeO, // O BowBottom, // o BowUp, // ^ BowLeft, // < BowRight, // >Miss, // x >;
Не все получилось, но это такое.
Теперь, если посмотреть в код, там есть ещё system(«cls») и Sleep, который немного напрягает. Давайте сделаем для этого маленький класс
class Platform < public: static void Init() < setlocale(LC_ALL, "rus"); >static void CleanScreen() < system("cls"); >static void Sleep(int s) < ::Sleep(s); >>;
На первый взгляд он выглядит странно, но на самом деле он инкапсулировал в себе все платформенно зависимое. И теперь вместе с #include можно унести в отдельный файл. Потом, позже, чуточку переделав, можно сделать этот код рабочим и под Linux/Mac или ncurce. Мелочь, а приятно.
Попутно выясняется, что функция field скорее всего должна называться show_field . Функция help на самом деле не только показывает подсказу, а и спрашивает координаты удара. get_user_input ? ask_user_about_coord ?. А по хорошему ее нужно ещё и разделить на несколько меньших.
Да, мы подобрались к самому вкусному. Функция game — она на самом деле огромная. Она должна быть порезана на куски. Более того, комментарии внутри намекают на это.
get_user_input(l, s, a2, b2); cout > t;
ой ой. мы получили координаты, а потом ещё прямо запрашиваем направление и чуть ниже заполняем наши поля. И посмотрев на это по новому, я понял, что это просто запрос одного однопалубного, одного двух палубного и одного трехпалубного. Неочевиденько.
В процессе выделения выясняется, что там есть прям готовая функция
void generate_bot_ships(Field& b)
о, значит b — это все таки от бот, а a — это игрок.
Я также заметил, что очень часто одни и теже переменные используются для разных целей. Не нужно боятся делать разные переменные для разных целей. одной такой есть переменная с именем l , которая и для пользовательского ввода используется, и для флажков.
#include #include #include #include #include #include using namespace std; class Platform < public: static void Init() < setlocale(LC_ALL, "rus"); >static void CleanScreen() < system("cls"); >static void Sleep(int s) < ::Sleep(s); >>; enum class CellType < None, Type0, // 0 Ship, // 1 TypeQ, // ? Hit, // * TypeO, // O BowBottom, // o BowUp, // ^ BowLeft, // < BowRight, // >Miss, // x >; std::ostream& operator<<(std::ostream& os, const CellType& c) < switch (c) < case CellType::None: os << ' '; break; case CellType::Type0: os << '0'; break; case CellType::Ship: os << '1'; break; case CellType::TypeQ: os << '?'; break; case CellType::Hit: os << '*'; break; case CellType::TypeO: os << 'O'; break; case CellType::BowBottom: os << 'o'; break; case CellType::BowUp: os << '^'; break; case CellType::BowLeft: os << '<'; break; case CellType::BowRight: os << '>'; break; case CellType::Miss: os return os; > class Field < static const int width = 10; static const int height = 10; CellType m_field[width][height]; public: const CellType get(int x, int y) < return m_field[x][y]; >void set(int x, int y, const CellType v) < m_field[x][y] = v; >void fill(const CellType el) < for (int i = 0; i < width; i++) < for (int j = 0; j < height; j++) < m_field[i][j] = el; >> > int get_width() const < return width; >int get_height() const < return height; >>; void show_field(Field& a2, Field& b2) < cout cout cout cout cout > > void get_user_input(short& l, short& s, Field& a2, Field& b2) < cout > l; cout > s; int tu = 10; while (tu != 0) < tu = 0; if (s >9 || l > 9) < tu = 1; cout else if (s < 0 || l < 0) < tu = 1; cout > > void ask_user_about_initial_ships(Field& a2, Field& b2, Field& a) < char t; short s, l; show_field(a2, b2); get_user_input(l, s, a2, b2); //расстановка корабля a.set(s, l, CellType::Ship); a2.set(s, l, CellType::BowUp); Platform::CleanScreen(); show_field(a2, b2); get_user_input(l, s, a2, b2); cout > t; //расстановка корабля switch (t) < case '^': if (s - 1 >= 0) < a.set(s, l, CellType::Ship); a2.set(s, l, CellType::TypeO); a.set(s - 1, l, CellType::Ship); a2.set(s - 1, l, CellType::BowUp); >break; case '>': if (l + 1 break; case '= 0) < a.set(s, l, CellType::Ship); a2.set(s, l, CellType::TypeO); a.set(s, l - 1, CellType::Ship); a2.set(s, l - 1, CellType::BowLeft); >break; case '!': if (s + 1 > Platform::CleanScreen(); show_field(a2, b2); get_user_input(l, s, a2, b2); cout > t; switch (t) < case '^': if (s - 2 >= 0) < a.set(s, l, CellType::Ship); a2.set(s, l, CellType::TypeO); a.set(s - 1, l, CellType::Ship); a2.set(s - 1, l, CellType::TypeO); a.set(s - 2, l, CellType::Ship); a2.set(s - 2, l, CellType::BowUp); >break; case '>': if (l + 2 break; case '= 0) < a.set(s, l, CellType::Ship); a2.set(s, l, CellType::TypeO); a.set(s, l - 1, CellType::Ship); a2.set(s, l - 1, CellType::TypeO); a.set(s, l - 2, CellType::Ship); a2.set(s, l - 2, CellType::BowLeft); >break; case '!': if (s + 2 > > void generate_bot_ships(Field& b) < short l; //бот расставляет корабли srand(time(NULL)); int TAB_BOT = rand() % 10; int LINE_BOT = rand() % 10; b.set(TAB_BOT, LINE_BOT, CellType::Ship); Platform::Sleep(200); int u = 0; l = 0; while (u != 1) < TAB_BOT = rand() % 10; LINE_BOT = rand() % 10; if (TAB_BOT - 1 > > Platform::Sleep(200); l = 0; while (l != 1) < TAB_BOT = rand() % 10; LINE_BOT = rand() % 10; if (TAB_BOT + 1 > > //бот закончил > int game(Field a, Field b) < //char t; short s, l; Field a2<>; Field b2<>; // заполнение масивов a2.fill(CellType::TypeQ); b2.fill(CellType::TypeQ); a.fill(CellType::Type0); b.fill(CellType::Type0); ask_user_about_initial_ships(a2, b2, a); Platform::CleanScreen(); show_field(a2, b2); generate_bot_ships(b); short bot_heat = 0, person_heat = 0; Platform::CleanScreen(); while (bot_heat < 6 && person_heat < 6) < show_field(a2, b2); cout > l; cout > s; if (b.get(s,l) == CellType::Ship) < person_heat++; b2.set(s,l, CellType::Hit); cout else < b2.set(s,l, CellType::Miss); cout Platform::CleanScreen(); show_field(a2, b2); cout else < a2.set(TAB_BOT,LINE_BOT, CellType::Miss); >need_to_continue = true; > > Platform::CleanScreen(); > if (bot_heat > person_heat) < cout else < cout return 0; > int main() < Platform::Init(); Field a<>; Field b<>; game(a, b); >
Но тут ещё работать и работать, а у меня есть и своя работа.
Источник