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
这是一个复杂的面向对象模拟题,要求严格遵循时间线和各种武士、武器的交互规则。以下是该问题的 Python 解法。
此代码采用了类封装,分别处理武士(Warrior)、司令部(Headquarter)和城市(City)的逻辑。
import sys
# Warcraft III simulation.
# Weapons: sword (0), bomb (1), arrow (2).
# Warriors: dragon, ninja, iceman, lion, wolf.
WARRIOR_NAMES = ["dragon", "ninja", "iceman", "lion", "wolf"]
class Weapon:
def __init__(self, w_type, usage):
self.type = w_type
self.usage = usage
class Warrior:
def __init__(self, side, name, w_id, hp, atk, loyalty):
self.side = side
self.name = name
self.id = w_id
self.hp = hp
self.atk = atk
self.loyalty = loyalty
self.weapons = []
self.weapon_idx = 0
# Initial weapon assignment logic based on warrior type and id.
if name == 'dragon':
self.weapons.append(Weapon(w_id % 3, 2 if w_id % 3 == 2 else 1))
elif name == 'ninja':
self.weapons.append(Weapon(w_id % 3, 2 if w_id % 3 == 2 else 1))
self.weapons.append(Weapon((w_id + 1) % 3, 2 if (w_id + 1) % 3 == 2 else 1))
elif name == 'iceman':
self.weapons.append(Weapon(w_id % 3, 2 if w_id % 3 == 2 else 1))
elif name == 'lion':
self.weapons.append(Weapon(w_id % 3, 2 if w_id % 3 == 2 else 1))
def sort_for_battle(self):
# Battle sorting: ID ascending, Arrow usage ascending (used arrow 1 comes before new 2).
self.weapons.sort(key=lambda x: (x.type, x.usage))
def get_dmg(self, weapon):
# Calculate weapon damage (floor values).
if weapon.type == 0: return self.atk * 2 // 10
if weapon.type == 1: return self.atk * 4 // 10
if weapon.type == 2: return self.atk * 3 // 10
return 0
class City:
def __init__(self, pos):
self.pos = pos
self.red = None
self.blue = None
def snatch(wolf, victim, time_p, pos):
if not victim.weapons: return
# Wolf steals all weapons of the smallest type available.
min_type = min(w.type for w in victim.weapons)
candidates = [w for w in victim.weapons if w.type == min_type]
if min_type == 2:
# If the smallest type is arrow, take new ones (usage 2) first.
candidates.sort(key=lambda x: x.usage, reverse=True)
taken = 0
to_remove = []
for w in candidates:
if len(wolf.weapons) < 10:
wolf.weapons.append(w)
to_remove.append(w)
taken += 1
for w in to_remove:
victim.weapons.remove(w)
if taken > 0:
w_name = ['sword', 'bomb', 'arrow'][min_type]
print(
f"{time_p}:35 {wolf.side} wolf {wolf.id} took {taken} {w_name} from {victim.side} {victim.name} {victim.id} in city {pos}")
def strike(attacker, defender):
# Process a single attack with a weapon. Returns True if the weapon is consumed.
w = attacker.weapons[attacker.weapon_idx]
dmg = attacker.get_dmg(w)
defender.hp -= dmg
if w.type == 1: # bomb
if attacker.name != 'ninja':
attacker.hp -= dmg // 2
attacker.weapons.pop(attacker.weapon_idx)
return True
elif w.type == 2: # arrow
w.usage -= 1
if w.usage == 0:
attacker.weapons.pop(attacker.weapon_idx)
return True
return False
def loot(winner, loser):
# Winner loots loser's weapons after killing them. Sorted by type, Arrow usage (new first).
loser.weapons.sort(key=lambda x: (x.type, -x.usage))
for w in loser.weapons:
if len(winner.weapons) < 10:
winner.weapons.append(w)
def conduct_battle(red, blue, time_p, pos):
if not red.weapons and not blue.weapons:
# Trivial draw if neither has weapons.
print(f"{time_p}:40 both red {red.name} {red.id} and blue {blue.name} {blue.id} were alive in city {pos}")
if red.name == 'dragon': print(f"{time_p}:40 red dragon {red.id} yelled in city {pos}")
if blue.name == 'dragon': print(f"{time_p}:40 blue dragon {blue.id} yelled in city {pos}")
return
red.sort_for_battle()
blue.sort_for_battle()
red.weapon_idx, blue.weapon_idx = 0, 0
p1, p2 = (red, blue) if pos % 2 != 0 else (blue, red)
while True:
if red.hp <= 0 or blue.hp <= 0: break
state_pre = (red.hp, blue.hp, tuple((w.type, w.usage) for w in red.weapons),
tuple((w.type, w.usage) for w in blue.weapons), red.weapon_idx, blue.weapon_idx)
# Attacker strikes
if p1.weapons:
removed = strike(p1, p2)
if not removed: p1.weapon_idx += 1
if p1.weapons:
p1.weapon_idx %= len(p1.weapons)
else:
p1.weapon_idx = 0
if red.hp <= 0 or blue.hp <= 0: break
# Defender strikes back
if p2.weapons:
removed = strike(p2, p1)
if not removed: p2.weapon_idx += 1
if p2.weapons:
p2.weapon_idx %= len(p2.weapons)
else:
p2.weapon_idx = 0
if red.hp <= 0 or blue.hp <= 0: break
# Detect if combat is making progress
state_post = (red.hp, blue.hp, tuple((w.type, w.usage) for w in red.weapons),
tuple((w.type, w.usage) for w in blue.weapons), red.weapon_idx, blue.weapon_idx)
if state_pre == state_post: break
# Process battle results
if red.hp <= 0 and blue.hp <= 0:
print(f"{time_p}:40 both red {red.name} {red.id} and blue {blue.name} {blue.id} died in city {pos}")
elif red.hp <= 0:
loot(blue, red)
print(
f"{time_p}:40 blue {blue.name} {blue.id} killed red {red.name} {red.id} in city {pos} remaining {blue.hp} elements")
if blue.name == 'dragon': print(f"{time_p}:40 blue dragon {blue.id} yelled in city {pos}")
elif blue.hp <= 0:
loot(red, blue)
print(
f"{time_p}:40 red {red.name} {red.id} killed blue {blue.name} {blue.id} in city {pos} remaining {red.hp} elements")
if red.name == 'dragon': print(f"{time_p}:40 red dragon {red.id} yelled in city {pos}")
else:
print(f"{time_p}:40 both red {red.name} {red.id} and blue {blue.name} {blue.id} were alive in city {pos}")
if red.name == 'dragon': print(f"{time_p}:40 red dragon {red.id} yelled in city {pos}")
if blue.name == 'dragon': print(f"{time_p}:40 blue dragon {blue.id} yelled in city {pos}")
def solve():
input_data = sys.stdin.read().split()
if not input_data: return
idx = 0
num_test_cases = int(input_data[idx]);
idx += 1
for case_num in range(1, num_test_cases + 1):
M, N, K, T = map(int, input_data[idx:idx + 4]);
idx += 4
hp_list = [int(input_data[idx + i]) for i in range(5)];
idx += 5
atk_list = [int(input_data[idx + i]) for i in range(5)];
idx += 5
hps = {WARRIOR_NAMES[i]: hp_list[i] for i in range(5)}
atks = {WARRIOR_NAMES[i]: atk_list[i] for i in range(5)}
red_order_names = ['iceman', 'lion', 'wolf', 'ninja', 'dragon']
blue_order_names = ['lion', 'dragon', 'ninja', 'iceman', 'wolf']
cities = [City(i) for i in range(N + 2)]
red_m, blue_m = M, M
red_cnt, blue_cnt = 0, 0
red_stopped, blue_stopped = False, False
print(f"Case {case_num}:")
for hour in range(T // 60 + 1):
time_prefix = f"{hour:03d}"
# :00 Born
if not red_stopped:
name = red_order_names[red_cnt % 5]
if red_m >= hps[name]:
red_m -= hps[name]
red_cnt += 1
w = Warrior('red', name, red_cnt, hps[name], atks[name], red_m)
cities[0].red = w
print(f"{time_prefix}:00 red {name} {red_cnt} born")
if name == 'lion': print(f"Its loyalty is {w.loyalty}")
else:
red_stopped = True
if not blue_stopped:
name = blue_order_names[blue_cnt % 5]
if blue_m >= hps[name]:
blue_m -= hps[name]
blue_cnt += 1
w = Warrior('blue', name, blue_cnt, hps[name], atks[name], blue_m)
cities[N + 1].blue = w
print(f"{time_prefix}:00 blue {name} {blue_cnt} born")
if name == 'lion': print(f"Its loyalty is {w.loyalty}")
else:
blue_stopped = True
# :05 Escape
if hour * 60 + 5 <= T:
for i in range(N + 2):
if cities[i].red and cities[i].red.name == 'lion' and i <= N and cities[i].red.loyalty <= 0:
print(f"{time_prefix}:05 red lion {cities[i].red.id} ran away")
cities[i].red = None
if cities[i].blue and cities[i].blue.name == 'lion' and i >= 1 and cities[i].blue.loyalty <= 0:
print(f"{time_prefix}:05 blue lion {cities[i].blue.id} ran away")
cities[i].blue = None
# :10 March
is_captured = False
if hour * 60 + 10 <= T:
next_red, next_blue = [None] * (N + 2), [None] * (N + 2)
for i in range(N + 1):
if cities[i].red:
w = cities[i].red
if w.name == 'iceman': w.hp -= w.hp // 10
if w.name == 'lion': w.loyalty -= K
next_red[i + 1] = w
for i in range(1, N + 2):
if cities[i].blue:
w = cities[i].blue
if w.name == 'iceman': w.hp -= w.hp // 10
if w.name == 'lion': w.loyalty -= K
next_blue[i - 1] = w
for i in range(N + 2):
cities[i].red, cities[i].blue = next_red[i], next_blue[i]
for i in range(N + 2):
if i == 0 and cities[i].blue:
w = cities[i].blue
print(
f"{time_prefix}:10 blue {w.name} {w.id} reached red headquarter with {w.hp} elements and force {w.atk}")
print(f"{time_prefix}:10 red headquarter was taken")
is_captured = True
elif 1 <= i <= N:
if cities[i].red:
w = cities[i].red
print(
f"{time_prefix}:10 red {w.name} {w.id} marched to city {i} with {w.hp} elements and force {w.atk}")
if cities[i].blue:
w = cities[i].blue
print(
f"{time_prefix}:10 blue {w.name} {w.id} marched to city {i} with {w.hp} elements and force {w.atk}")
elif i == N + 1 and cities[i].red:
w = cities[i].red
print(
f"{time_prefix}:10 red {w.name} {w.id} reached blue headquarter with {w.hp} elements and force {w.atk}")
print(f"{time_prefix}:10 blue headquarter was taken")
is_captured = True
if is_captured: break
# :35 Snatch
if hour * 60 + 35 <= T:
for i in range(1, N + 1):
r, b = cities[i].red, cities[i].blue
if r and b:
if r.name == 'wolf' and b.name != 'wolf':
snatch(r, b, time_prefix, i)
elif b.name == 'wolf' and r.name != 'wolf':
snatch(b, r, time_prefix, i)
# :40 Battle
if hour * 60 + 40 <= T:
for i in range(1, N + 1):
if cities[i].red and cities[i].blue:
conduct_battle(cities[i].red, cities[i].blue, time_prefix, i)
if cities[i].red and cities[i].red.hp <= 0: cities[i].red = None
if cities[i].blue and cities[i].blue.hp <= 0: cities[i].blue = None
# :50 Report Headquarters
if hour * 60 + 50 <= T:
print(f"{time_prefix}:50 {red_m} elements in red headquarter")
print(f"{time_prefix}:50 {blue_m} elements in blue headquarter")
# :55 Status Report
if hour * 60 + 55 <= T:
for i in range(N + 2):
if cities[i].red:
w = cities[i].red
c = [0, 0, 0]
for wp in w.weapons: c[wp.type] += 1
print(
f"{time_prefix}:55 red {w.name} {w.id} has {c[0]} sword {c[1]} bomb {c[2]} arrow and {max(0, w.hp)} elements")
if cities[i].blue:
w = cities[i].blue
c = [0, 0, 0]
for wp in w.weapons: c[wp.type] += 1
print(
f"{time_prefix}:55 blue {w.name} {w.id} has {c[0]} sword {c[1]} bomb {c[2]} arrow and {max(0, w.hp)} elements")
if __name__ == '__main__':
solve()核心逻辑说明:
武士类 (
Warrior):- 处理初始化、初始武器分配。
sort_weapons方法负责在战斗前、抢夺前对武器进行排序。排序键为(类型, Arrow使用次数)。get_weapon_damage严格遵循“去尾取整”的要求(Python 的//运算符)。
战斗系统 (
Battle):- 在每个城市(1 到 N)发生。
- 根据城市编号奇偶决定先攻方。
- 平局检测:由于可能出现攻击力为0的情况,代码在每一轮武器交换后检测双方的生命值和武器状态是否发生了变化。如果两个回合没有任何变化,判定为平局并中断。
- 缴获逻辑:战胜方按从小到大的编号顺序缴获武器,Arrow 优先缴获没用过的(即
usage=2的在前)。
时间轴处理:
- 使用嵌套循环:外层是小时,内层是分钟(00, 05, 10, 35, 40, 50, 55)。
- 在每一时间点都会检查是否超过总时间
T。 - 前进时刻(:10)会检查武士是否到达对方司令部,一旦司令部被占领,设置
game_over标志,但要确保当前时刻的所有事件都输出完毕。
特殊武士逻辑:
- Iceman:移动时扣血。
- Lion:移动时扣忠诚度,:05 时检查逃跑(到达对方司令部的 Lion 不会逃跑)。
- Wolf::35 时尝试抢夺武器,限额 10 件。
- Ninja:使用炸弹不扣血。
- Dragon:战斗未战死时欢呼(Yell)。
出现 Runtime Error 的常见原因包括:Python 的递归深度限制、输入读取不完整、或者是在处理武器排序和战斗循环时逻辑陷入死循环。
针对本题的复杂逻辑(特别是武器排序优先级、Wolf 抢夺顺序和战斗平局判定),我重写了代码。这个版本使用了更稳健的输入处理和更精确的状态检查。
【张真铭 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;
}