T16926: 魔兽世界三(开战)
OOP implementation, http://cs101.openjudge.cn/practice/16926/
魔兽世界的西面是红魔军的司令部,东面是蓝魔军的司令部。两个司令部之间是依次排列的若干城市,城市从西向东依次编号为1,2,3 .... N ( N <= 20)。红魔军的司令部算作编号为0的城市,蓝魔军的司令部算作编号为N+1的城市。司令部有生命元,用于制造武士。
两军的司令部都会制造武士。武士一共有dragon 、ninja、iceman、lion、wolf 五种。每种武士都有编号、生命值、攻击力这三种属性。
双方的武士编号都是从1开始计算。红方制造出来的第n 个武士,编号就是n。同样,蓝方制造出来的第n 个武士,编号也是n。
武士在刚降生的时候有一个初始的生命值,生命值在战斗中会发生变化,如果生命值减少到0(生命值变为负数时应当做变为0处理),则武士死亡(消失)。
武士可以拥有武器。武器有三种,sword, bomb,和arrow,编号分别为0,1,2。
sword的攻击力是使用者当前攻击力的20%(去尾取整)。
bomb的攻击力是使用者当前攻击力的40%(去尾取整),但是也会导致使用者受到攻击,对使用者的攻击力是对敌人取整后的攻击力的1/2(去尾取整)。Bomb一旦使用就没了。
arrow的攻击力是使用者当前攻击力的30%(去尾取整)。一个arrow用两次就没了。
武士降生后就朝对方司令部走,在经过的城市如果遇到敌人(同一时刻每个城市最多只可能有1个蓝武士和一个红武士),就会发生战斗。战斗的规则是:
- 在奇数编号城市,红武士先发起攻击
- 在偶数编号城市,蓝武士先发起攻击
- 战斗开始前,双方先对自己的武器排好使用顺序,然后再一件一件地按顺序使用。编号小的武器,排在前面。若有多支arrow,用过的排在前面。排好序后,攻击者按此排序依次对敌人一件一件地使用武器。如果一种武器有多件,那就都要用上。每使用一件武器,被攻击者生命值要减去武器攻击力。如果任何一方生命值减为0或小于0即为死去。有一方死去,则战斗结束。
- 双方轮流使用武器,甲用过一件,就轮到乙用。某一方把自己所有的武器都用过一轮后,就从头开始再用一轮。如果某一方没有武器了,那就挨打直到死去或敌人武器用完。武器排序只在战斗前进行,战斗中不会重新排序。
- 如果双方武器都用完且都还活着,则战斗以平局结束。如果双方都死了,也算平局。
- 有可能由于武士自身攻击力太低,而导致武器攻击力为0。攻击力为0的武器也要使用。如果战斗中双方的生命值和武器的状态都不再发生变化,则战斗结束,算平局。
- 战斗的胜方获得对方手里的武器。武士手里武器总数不超过10件。缴获武器时,按照武器种类编号从小到大缴获。如果有多件arrow,优先缴获没用过的。
- 如果战斗开始前双方都没有武器,则战斗视为平局。如果先攻击方没有武器,则由后攻击方攻击。
不同的武士有不同的特点。
编号为n的dragon降生时即获得编号为n%3 的武器。dragon在战斗结束后,如果还没有战死,就会欢呼。
编号为n的ninjia降生时即获得编号为n%3 和(n+1)%3的武器。ninja 使用bomb不会让自己受伤。
编号为n的iceman降生时即获得编号为n%3 的武器。iceman每前进一步,生命值减少10%(减少的量要去尾取整)。
编号为n的lion降生时即获得编号为n%3 的武器。lion 有“忠诚度”这个属性,其初始值等于它降生之后其司令部剩余生命元的数目。每前进一步忠诚度就降低K。忠诚度降至0或0以下,则该lion逃离战场,永远消失。但是已经到达敌人司令部的lion不会逃跑。lion在己方司令部可能逃跑。
wolf降生时没有武器,但是在战斗开始前会抢到敌人编号最小的那种武器。如果敌人有多件这样的武器,则全部抢来。Wolf手里武器也不能超过10件。如果敌人arrow太多没法都抢来,那就先抢没用过的。如果敌人也是wolf,则不抢武器。
以下是不同时间会发生的不同事件:
在每个整点,即每个小时的第0分, 双方的司令部中各有一个武士降生。
红方司令部按照iceman、lion、wolf、ninja、dragon 的顺序制造武士。
蓝方司令部按照lion、dragon、ninja、iceman、wolf 的顺序制造武士。
制造武士需要生命元。
制造一个初始生命值为m 的武士,司令部中的生命元就要减少m 个。
如果司令部中的生命元不足以制造某本该造的武士,那就从此停止制造武士。
在每个小时的第5分,该逃跑的lion就在这一时刻逃跑了。
在每个小时的第10分:所有的武士朝敌人司令部方向前进一步。即从己方司令部走到相邻城市,或从一个城市走到下一个城市。或从和敌军司令部相邻的城市到达敌军司令部。
在每个小时的第35分:在有wolf及其敌人的城市,wolf要抢夺对方的武器。
在每个小时的第40分:在有两个武士的城市,会发生战斗。
在每个小时的第50分,司令部报告它拥有的生命元数量。
在每个小时的第55分,每个武士报告其拥有的武器情况。
武士到达对方司令部后就算完成任务了,从此就呆在那里无所事事。
任何一方的司令部里若是出现了敌人,则认为该司令部已被敌人占领。
任何一方的司令部被敌人占领,则战争结束。战争结束之后就不会发生任何事情了。
给定一个时间,要求你将从0点0分开始到此时间为止的所有事件按顺序输出。事件及其对应的输出样例如下:
- 武士降生
输出样例:000:00 blue dragon 1 born
表示在0点0分,编号为1的蓝魔dragon武士降生
如果造出的是lion,那么还要多输出一行,例:
000:00 blue lion 1 born
Its loyalty is 24
表示该lion降生时的忠诚度是24
- lion逃跑
输出样例:000:05 blue lion 1 ran away
表示在0点5分,编号为1的蓝魔lion武士逃走
- 武士前进到某一城市
输出样例:
000:10 red iceman 1 marched to city 1 with 20 elements and force 30
表示在0点10分,红魔1号武士iceman前进到1号城市,此时他生命值为20,攻击力为30
对于iceman,输出的生命值应该是变化后的数值
- wolf抢敌人的武器
000:35 blue wolf 2 took 3 bomb from red dragon 2 in city 4
表示在0点35分,4号城市中,红魔1号武士wolf 抢走蓝魔2号武士dragon 3个bomb。为简单起见,武器不写复数形式
- 报告战斗情况
战斗只有3种可能的输出结果:
000:40 red iceman 1 killed blue lion 12 in city 2 remaining 20 elements
表示在0点40分,1号城市中,红魔1号武士iceman 杀死蓝魔12号武士lion后,剩下生命值20
000:40 both red iceman 1 and blue lion 12 died in city 2
注意,把红武士写前面
000:40 both red iceman 1 and blue lion 12 were alive in city 2
注意,把红武士写前面
- 武士欢呼
输出样例:003:40 blue dragon 2 yelled in city 4
- 武士抵达敌军司令部
输出样例:001:10 red iceman 1 reached blue headquarter with 20 elements and force 30
(此时他生命值为20,攻击力为30)对于iceman,输出的生命值和攻击力应该是变化后的数值
- 司令部被占领
输出样例:003:10 blue headquarter was taken
9)司令部报告生命元数量
000:50 100 elements in red headquarter
000:50 120 elements in blue headquarter
表示在0点50分,红方司令部有100个生命元,蓝方有120个
10)武士报告情况
000:55 blue wolf 2 has 2 sword 3 bomb 0 arrow and 7 elements
为简单起见,武器都不写复数形式。elements一律写复数,哪怕只有1个
交代武器情况时,次序依次是:sword,bomb, arrow。
输出事件时:
首先按时间顺序输出;
同一时间发生的事件,按发生地点从西向东依次输出. 武士前进的事件, 算是发生在目的地。
在一次战斗中有可能发生上面的 5 至 6 号事件。这些事件都算同时发生,其时间就是战斗开始时间。一次战斗中的这些事件,序号小的应该先输出。
两个武士同时抵达同一城市,则先输出红武士的前进事件,后输出蓝武士的。
对于同一城市,同一时间发生的事情,先输出红方的,后输出蓝方的。
显然,8号事件发生之前的一瞬间一定发生了7号事件。输出时,这两件事算同一时间发生,但是应先输出7号事件
虽然任何一方的司令部被占领之后,就不会有任何事情发生了。但和司令部被占领同时发生的事件,全都要输出。
输入
第一行是t,代表测试数据组数
每组样例共三行。
第一行,4个整数 M,N,K, T。其含义为: 每个司令部一开始都有M个生命元( 1 <= M <= 100000) 两个司令部之间一共有N个城市( 1 <= N <= 20 ) lion每前进一步,忠诚度就降低K。(0<=K<=100) 要求输出从0时0分开始,到时间T为止(包括T) 的所有事件。T以分钟为单位,0 <= T <= 6000
第二行:五个整数,依次是 dragon 、ninja、iceman、lion、wolf 的初始生命值。它们都大于0小于等于200
第三行:五个整数,依次是 dragon 、ninja、iceman、lion、wolf 的攻击力。它们都大于0小于等于200
输出
对每组数据,先输出一行:
Case n:
如对第一组数据就输出 Case 1:
然后按恰当的顺序和格式输出到时间T为止发生的所有事件。每个事件都以事件发生的时间开头,时间格式是“时: 分”,“时”有三位,“分”有两位。
样例输入
1
20 1 10 400
20 20 30 10 20
5 5 5 5 5样例输出
Case 1:
000:00 blue lion 1 born
Its loyalty is 10
000:10 blue lion 1 marched to city 1 with 10 elements and force 5
000:50 20 elements in red headquarter
000:50 10 elements in blue headquarter
000:55 blue lion 1 has 0 sword 1 bomb 0 arrow and 10 elements
001:05 blue lion 1 ran away
001:50 20 elements in red headquarter
001:50 10 elements in blue headquarter
002:50 20 elements in red headquarter
002:50 10 elements in blue headquarter
003:50 20 elements in red headquarter
003:50 10 elements in blue headquarter
004:50 20 elements in red headquarter
004:50 10 elements in blue headquarter
005:50 20 elements in red headquarter
005:50 10 elements in blue headquarter提示
请注意浮点数精度误差问题。OJ上的编译器编译出来的可执行程序,在这方面和你电脑上执行的程序很可能会不一致。5 * 0.3 的结果,有的机器上可能是 15.00000001,去尾取整得到15,有的机器上可能是14.9999999,去尾取整后就变成14。因此,本题不要写 5 * 0.3,要写 5 * 3 / 10。
来源
Guo Wei
【张真铭 25元培】
@🗿:当暴力成为主流,人类科研的希望将不复存在……
做了一下程设魔兽三,感觉就是暴力翻译,把人话翻译成代码(
主要就是练习oop里的继承多态,其实不太需要动脑子,还有就是这种复杂的程序对代码稳健性还是有要求的
再次吐槽cout的流输出,丑的批爆,oj快点用c++23吧,其实我早就是std::print和std::ranges的粉丝了,至于cout,我祝他好运。
码力耗尽,一段时间之内不会写代码了,歇逼。
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <format>
#include <iostream>
#include <memory>
#include <print>
#include <ranges>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
enum class WarriorType : std::uint8_t {
DRAGON = 0,
NINJA,
ICEMAN,
LION,
WOLF,
COUNT
};
constexpr int WARRIOR_COUNT = std::to_underlying(WarriorType::COUNT);
constexpr std::array<std::string_view, WARRIOR_COUNT> WARRIOR_NAMES = {
"dragon", "ninja", "iceman", "lion", "wolf"};
constexpr std::array<WarriorType, 5> RED_ORDER = {
WarriorType::ICEMAN, WarriorType::LION, WarriorType::WOLF,
WarriorType::NINJA, WarriorType::DRAGON};
constexpr std::array<WarriorType, 5> BLUE_ORDER = {
WarriorType::LION, WarriorType::DRAGON, WarriorType::NINJA,
WarriorType::ICEMAN, WarriorType::WOLF};
enum class WeaponType : std::uint8_t { SWORD = 0, BOMB, ARROW, COUNT };
constexpr int WEAPON_COUNT = std::to_underlying(WeaponType::COUNT);
constexpr std::array<std::string_view, WEAPON_COUNT> WEAPON_NAMES = {
"sword", "bomb", "arrow"};
struct Config {
int id;
int hp;
int atk;
int res_m;
WarriorType type;
std::string_view headquarter;
};
class Warrior {
public:
explicit Warrior(const Config &cfg)
: m_id(cfg.id), m_hp(cfg.hp), m_atk(cfg.atk),
m_headquarter(cfg.headquarter), m_type(cfg.type) {}
virtual ~Warrior() = default;
void get_damage(int damage) {
m_hp -= damage;
if (m_hp < 0)
m_hp = 0;
}
[[nodiscard]] auto get_id() const noexcept -> int { return m_id; }
[[nodiscard]] auto get_hp() const noexcept -> int { return m_hp; }
[[nodiscard]] auto get_atk() const noexcept -> int { return m_atk; }
[[nodiscard]] auto get_type() const noexcept -> WarriorType { return m_type; }
[[nodiscard]] auto get_headquarter_name() const noexcept -> std::string_view {
return m_headquarter;
}
[[nodiscard]] auto get_weapons_state() const noexcept -> int {
int state = 0;
for (const auto &w : m_weapons)
state += w.usage;
return state;
}
struct WeaponInfo {
WeaponType type;
int usage;
WeaponInfo(WeaponType t, int c) : type(t), usage(c) {}
};
std::vector<WeaponInfo> m_weapons;
void before_fight() {
std::ranges::sort(m_weapons, [](const WeaponInfo &a, const WeaponInfo &b) {
if (a.type == b.type)
return a.usage < b.usage;
return std::to_underlying(a.type) < std::to_underlying(b.type);
});
}
void cleanup_weapons() {
std::erase_if(m_weapons, [](const WeaponInfo &w) { return w.usage <= 0; });
m_iter = 0;
}
void loot(Warrior *obj) {
if (!obj)
return;
int size = static_cast<std::uint8_t>(obj->m_weapons.size());
int i = 0;
for (; i < size && m_weapons.size() < m_max_weapons &&
obj->m_weapons[i].type != WeaponType::ARROW;
++i) {
m_weapons.emplace_back(obj->m_weapons[i]);
}
for (int j = size - 1; j >= i && m_weapons.size() < m_max_weapons &&
obj->m_weapons[j].type == WeaponType::ARROW;
--j) {
m_weapons.emplace_back(obj->m_weapons[j]);
}
}
void report_weapons(std::string_view time) const noexcept {
std::array<int, 3> counts{0, 0, 0};
for (const auto &w : m_weapons) {
if (w.usage > 0)
++counts[std::to_underlying(w.type)];
}
std::println("{} {} {} {} has {} sword {} bomb {} arrow and {} elements",
time, get_headquarter_name(),
WARRIOR_NAMES[std::to_underlying(get_type())], m_id, counts[0],
counts[1], counts[2], m_hp);
}
virtual void print_extra_info() const noexcept = 0;
virtual void attack(Warrior *obj) noexcept {
if (!obj || m_weapons.empty() || m_hp == 0)
return;
int n = static_cast<std::uint8_t>(m_weapons.size());
for (int i : std::views::iota(0, n)) {
auto &weapon = m_weapons[m_iter];
if (weapon.usage > 0) {
int damage = weapon_damage(weapon.type);
obj->get_damage(damage);
if (weapon.type == WeaponType::BOMB) {
if (m_type != WarriorType::NINJA) {
m_hp -= (damage / 2);
if (m_hp < 0)
m_hp = 0;
}
weapon.usage = 0;
} else if (weapon.type == WeaponType::ARROW) {
--weapon.usage;
}
m_iter = (m_iter + 1) % n;
return;
}
m_iter = (m_iter + 1) % n;
}
}
void reset_iter() { m_iter = 0; }
virtual void yell(std::string_view time, int pos) const noexcept {}
protected:
int m_id;
int m_hp;
int m_atk;
WarriorType m_type;
const int m_max_weapons = 10;
std::string_view m_headquarter;
int m_iter{0};
[[nodiscard]] auto weapon_damage(WeaponType type) const noexcept -> int {
switch (type) {
case WeaponType::ARROW:
return m_atk * 3 / 10;
case WeaponType::BOMB:
return m_atk * 4 / 10;
case WeaponType::SWORD:
return m_atk * 2 / 10;
default:
return 0;
}
}
};
class Dragon : public Warrior {
public:
explicit Dragon(const Config &cfg) : Warrior(cfg) {
m_weapons.emplace_back(
static_cast<WeaponType>(cfg.id % 3),
static_cast<WeaponType>(cfg.id % 3) == WeaponType::ARROW ? 2 : 1);
}
void yell(std::string_view time, int pos) const noexcept override {
std::println("{} {} dragon {} yelled in city {}", time,
get_headquarter_name(), get_id(), pos);
}
void print_extra_info() const noexcept override {}
};
class Ninja : public Warrior {
public:
explicit Ninja(const Config &cfg) : Warrior(cfg) {
m_weapons.emplace_back(
static_cast<WeaponType>(cfg.id % 3),
static_cast<WeaponType>(cfg.id % 3) == WeaponType::ARROW ? 2 : 1);
m_weapons.emplace_back(
static_cast<WeaponType>((cfg.id + 1) % 3),
static_cast<WeaponType>((cfg.id + 1) % 3) == WeaponType::ARROW ? 2 : 1);
}
void print_extra_info() const noexcept override {}
};
class Iceman : public Warrior {
public:
explicit Iceman(const Config &cfg) : Warrior(cfg) {
m_weapons.emplace_back(
static_cast<WeaponType>(cfg.id % 3),
static_cast<WeaponType>(cfg.id % 3) == WeaponType::ARROW ? 2 : 1);
}
void move_take_blood() { m_hp -= m_hp / 10; }
void print_extra_info() const noexcept override {}
};
int K;
class Lion : public Warrior {
public:
explicit Lion(const Config &cfg) : Warrior(cfg) {
m_loyalty = cfg.res_m;
m_weapons.emplace_back(
static_cast<WeaponType>(cfg.id % 3),
static_cast<WeaponType>(cfg.id % 3) == WeaponType::ARROW ? 2 : 1);
}
[[nodiscard]] auto is_loyal() const noexcept -> bool { return m_loyalty > 0; }
void down_loyalty() { m_loyalty -= m_K; }
void print_extra_info() const noexcept override {
std::println("Its loyalty is {}", m_loyalty);
}
private:
int m_loyalty;
const int m_K = K;
};
class Wolf : public Warrior {
public:
using Warrior::Warrior;
void snatch(Warrior *obj, int city_pos, std::string_view time) {
if (!obj)
return;
before_fight();
obj->before_fight();
if (!obj->m_weapons.empty() && m_weapons.size() < m_max_weapons) {
int snatch_num = 0;
auto obj_type = obj->m_weapons.front().type;
if (obj_type == WeaponType::ARROW) {
for (int i = static_cast<std::uint8_t>(obj->m_weapons.size() - 1);
i >= 0 && m_weapons.size() < m_max_weapons; --i) {
m_weapons.emplace_back(obj->m_weapons[i]);
obj->m_weapons.erase(obj->m_weapons.begin() + i);
++snatch_num;
}
} else {
while (!obj->m_weapons.empty() &&
obj->m_weapons.front().type == obj_type &&
m_weapons.size() < m_max_weapons) {
m_weapons.emplace_back(obj->m_weapons.front());
obj->m_weapons.erase(obj->m_weapons.begin());
++snatch_num;
}
}
if (snatch_num > 0) {
std::println("{} {} wolf {} took {} {} from {} {} {} in city {}", time,
m_headquarter, m_id, snatch_num,
WEAPON_NAMES[std::to_underlying(obj_type)],
obj->get_headquarter_name(),
WARRIOR_NAMES[std::to_underlying(obj->get_type())],
obj->get_id(), city_pos);
}
}
}
void print_extra_info() const noexcept override {}
};
class Headquarter {
public:
explicit Headquarter(const std::array<int, WARRIOR_COUNT> &atk, int initial_m,
const std::array<int, WARRIOR_COUNT> &hp_costs, int N)
: m_atk(atk), m_total_m(initial_m), m_costs(hp_costs), m_N(N) {}
virtual ~Headquarter() = default;
[[nodiscard]] virtual auto get_name() const noexcept -> std::string_view = 0;
[[nodiscard]] virtual auto get_order() const noexcept
-> const std::array<WarriorType, 5> & = 0;
void report_elements(std::string_view time) const {
std::println("{} {} elements in {} headquarter", time, m_total_m,
get_name());
}
[[nodiscard]] auto step(std::string_view time) noexcept
-> std::unique_ptr<Warrior> {
if (m_stopped)
return nullptr;
const auto &order = get_order();
WarriorType type = order[m_iter];
int cost = m_costs[std::to_underlying(type)];
if (m_total_m >= cost) {
m_total_m -= cost;
++m_counts[std::to_underlying(type)];
int id = ++m_total_count;
int atk = m_atk[std::to_underlying(type)];
auto warrior = create_warrior(id, type, cost, atk);
log_production(time, type);
warrior->print_extra_info();
m_iter = (m_iter + 1) % 5;
return warrior;
}
m_stopped = true;
return nullptr;
}
[[nodiscard]] auto is_stopped() const noexcept -> bool { return m_stopped; }
protected:
int m_total_m;
int m_N;
const std::array<int, WARRIOR_COUNT> &m_costs;
const std::array<int, WARRIOR_COUNT> &m_atk;
std::array<int, WARRIOR_COUNT> m_counts{0};
int m_total_count{0};
int m_iter{0};
bool m_stopped{false};
private:
void log_production(std::string_view time, WarriorType type) const {
std::println("{} {} {} {} born", time, get_name(),
WARRIOR_NAMES[std::to_underlying(type)], m_total_count);
}
[[nodiscard]] auto create_warrior(int id, WarriorType type, int cost,
const int atk) const noexcept
-> std::unique_ptr<Warrior> {
Config cfg = {.id = id,
.hp = cost,
.atk = atk,
.res_m = m_total_m,
.type = type,
.headquarter = get_name()};
switch (type) {
case WarriorType::DRAGON:
return std::make_unique<Dragon>(cfg);
case WarriorType::NINJA:
return std::make_unique<Ninja>(cfg);
case WarriorType::ICEMAN:
return std::make_unique<Iceman>(cfg);
case WarriorType::LION:
return std::make_unique<Lion>(cfg);
case WarriorType::WOLF:
return std::make_unique<Wolf>(cfg);
default:
return nullptr;
}
}
};
class RedHeadquarter : public Headquarter {
public:
using Headquarter::Headquarter;
[[nodiscard]] auto get_name() const noexcept -> std::string_view override {
return "red";
}
[[nodiscard]] auto get_order() const noexcept
-> const std::array<WarriorType, 5> & override {
return RED_ORDER;
}
};
class BlueHeadquarter : public Headquarter {
public:
using Headquarter::Headquarter;
[[nodiscard]] auto get_name() const noexcept -> std::string_view override {
return "blue";
}
[[nodiscard]] auto get_order() const noexcept
-> const std::array<WarriorType, 5> & override {
return BLUE_ORDER;
}
};
class City {
public:
explicit City(int i) : m_pos(i) {}
[[nodiscard]] auto get_red() const noexcept -> Warrior * {
return m_red_warrior.get();
}
[[nodiscard]] auto get_blue() const noexcept -> Warrior * {
return m_blue_warrior.get();
}
[[nodiscard]] auto red_leaves() -> std::unique_ptr<Warrior> {
return std::move(m_red_warrior);
}
[[nodiscard]] auto blue_leaves() -> std::unique_ptr<Warrior> {
return std::move(m_blue_warrior);
}
void produce_red(std::unique_ptr<Warrior> w) { m_red_warrior = std::move(w); }
void produce_blue(std::unique_ptr<Warrior> w) {
m_blue_warrior = std::move(w);
}
void accept_incoming_red(std::unique_ptr<Warrior> w) {
m_incoming_red = std::move(w);
}
void accept_incoming_blue(std::unique_ptr<Warrior> w) {
m_incoming_blue = std::move(w);
}
[[nodiscard]] auto is_taken() const noexcept -> bool {
return m_enemy_count >= 1;
}
void check_lion(std::string_view time, int N) {
if (m_red_warrior && m_red_warrior->get_type() == WarriorType::LION &&
m_pos < N + 1) {
auto *lion = static_cast<Lion *>(m_red_warrior.get());
if (!lion->is_loyal()) {
std::println("{} red lion {} ran away", time, lion->get_id());
m_red_warrior = nullptr;
}
}
if (m_blue_warrior && m_blue_warrior->get_type() == WarriorType::LION &&
m_pos > 0) {
auto *lion = static_cast<Lion *>(m_blue_warrior.get());
if (!lion->is_loyal()) {
std::println("{} blue lion {} ran away", time, lion->get_id());
m_blue_warrior = nullptr;
}
}
}
void commit_arrivals(std::string_view time, int N) {
if (m_incoming_red) {
m_red_warrior = std::move(m_incoming_red);
if (m_red_warrior->get_type() == WarriorType::ICEMAN)
static_cast<Iceman *>(m_red_warrior.get())->move_take_blood();
else if (m_red_warrior->get_type() == WarriorType::LION)
static_cast<Lion *>(m_red_warrior.get())->down_loyalty();
std::print("{} red {} {} ", time,
WARRIOR_NAMES[std::to_underlying(m_red_warrior->get_type())],
m_red_warrior->get_id());
if (m_pos == N + 1) {
std::println("reached blue headquarter with {} elements and force {}",
m_red_warrior->get_hp(), m_red_warrior->get_atk());
++m_enemy_count;
if (is_taken())
std::println("{} blue headquarter was taken", time);
} else {
std::println("marched to city {} with {} elements and force {}", m_pos,
m_red_warrior->get_hp(), m_red_warrior->get_atk());
}
}
if (m_incoming_blue) {
m_blue_warrior = std::move(m_incoming_blue);
if (m_blue_warrior->get_type() == WarriorType::ICEMAN)
static_cast<Iceman *>(m_blue_warrior.get())->move_take_blood();
else if (m_blue_warrior->get_type() == WarriorType::LION)
static_cast<Lion *>(m_blue_warrior.get())->down_loyalty();
std::print("{} blue {} {} ", time,
WARRIOR_NAMES[std::to_underlying(m_blue_warrior->get_type())],
m_blue_warrior->get_id());
if (m_pos == 0) {
std::println("reached red headquarter with {} elements and force {}",
m_blue_warrior->get_hp(), m_blue_warrior->get_atk());
++m_enemy_count;
if (is_taken())
std::println("{} red headquarter was taken", time);
} else {
std::println("marched to city {} with {} elements and force {}", m_pos,
m_blue_warrior->get_hp(), m_blue_warrior->get_atk());
}
}
}
void wolf_snatch(std::string_view time) {
if (!m_red_warrior || !m_blue_warrior)
return;
bool red_is_wolf = (m_red_warrior->get_type() == WarriorType::WOLF);
bool blue_is_wolf = (m_blue_warrior->get_type() == WarriorType::WOLF);
if (red_is_wolf && !blue_is_wolf)
static_cast<Wolf *>(m_red_warrior.get())
->snatch(m_blue_warrior.get(), m_pos, time);
else if (!red_is_wolf && blue_is_wolf)
static_cast<Wolf *>(m_blue_warrior.get())
->snatch(m_red_warrior.get(), m_pos, time);
}
void fight(std::string_view time) {
if (!m_red_warrior || !m_blue_warrior)
return;
bool both_no_weapons =
m_red_warrior->m_weapons.empty() && m_blue_warrior->m_weapons.empty();
if (!both_no_weapons) {
m_red_warrior->before_fight();
m_blue_warrior->before_fight();
m_red_warrior->reset_iter();
m_blue_warrior->reset_iter();
Warrior *first =
(m_pos % 2 == 1) ? m_red_warrior.get() : m_blue_warrior.get();
Warrior *second =
(m_pos % 2 == 1) ? m_blue_warrior.get() : m_red_warrior.get();
int unchanged_rounds = 0;
while (true) {
int r_hp = m_red_warrior->get_hp(),
r_ws = m_red_warrior->get_weapons_state();
int b_hp = m_blue_warrior->get_hp(),
b_ws = m_blue_warrior->get_weapons_state();
first->attack(second);
if (first->get_hp() <= 0 || second->get_hp() <= 0)
break;
second->attack(first);
if (first->get_hp() <= 0 || second->get_hp() <= 0)
break;
if (r_hp == m_red_warrior->get_hp() &&
r_ws == m_red_warrior->get_weapons_state() &&
b_hp == m_blue_warrior->get_hp() &&
b_ws == m_blue_warrior->get_weapons_state()) {
unchanged_rounds++;
} else {
unchanged_rounds = 0;
}
if (unchanged_rounds >= 20)
break;
}
}
handle_result_and_logs(time);
}
void report_warrior_status(std::string_view time) const {
if (m_red_warrior)
m_red_warrior->report_weapons(time);
if (m_blue_warrior)
m_blue_warrior->report_weapons(time);
}
private:
int m_pos;
int m_enemy_count{0};
std::unique_ptr<Warrior> m_red_warrior;
std::unique_ptr<Warrior> m_blue_warrior;
std::unique_ptr<Warrior> m_incoming_red;
std::unique_ptr<Warrior> m_incoming_blue;
void print_both_died_log(std::string_view time) const noexcept {
std::println("{} both red {} {} and blue {} {} died in city {}", time,
WARRIOR_NAMES[std::to_underlying(m_red_warrior->get_type())],
m_red_warrior->get_id(),
WARRIOR_NAMES[std::to_underlying(m_blue_warrior->get_type())],
m_blue_warrior->get_id(), m_pos);
}
void print_alive_log(std::string_view time) const noexcept {
std::println("{} both red {} {} and blue {} {} were alive in city {}", time,
WARRIOR_NAMES[std::to_underlying(m_red_warrior->get_type())],
m_red_warrior->get_id(),
WARRIOR_NAMES[std::to_underlying(m_blue_warrior->get_type())],
m_blue_warrior->get_id(), m_pos);
}
void handle_result_and_logs(std::string_view time) noexcept {
bool red_dead = (m_red_warrior->get_hp() <= 0);
bool blue_dead = (m_blue_warrior->get_hp() <= 0);
if (red_dead && blue_dead) {
print_both_died_log(time);
m_red_warrior = nullptr;
m_blue_warrior = nullptr;
} else if (red_dead) {
m_red_warrior->cleanup_weapons();
m_blue_warrior->cleanup_weapons();
m_blue_warrior->loot(m_red_warrior.get());
std::println(
"{} blue {} {} killed red {} {} in city {} remaining {} elements",
time, WARRIOR_NAMES[std::to_underlying(m_blue_warrior->get_type())],
m_blue_warrior->get_id(),
WARRIOR_NAMES[std::to_underlying(m_red_warrior->get_type())],
m_red_warrior->get_id(), m_pos, m_blue_warrior->get_hp());
m_red_warrior = nullptr;
if (m_blue_warrior->get_type() == WarriorType::DRAGON)
m_blue_warrior->yell(time, m_pos);
} else if (blue_dead) {
m_blue_warrior->cleanup_weapons();
m_red_warrior->cleanup_weapons();
m_red_warrior->loot(m_blue_warrior.get());
std::println(
"{} red {} {} killed blue {} {} in city {} remaining {} elements",
time, WARRIOR_NAMES[std::to_underlying(m_red_warrior->get_type())],
m_red_warrior->get_id(),
WARRIOR_NAMES[std::to_underlying(m_blue_warrior->get_type())],
m_blue_warrior->get_id(), m_pos, m_red_warrior->get_hp());
m_blue_warrior = nullptr;
if (m_red_warrior->get_type() == WarriorType::DRAGON)
m_red_warrior->yell(time, m_pos);
} else {
m_red_warrior->cleanup_weapons();
m_blue_warrior->cleanup_weapons();
print_alive_log(time);
if (m_red_warrior->get_type() == WarriorType::DRAGON)
m_red_warrior->yell(time, m_pos);
if (m_blue_warrior->get_type() == WarriorType::DRAGON)
m_blue_warrior->yell(time, m_pos);
}
}
};
auto main() -> int {
std::cin.tie(nullptr)->sync_with_stdio(false);
int t;
if (!(std::cin >> t))
return 0;
for (int caseIdx : std::views::iota(1, t + 1)) {
int M, N, T;
std::cin >> M >> N >> K >> T;
std::array<int, WARRIOR_COUNT> warrior_HP;
for (int &hp : warrior_HP)
std::cin >> hp;
std::array<int, WARRIOR_COUNT> warrior_ATK;
for (int &atk : warrior_ATK)
std::cin >> atk;
std::println("Case {}:", caseIdx);
std::array<std::unique_ptr<Headquarter>, 2> bases = {
std::make_unique<RedHeadquarter>(warrior_ATK, M, warrior_HP, N),
std::make_unique<BlueHeadquarter>(warrior_ATK, M, warrior_HP, N)};
std::vector<City> cities;
cities.reserve(N + 2);
for (int i : std::views::iota(0, N + 2))
cities.emplace_back(i);
int hour = 0;
bool game_over = false;
while (true) {
if (hour * 60 > T || game_over)
break;
std::string time_00 = std::format("{:03}:00", hour);
cities[0].produce_red(bases[0]->step(time_00));
cities[N + 1].produce_blue(bases[1]->step(time_00));
if (hour * 60 + 5 > T)
break;
std::string time_05 = std::format("{:03}:05", hour);
for (int i : std::views::iota(0, N + 2))
cities[i].check_lion(time_05, N);
if (hour * 60 + 10 > T)
break;
std::string time_10 = std::format("{:03}:10", hour);
for (int i : std::views::iota(0, N + 2)) {
if (cities[i].get_red() && i < N + 1)
cities[i + 1].accept_incoming_red(cities[i].red_leaves());
if (cities[i].get_blue() && i > 0)
cities[i - 1].accept_incoming_blue(cities[i].blue_leaves());
}
for (int i : std::views::iota(0, N + 2)) {
cities[i].commit_arrivals(time_10, N);
if (cities[i].is_taken())
game_over = true;
}
if (game_over)
break;
if (hour * 60 + 35 > T)
break;
std::string time_35 = std::format("{:03}:35", hour);
for (int i : std::views::iota(1, N + 1))
cities[i].wolf_snatch(time_35);
if (hour * 60 + 40 > T)
break;
std::string time_40 = std::format("{:03}:40", hour);
for (int i : std::views::iota(1, N + 1))
cities[i].fight(time_40);
if (hour * 60 + 50 > T)
break;
std::string time_50 = std::format("{:03}:50", hour);
bases[0]->report_elements(time_50);
bases[1]->report_elements(time_50);
if (hour * 60 + 55 > T)
break;
std::string time_55 = std::format("{:03}:55", hour);
for (int i : std::views::iota(0, N + 2))
cities[i].report_warrior_status(time_55);
++hour;
}
}
return 0;
}