對拍器在此。https://www.luogu.com/discuss/81283
獻忠!
AC程式碼
mod oiread {
use std::{
io::{stdin, Read},
ops::{Add, Mul, Neg},
};
pub fn next() -> u8 {
let mut a = stdin().lock();
let mut c = [0u8];
match a.read(&mut c) {
Ok(0) => b'\n', // End Of File
Ok(1) => c[0],
_ => panic!(),
}
}
pub fn get_char() -> char {
next().into()
}
pub fn read_inum<T>() -> T
where
T: Add<Output = T> + Mul<Output = T> + From<u8> + Neg<Output = T>,
{
let mut ans: T = T::from(0);
let mut flag = false;
let mut c = next();
while c < b'0' || c > b'9' {
if c == b'-' {
flag = true;
}
c = next();
}
while c >= b'0' && c <= b'9' {
ans = ans * T::from(10) + T::from(c - b'0');
c = next();
}
if flag {
-ans
} else {
ans
}
}
pub fn read_unum<T>() -> T
where
T: Add<Output = T> + Mul<Output = T> + From<u8>,
{
let mut ans: T = T::from(0);
let mut c = next();
while c < b'0' || c > b'9' {
c = next();
}
while c >= b'0' && c <= b'9' {
ans = ans * T::from(10) + T::from(c - b'0');
c = next();
}
ans
}
pub fn read_i64() -> i64 {
read_inum::<i64>()
}
pub fn read_str() -> String {
let mut res = String::new();
let mut c = next();
while c == b' ' || c == b'\n' || c == b'\r' {
c = next();
}
while c != b' ' && c != b'\n' && c != b'\r' {
res.push(c as char);
c = next();
}
res
}
pub fn read_u8s() -> Vec<u8> {
let mut res = Vec::new();
let mut c = next();
while c == b' ' || c == b'\n' || c == b'\r' {
c = next();
}
while c != b' ' && c != b'\n' && c != b'\r' {
res.push(c);
c = next();
}
res
}
}
use std::collections::VecDeque;
use std::fmt;
use std::fmt::Display;
use std::mem::swap;
use std::process::exit;
use oiread::read_i64 as read;
use oiread::read_str;
#[derive(PartialEq, Eq, Clone, Copy)]
enum BaseType {
Atk,
Def,
Peach,
}
impl From<String> for BaseType {
fn from(value: String) -> Self {
match value.as_str() {
"P" => BaseType::Peach,
"K" => BaseType::Atk,
"D" => BaseType::Def,
x => panic!("Base Card Type Error : {}", x),
}
}
}
impl Display for BaseType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match *self {
BaseType::Atk => "K",
BaseType::Def => "D",
BaseType::Peach => "P",
}
)
}
}
#[derive(PartialEq, Eq, Clone, Copy)]
enum SkillType {
Fight,
ReqAtk,
ReqDef,
Cancel,
}
impl From<String> for SkillType {
fn from(value: String) -> Self {
match value.as_str() {
"F" => SkillType::Fight,
"N" => SkillType::ReqAtk,
"W" => SkillType::ReqDef,
"J" => SkillType::Cancel,
x => panic!("Skill Card Type Error : {}", x),
}
}
}
impl Display for SkillType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match *self {
SkillType::Fight => "F",
SkillType::ReqAtk => "N",
SkillType::ReqDef => "W",
SkillType::Cancel => "J",
}
)
}
}
#[derive(PartialEq, Eq, Clone, Copy)]
enum CardType {
Base(BaseType),
Skill(SkillType),
Equip,
}
impl From<String> for CardType {
fn from(value: String) -> Self {
match value.as_str() {
"P" | "K" | "D" => CardType::Base(value.into()),
"F" | "N" | "W" | "J" => CardType::Skill(value.into()),
"Z" => CardType::Equip,
x => panic!("Card Type Error : {}", x),
}
}
}
impl Display for CardType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
CardType::Base(card) => write!(f, "{}", card),
CardType::Skill(card) => write!(f, "{}", card),
CardType::Equip => write!(f, "Z"),
}
}
}
#[cfg(feature = "debug")]
impl CardType {
fn show(&self) -> &'static str {
match *self {
CardType::Base(card) => match card {
BaseType::Atk => "殺",
BaseType::Def => "閃",
BaseType::Peach => "桃",
},
CardType::Skill(card) => match card {
SkillType::Fight => "決鬥",
SkillType::ReqAtk => "南蠻入侵",
SkillType::ReqDef => "萬箭齊發",
SkillType::Cancel => "無懈可擊",
},
CardType::Equip => "諸葛連弩",
}
}
}
#[derive(PartialEq, Debug, Clone, Copy)]
enum Identity {
MainPig,
ZhongPig,
FanPig,
}
impl From<String> for Identity {
fn from(value: String) -> Self {
match value.as_str() {
"MP" => Self::MainPig,
"ZP" => Self::ZhongPig,
"FP" => Self::FanPig,
x => panic!("Error Pig Identity: {}", x),
}
}
}
#[cfg(feature = "debug")]
impl Identity {
fn show(&self) -> &'static str {
match *self {
Identity::MainPig => "主公",
Identity::ZhongPig => "忠臣",
Identity::FanPig => "反賊",
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum ActType {
None,
Zhong,
Fan,
}
#[derive(PartialEq, Eq, Clone, Copy)]
enum State {
None,
SimFan,
Fan,
Zhong,
}
#[cfg(feature = "debug")]
impl State {
fn show(&self) -> &'static str {
match *self {
State::None | State::SimFan => " ",
State::Fan | State::Zhong => "暴露",
}
}
}
#[derive(Clone, Copy)]
struct Act {
tp: ActType,
card: CardType,
src: usize,
dst: Option<usize>,
}
impl Act {
fn new(tp: ActType, card: CardType, src: usize, dst: Option<usize>) -> Self {
Self { tp, card, src, dst }
}
}
struct Pig {
tp: Identity,
hp: isize,
pos: usize,
state: State,
cards: Vec<CardType>,
equip: bool,
alive: bool,
attacked: bool,
}
impl Pig {
const MAX_HP: isize = 4;
fn new(pos: usize) -> Self {
let tp = Identity::from(read_str());
if pos == 0 {
assert_eq!(tp, Identity::MainPig);
}
let mut cards = Vec::new();
for _ in 0..4 {
cards.push(CardType::from(read_str()));
}
Self {
tp,
hp: Self::MAX_HP,
state: State::None,
cards,
pos,
equip: false,
alive: true,
attacked: false,
}
}
fn is_known(&self) -> bool {
self.tp == Identity::MainPig || self.state == State::Zhong || self.state == State::Fan
}
fn get_card(&mut self, pool: &mut Pool, cnt: usize) {
for _ in 0..cnt {
if let Some(c) = pool.get() {
self.cards.push(c);
}
}
}
fn get_enemy(&self, players: &Vec<Pig>) -> Option<usize> {
if Identity::FanPig == self.tp {
return Some(0);
}
// [self.pos+1, self.pos+2, ... n-1, 0, 1, 2, ... self.pos-1 ]
let mut range = (0..players.len()).collect::<Vec<usize>>();
range.rotate_left(self.pos + 1);
for i in range {
if i == self.pos || !players[i].alive {
continue;
}
let pig = &players[i];
match self.tp {
Identity::MainPig => {
if let State::Fan | State::SimFan = pig.state {
return Some(i);
}
}
Identity::ZhongPig => {
if State::Fan == pig.state {
return Some(i);
}
}
Identity::FanPig => unreachable!(),
}
}
None
}
fn get_enemy_dis1(&self, players: &Vec<Pig>) -> Option<usize> {
// [self.pos+1, self.pos+2, ... n-1, 0, 1, 2, ... self.pos-1 ]
let mut range = (0..players.len()).collect::<Vec<_>>();
range.rotate_left(self.pos + 1);
for i in range {
if i == self.pos || !players[i].alive {
continue;
}
let pig = &players[i];
match self.tp {
Identity::MainPig => {
if let State::Fan | State::SimFan = pig.state {
return Some(i);
}
}
Identity::ZhongPig => {
if State::Fan == pig.state {
return Some(i);
}
}
Identity::FanPig => {
if State::Zhong == pig.state || pig.tp == Identity::MainPig {
return Some(i);
}
}
}
break;
}
None
}
fn ask(&self, asked_card: CardType) -> Option<usize> {
for (i, card) in self.cards.iter().enumerate() {
if *card == asked_card {
return Some(i);
}
}
None
}
fn try_act(&self, players: &Vec<Pig>) -> Option<(usize, Act)> {
for i in 0..self.cards.len() {
let card = &self.cards[i];
match card {
CardType::Equip => {
return Some((i, Act::new(ActType::None, CardType::Equip, self.pos, None)));
}
CardType::Base(card) => match card {
BaseType::Atk => {
if !self.equip && self.attacked {
continue;
}
let enemy = self.get_enemy_dis1(players);
if enemy.is_some() {
return Some((
i,
Act::new(
match self.tp {
Identity::MainPig => ActType::None,
Identity::ZhongPig => ActType::Zhong,
Identity::FanPig => ActType::Fan,
},
CardType::Base(BaseType::Atk),
self.pos,
enemy,
),
));
}
}
BaseType::Def => {}
BaseType::Peach => {
if self.hp != Self::MAX_HP {
return Some((
i,
Act::new(
ActType::None,
CardType::Base(BaseType::Peach),
self.pos,
Some(self.pos),
),
));
}
}
},
CardType::Skill(card) => match card {
SkillType::Fight => {
let enemy = self.get_enemy(players);
if enemy.is_some() {
return Some((
i,
Act::new(
match self.tp {
Identity::MainPig => ActType::None,
Identity::ZhongPig => ActType::Zhong,
Identity::FanPig => ActType::Fan,
},
CardType::Skill(SkillType::Fight),
self.pos,
enemy,
),
));
}
}
SkillType::ReqAtk => {
return Some((
i,
Act::new(
ActType::None,
CardType::Skill(SkillType::ReqAtk),
self.pos,
None,
),
))
}
SkillType::ReqDef => {
return Some((
i,
Act::new(
ActType::None,
CardType::Skill(SkillType::ReqDef),
self.pos,
None,
),
))
}
SkillType::Cancel => {}
},
}
}
None
}
}
fn main() {
let (n, m) = (read() as usize, read() as usize);
let mut players = Vec::new();
for i in 0..n {
players.push(Pig::new(i));
}
let mut pool = Pool::new(m);
// game start
loop {
for now_pig_id in 0..players.len() {
if !players[now_pig_id].alive {
continue;
}
// turn start
#[cfg(feature = "debug")]
println!("Begin: {} // {}", now_pig_id + 1, players[0].cards.len());
players[now_pig_id].attacked = false;
players[now_pig_id].get_card(&mut pool, 2);
#[cfg(feature = "debug")]
show(&mut players);
loop {
if !players[now_pig_id].alive {
break;
}
let act = players[now_pig_id].try_act(&players);
if act.is_none() {
break;
}
let (card_id, act) = act.unwrap();
players[now_pig_id].cards.remove(card_id);
assert_eq!(act.src, now_pig_id);
// use card
match act.tp {
ActType::None => {}
ActType::Zhong => players[now_pig_id].state = State::Zhong,
ActType::Fan => players[now_pig_id].state = State::Fan,
}
match act.card {
CardType::Base(card) => match card {
BaseType::Atk => {
assert!(act.dst.is_some());
players[now_pig_id].attacked = true;
let d = act.dst.unwrap();
let t = players[d].ask(CardType::Base(BaseType::Def));
if let Some(card_id) = t {
players[d].cards.remove(card_id);
} else {
players[d].hp -= 1;
check_dead(&mut players, &mut pool, now_pig_id, d);
}
}
BaseType::Peach => {
assert!(players[now_pig_id].hp < Pig::MAX_HP);
players[now_pig_id].hp += 1;
}
BaseType::Def => unreachable!(),
},
CardType::Skill(card) => match card {
SkillType::Fight => {
#[cfg(feature = "debug")]
println!(
"決鬥!->{} // {}",
act.dst.unwrap() + 1,
players[0].cards.len()
);
// check Cancel
assert!(act.dst.is_some());
let effect = check_cancel(act, &mut players);
if effect {
let mut wait = now_pig_id;
let mut now = act.dst.unwrap();
loop {
if wait == 0 && players[now].tp == Identity::ZhongPig {
break;
}
let t = players[now].ask(CardType::Base(BaseType::Atk));
if t.is_none() {
break;
};
players[now].cards.remove(t.unwrap());
swap(&mut now, &mut wait);
}
players[now].hp -= 1;
check_dead(&mut players, &mut pool, wait, now);
}
}
SkillType::ReqAtk => {
// loop every pig, check Cancel, req atk
let s = now_pig_id;
// s+1, s+2 .. n-1 0 1 .. s-1
let mut range = (0..players.len()).collect::<Vec<_>>();
range.rotate_left(s + 1);
for i in range {
if !players[i].alive || i == s {
continue;
}
let effect = check_cancel(
Act::new(
ActType::None,
CardType::Skill(SkillType::ReqAtk),
s,
Some(i),
),
&mut players,
);
#[cfg(feature = "debug")]
println!("effect = {effect} // {}", players[0].cards.len());
if !effect {
continue;
}
let t = players[i].ask(CardType::Base(BaseType::Atk));
if let Some(card_id) = t {
players[i].cards.remove(card_id);
} else {
players[i].hp -= 1;
check_dead(&mut players, &mut pool, s, i);
}
}
}
SkillType::ReqDef => {
// same as before
let s = now_pig_id;
// s+1, s+2 .. n-1 0 1 .. s-1
let mut range = (0..players.len()).collect::<Vec<_>>();
range.rotate_left(s + 1);
for i in range {
if !players[i].alive || i == s {
continue;
}
let effect = check_cancel(
Act::new(
ActType::None,
CardType::Skill(SkillType::ReqDef),
s,
Some(i),
),
&mut players,
);
if !effect {
continue;
}
let t = players[i].ask(CardType::Base(BaseType::Def));
if let Some(card_id) = t {
players[i].cards.remove(card_id);
} else {
players[i].hp -= 1;
check_dead(&mut players, &mut pool, s, i);
}
}
}
SkillType::Cancel => unreachable!(),
},
CardType::Equip => {
players[now_pig_id].equip = true;
}
}
// end card
//println!("use card : {} to {:?}", act.card, act.dst);
}
// end turn
//println!("end turn.------------------------");
//out_result(&players);
}
}
}
fn check_cancel(act: Act, players: &mut Vec<Pig>) -> bool {
let mut now_act = act;
let mut effect = true;
loop {
let res = who_use_cancel(now_act, players);
if res.is_none() {
break;
}
let (i, j, tp) = res.unwrap();
players[i].cards.remove(j);
#[cfg(feature = "debug")]
println!("{}:無懈可擊! // {}", i + 1, players[0].cards.len());
effect = !effect;
now_act = Act::new(tp, CardType::Skill(SkillType::Cancel), i, Some(now_act.src));
}
effect
}
fn who_use_cancel(act: Act, players: &mut Vec<Pig>) -> Option<(usize, usize, ActType)> {
assert!(act.dst.is_some());
let s = act.src;
let t = act.dst.unwrap();
if !players[t].is_known() && !players[s].is_known() {
// both unknown
return None;
}
// s, s+1, ..., n-1, 0, 1, ..., s-1
let mut range = (0..players.len()).collect::<Vec<_>>();
range.rotate_left(s);
for i in range {
if !players[i].alive {
continue;
}
match players[i].tp {
Identity::MainPig | Identity::ZhongPig => {
if ((players[t].state == State::Zhong || players[t].tp == Identity::MainPig)
&& (act.tp != ActType::Zhong))
|| act.tp == ActType::Fan
{
for j in 0..players[i].cards.len() {
if players[i].cards[j] == CardType::Skill(SkillType::Cancel) {
if players[i].tp != Identity::MainPig {
players[i].state = State::Zhong;
}
return Some((i, j, ActType::Zhong));
}
}
}
}
Identity::FanPig => {
if (players[t].state == State::Fan && act.tp != ActType::Fan)
|| act.tp == ActType::Zhong
{
for j in 0..players[i].cards.len() {
if players[i].cards[j] == CardType::Skill(SkillType::Cancel) {
players[i].state = State::Fan;
return Some((i, j, ActType::Fan));
}
}
}
}
}
}
None
}
fn check_dead(players: &mut Vec<Pig>, pool: &mut Pool, src: usize, dst: usize) {
if players[dst].tp == Identity::MainPig && players[src].state == State::None {
players[src].state = State::SimFan;
}
if players[dst].hp <= 0 {
let t = players[dst].ask(CardType::Base(BaseType::Peach));
if let Some(card_id) = t {
players[dst].cards.remove(card_id);
players[dst].hp += 1;
} else {
players[dst].alive = false;
players[dst].cards.clear();
players[dst].equip = false;
// check result
check_game_over(players);
if players[dst].tp == Identity::FanPig {
players[src].get_card(pool, 3);
} else if players[dst].tp == Identity::ZhongPig && src == 0 {
players[0].cards.clear();
players[0].equip = false;
}
}
}
}
fn check_game_over(players: &Vec<Pig>) {
if !players[0].alive {
// game over; FP win
println!("FP");
out_result(players);
exit(0);
}
for pig in players {
if pig.tp == Identity::FanPig && pig.alive {
return;
}
}
// game over; MP win
println!("MP");
out_result(players);
exit(0);
}
fn out_result(players: &Vec<Pig>) {
for pig in players {
if pig.alive {
println_card(&pig.cards);
} else {
println!("DEAD");
}
}
}
fn println_card(cards: &Vec<CardType>) {
for (i, card) in cards.iter().enumerate() {
print!("{}", card);
if i < cards.len() - 1 {
print!(" ");
}
}
println!("");
}
struct Pool {
pool: VecDeque<CardType>,
last: Option<CardType>,
}
impl Pool {
fn new(cnt: usize) -> Self {
let mut pool = VecDeque::new();
for _ in 0..cnt {
pool.push_back(CardType::from(read_str()));
}
Self { pool, last: None }
}
// data is wrong.
// if pool is empty, return the last card.
fn get(&mut self) -> Option<CardType> {
if !self.pool.is_empty() {
self.last = self.pool.pop_front();
}
return self.last;
}
}
#[cfg(feature = "debug")]
fn show(players: &mut Vec<Pig>) {
for i in 0..players.len() {
print!(
"{}:{{{}}}({})hp:",
i + 1,
players[i].tp.show(),
players[i].state.show()
);
for _ in 0..players[i].hp {
print!("*");
}
for x in &players[i].cards {
print!("【{}】", x.show());
}
println!();
}
}