LOJ#2885. 「SDOI2010」豬國殺

huyufeifei發表於2024-09-15

對拍器在此。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!();
    }
}

相關文章