Задать вопрос
@Xiran

Почему не решает задачу?

Задача


Вам предстоит проэмулировать игру в Морской бой. В рамках данной задачи давайте считать, что игроки честные, корректно расставляют корабли на поле, и никогда не обманывают противника.

Игра в Морской бой проходит на клетчатом поле размером n×m (n строк и m столбцов). Строки занумерованы от 1 до n снизу вверх, столбцы — буквами слева направо. Первые 26 столбцов нумеруются от 'A' до 'Z', а если столбцов больше, то следующие 26 нумеруются от 'AA' до 'AZ'.

У каждого из двух игроков есть свое неизвестное противнику поле, на котором он расставляет корабли. Корабли представляют из себя полосы 1×x или x×1, где x от 1 до L, и расставляются на поле так, что никакие два корабля не соприкасаются по стороне, то есть если две занятые клетки имеют общую сторону, они принадлежат одному кораблю. Соприкосновение по углу разрешается. О количестве кораблей каждого типа игроки договариваются заранее, но строгих ограничений нет.

Вам дана изначальная расстановка кораблей на поле, а также последовательность ходов игроков. В течение хода игрок совершает один «выстрел»: игрок называет клетку противника, после чего противник сообщает вердикт: «мимо», если клетка свободная, «попал», если клетка занята, и «убил», если это было попадание в последнюю клетку соответствующего корабля.

В отличие от стандартных правил,

игроки ходят строго по очереди вне зависимости от вердикта их последнего хода;
у каждого игрока есть «бонус» — каждый игрок может в начале игры пометить k клеток кораблей на своем поле как «усиленные» — тогда по ним потребуется попасть дважды, чтобы стоящий на них корабль считался потопленным.

Проанализируйте запись ходов игры и выведите вердикт каждого хода и информацию о победителе, если игра на каком-то ходу завершается (все корабли одного из игроков потоплены).
Формат входных данных

Каждый тест состоит из нескольких наборов входных данных. В первой строке ввода находится одно целое число t — количество наборов входных данных (1≤t≤50). Далее следует описание наборов входных данных.

Первая строка каждого набора входных данных содержит четыре целых числа n, m, L и k — размеры поля, максимальную длину корабля и число бонусов у каждого игрока (4≤n,m≤50; 1≤L≤10; 1≤k≤30).

Следующие n строк содержат по m символов каждая и задают расстановку кораблей первого игрока. Каждый символ — это либо 'X' (клетка корабля), либо '#' (усиленная клетка корабля), либо '.' (пустая клетка). Затем следуют еще n строк, задающие поле второго игрока в том же формате.

Гарантируется, что расстановка корректная и соответствует всем правилам игры.

Следующая строка содержит одно целое число q — количество ходов в игре (1≤q≤2nm). В i-й из следующих q строк задан ход игрока в формате «FIRE r​i​​ c​i​​», где r​i​​ — номер строки (в нумерации снизу вверх), а c​i​​ — номер столбца, записанный одной или двумя буквами от 'A' до 'Z' в соответствии с описанием нумерации из условия (1≤r​i​​≤n; 'A' ≤c​i​​≤ 'AX').
Формат выходных данных

После каждого хода игры выведите на отдельной строке одну из следующих строк:

«MISSED», если ход был сделан мимо (или данная клетка уже потоплена, то есть по ней было одно попадание в случае обычной клетки корабля или два попадания в случае усиленной);
«HIT», если игрок текущим ходом задел корабль противника;
«SUNK», если игрок текущим ходом потопил корабль противника;
«WIN p», если игрок номер p (1≤p≤2) текущим ходом выиграл.

После того, как какой-то игрок выиграл, если во входных данных остались еще какие-то ходы, их следует проигнорировать и не выводить ничего (до следующего набора входных данных).

Пример:
Ввод:
1
4 4 3 1
....
...X
X...
.X#X
XXX.
...X
....
.#..
12
FIRE 3 D
FIRE 2 A
FIRE 3 C
FIRE 1 C
FIRE 4 D
FIRE 1 B
FIRE 4 A
FIRE 1 D
FIRE 2 D
FIRE 1 C
FIRE 4 B
FIRE 3 D
Вывод:
SUNK
SUNK
MISSED
HIT
MISSED
HIT
HIT
HIT
MISSED
SUNK
HIT
WIN 2


Мой код

#include <exception>
#include <ios>
#include <iostream>
#include <string>
#include <utility>
#include <vector>

struct field_t {
    field_t(int n, int m) : alive_cells_count(0), self_(n), m_(m) {
        for (auto &i : self_) {
            std::cin >> i;
        
            for (const auto j : i)
                if (j == 'X' || j == '#')
                    alive_cells_count++;
        }
    }

    std::string fire(int target_n, std::string_view target_m) {
        const auto [norm_n, norm_m] = to_normal_coords(target_n, target_m);

        if (self_.at(norm_n).at(norm_m) == '#') {
            self_.at(norm_n).at(norm_m) = 'X';
            return "HIT";
        }
        else if (self_.at(norm_n).at(norm_m) == 'X') {
            self_.at(norm_n).at(norm_m) = '$';
            alive_cells_count--;

            if (alive_cells_count == 0)
                return "GAMEOVER";

            if (has_neighbours(norm_n, norm_m))
                return "HIT";

            return "SUNK";
        }


        return "MISSED";
    }
    
private:
    std::vector<std::string> self_;
    int alive_cells_count;
    int m_;

    std::pair<int, int> to_normal_coords(int n, std::string_view m) {
        const int norm_n = -(n - self_.size());
        auto m_last = m.back();
        int norm_m = static_cast<int>(m_last - 'A');

        if (m.size() == 2)
            norm_m += 26;

        return {norm_n, norm_m};
    }

    bool has_neighbours(int norm_n, int norm_m) {
        for (int i = 1; norm_n + i < self_.size(); i++) {
            const auto neighbour = self_.at(norm_n + i).at(norm_m);
            if (neighbour == 'X' || neighbour == '#')
                return true;
            if (neighbour == '.')
                break;
        }

        for (int i = 1; norm_n - i >= 0; i++) {
            const auto neighbour = self_.at(norm_n - i).at(norm_m);
            if (neighbour == 'X' || neighbour == '#')
                return true;
            if (neighbour == '.')
                break;
    }

        for (int i = 1; norm_m + i < m_; i++) {
            const auto neighbour = self_.at(norm_n).at(norm_m + i);
            if (neighbour == 'X' || neighbour == '#')
                return true;
            if (neighbour == '.')
                break;
        }

        for (int i = 1; norm_m - i >= 0; i++) {
            const auto neighbour = self_.at(norm_n).at(norm_m - i);
            if (neighbour == 'X' || neighbour == '#')
                return true;
            if (neighbour == '.')
                break;
        }
        
        return false;
    }
};

int main() {
    int t = 0;
    std::cin >> t;

    for (; t; t--) {
        int n = 0, m = 0, unused;

    try {
        std::cin >> n >> m >> unused >> unused;

        field_t players[2]{{n, m},{n,m}};

        int curr_player = 1;

        int q;
        std::cin >> q;

        for (; q; q--) {
            bool flag = true;

            std::cin.ignore(5); 
            int target_n;
            std::string target_m;
            std::cin >> target_n >> target_m;
        
            if (flag) {
                auto res = players[curr_player].fire(target_n, target_m);

                curr_player = curr_player == 1 ? 0 : 1;

                if (res == "GAMEOVER") {
                    std::cout << "WIN " << curr_player + 1 << std::endl;
                    flag = false;
                } else
                    std::cout << res << std::endl;
            }
        }
    
    } catch (const std::exception &ex) {
        std::cout << ex.what() << ' ' << n;
    }
    }
}


Решает все правильно до 2 ого теста (проходит тест-пример), падает с ошибкой
expected 'MISSED' found 'HIT'
Почему так вообще может быть, идей нет
  • Вопрос задан
  • 157 просмотров
Подписаться 1 Простой 5 комментариев
Помогут разобраться в теме Все курсы
  • Нетология
    Разработчик на C++
    12 месяцев
    Далее
  • Skillbox
    Профессия Разработчик С++
 с нуля + ИИ
    7 месяцев
    Далее
  • Бруноям
    Разработчик на C++
    5 месяцев
    Далее
Решения вопроса 1
@Xiran Автор вопроса
Ошибка была в том, что флаг игнорирования ввода после победы одного из игроков был определен в цикле, а не до него.
Рабочее решение:
#include <exception>
#include <ios>
#include <iostream>
#include <string>
#include <utility>
#include <vector>

struct field_t {
    field_t(int n, int m) : alive_cells_count(0), self_(n), m_(m) {
        for (auto &i : self_) {
            std::cin >> i;
        
            for (const auto j : i)
                if (j == 'X' || j == '#')
                    alive_cells_count++;
        }
    }

    std::string fire(int target_n, std::string_view target_m) {
        const auto [norm_n, norm_m] = to_normal_coords(target_n, target_m);

        if (self_.at(norm_n).at(norm_m) == '#') {
            self_.at(norm_n).at(norm_m) = 'X';
            return "HIT";
        }
        else if (self_.at(norm_n).at(norm_m) == 'X') {
            self_.at(norm_n).at(norm_m) = '$';
            alive_cells_count--;

            if (alive_cells_count == 0)
                return "GAMEOVER";

            if (has_neighbours(norm_n, norm_m))
                return "HIT";

            return "SUNK";
        }


        return "MISSED";
    }
    
private:
    std::vector<std::string> self_;
    int alive_cells_count;
    int m_;

    std::pair<int, int> to_normal_coords(int n, std::string_view m) {
        const int norm_n = -(n - self_.size());
        auto m_last = m.back();
        int norm_m = static_cast<int>(m_last - 'A');

        if (m.size() == 2)
            norm_m += 26;

        return {norm_n, norm_m};
    }

    bool has_neighbours(int norm_n, int norm_m) {
        for (int i = 1; norm_n + i < self_.size(); i++) {
            const auto neighbour = self_.at(norm_n + i).at(norm_m);
            if (neighbour == 'X' || neighbour == '#')
                return true;
            if (neighbour == '.')
                break;
        }

        for (int i = 1; norm_n - i >= 0; i++) {
            const auto neighbour = self_.at(norm_n - i).at(norm_m);
            if (neighbour == 'X' || neighbour == '#')
                return true;
            if (neighbour == '.')
                break;
    }

        for (int i = 1; norm_m + i < m_; i++) {
            const auto neighbour = self_.at(norm_n).at(norm_m + i);
            if (neighbour == 'X' || neighbour == '#')
                return true;
            if (neighbour == '.')
                break;
        }

        for (int i = 1; norm_m - i >= 0; i++) {
            const auto neighbour = self_.at(norm_n).at(norm_m - i);
            if (neighbour == 'X' || neighbour == '#')
                return true;
            if (neighbour == '.')
                break;
        }
        
        return false;
    }
};

int main() {
    int t = 0;
    std::cin >> t;

    for (; t; t--) {
        int n = 0, m = 0, unused;

    try {
        std::cin >> n >> m >> unused >> unused;

        field_t players[2]{{n, m},{n,m}};

        int curr_player = 1;

        int q;
        std::cin >> q;

        bool flag = true;

        for (; q; q--) {
            std::cin.ignore(5); 
            int target_n;
            std::string target_m;
            std::cin >> target_n >> target_m;
        
            if (flag) {
                auto res = players[curr_player].fire(target_n, target_m);

                curr_player = curr_player == 1 ? 0 : 1;

                if (res == "GAMEOVER") {
                    std::cout << "WIN " << curr_player + 1 << std::endl;
                    flag = false;
                } else
                    std::cout << res << std::endl;
            }
        }
    
    } catch (const std::exception &ex) {
        std::cout << ex.what() << ' ' << n;
    }
    }
}
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы