結果

問題 No.5015 Escape from Labyrinth
ユーザー r3yoheir3yohei
提出日時 2023-05-06 17:15:06
言語 Rust
(1.77.0 + proconio)
結果
WA  
実行時間 -
コード長 32,014 bytes
コンパイル時間 4,073 ms
コンパイル使用メモリ 177,172 KB
実行使用メモリ 25,236 KB
スコア 19,950
最終ジャッジ日時 2023-05-06 17:16:27
合計ジャッジ時間 78,099 ms
ジャッジサーバーID
(参考情報)
judge13 / judge12
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 WA -
testcase_01 WA -
testcase_02 AC 552 ms
15,360 KB
testcase_03 WA -
testcase_04 WA -
testcase_05 AC 604 ms
15,236 KB
testcase_06 AC 608 ms
16,456 KB
testcase_07 AC 588 ms
16,240 KB
testcase_08 AC 617 ms
15,592 KB
testcase_09 WA -
testcase_10 WA -
testcase_11 WA -
testcase_12 WA -
testcase_13 WA -
testcase_14 WA -
testcase_15 AC 544 ms
15,044 KB
testcase_16 AC 548 ms
15,464 KB
testcase_17 AC 588 ms
15,352 KB
testcase_18 WA -
testcase_19 AC 604 ms
15,244 KB
testcase_20 WA -
testcase_21 WA -
testcase_22 WA -
testcase_23 WA -
testcase_24 AC 636 ms
15,348 KB
testcase_25 AC 698 ms
15,932 KB
testcase_26 AC 644 ms
16,148 KB
testcase_27 AC 652 ms
15,272 KB
testcase_28 WA -
testcase_29 AC 575 ms
15,884 KB
testcase_30 WA -
testcase_31 WA -
testcase_32 WA -
testcase_33 AC 716 ms
16,616 KB
testcase_34 AC 659 ms
16,052 KB
testcase_35 AC 682 ms
15,688 KB
testcase_36 AC 535 ms
15,372 KB
testcase_37 WA -
testcase_38 WA -
testcase_39 WA -
testcase_40 WA -
testcase_41 WA -
testcase_42 AC 543 ms
15,508 KB
testcase_43 AC 543 ms
15,432 KB
testcase_44 WA -
testcase_45 AC 573 ms
15,372 KB
testcase_46 AC 611 ms
15,496 KB
testcase_47 AC 521 ms
15,072 KB
testcase_48 WA -
testcase_49 WA -
testcase_50 WA -
testcase_51 WA -
testcase_52 AC 469 ms
10,600 KB
testcase_53 WA -
testcase_54 WA -
testcase_55 AC 560 ms
15,176 KB
testcase_56 WA -
testcase_57 WA -
testcase_58 WA -
testcase_59 WA -
testcase_60 AC 581 ms
15,676 KB
testcase_61 AC 568 ms
14,872 KB
testcase_62 WA -
testcase_63 AC 326 ms
8,724 KB
testcase_64 WA -
testcase_65 WA -
testcase_66 WA -
testcase_67 AC 668 ms
16,452 KB
testcase_68 AC 590 ms
15,504 KB
testcase_69 AC 750 ms
16,764 KB
testcase_70 AC 609 ms
15,324 KB
testcase_71 WA -
testcase_72 AC 631 ms
15,864 KB
testcase_73 AC 125 ms
6,608 KB
testcase_74 WA -
testcase_75 WA -
testcase_76 AC 613 ms
15,028 KB
testcase_77 AC 286 ms
8,504 KB
testcase_78 AC 630 ms
15,016 KB
testcase_79 WA -
testcase_80 AC 546 ms
14,548 KB
testcase_81 AC 759 ms
17,480 KB
testcase_82 AC 643 ms
16,076 KB
testcase_83 AC 648 ms
15,520 KB
testcase_84 WA -
testcase_85 WA -
testcase_86 WA -
testcase_87 AC 576 ms
15,240 KB
testcase_88 WA -
testcase_89 AC 472 ms
15,804 KB
testcase_90 AC 548 ms
14,904 KB
testcase_91 WA -
testcase_92 WA -
testcase_93 WA -
testcase_94 WA -
testcase_95 WA -
testcase_96 AC 395 ms
11,388 KB
testcase_97 WA -
testcase_98 AC 635 ms
15,024 KB
testcase_99 WA -
権限があれば一括ダウンロードができます

ソースコード

diff #

#![allow(non_snake_case, unused)]

use std::io::*;
use std::{collections::*, fmt::format};
use std::{cmp::*, vec};
use crate::input::{*, marker::*};
use crate::rand::Xoshiro256;

// コピペで使える proconio もどき
// cunitacさんからお借りしています
// https://gist.github.com/cunitac/b00be62bf68c9fb6055d22eb77c17e14

use crate::input::marker::Usize1;
use crate::input::marker::Chars;

pub mod input {
    use std::{
        cell::RefCell,
        fmt::Debug,
        io::{stdin, BufRead, BufReader, Stdin},
        str::{FromStr, SplitWhitespace},
    };

    thread_local!(
        pub static STDIN_SOURCE: RefCell<Source> = RefCell::new(Source {
            stdin: BufReader::new(stdin()),
            tokens: "".split_whitespace(),
        });
    );

    pub struct Source {
        stdin: BufReader<Stdin>,
        tokens: SplitWhitespace<'static>,
    }
    impl Source {
        pub fn next_token(&mut self) -> Option<&str> {
            self.tokens.next().or_else(|| {
                let mut input = String::new();
                self.stdin.read_line(&mut input).unwrap();
                self.tokens = Box::leak(input.into_boxed_str()).split_whitespace();
                self.tokens.next()
            })
        }
    }
    #[macro_export]
    macro_rules! read_value {
        (from $s:expr, [$t:tt; $n:expr]) => {
            (0..$n).map(|_| $crate::read_value!(from $s, $t)).collect::<Vec<_>>()
        };
        (from $s:expr, [$t:tt]) => {
            let n = $crate::read_value!(from $s, usize);
            $crate::read_value!(from $s, [$t; n])
        };
        (from $s:expr, $t:ty) => {
            <$t as $crate::input::Readable>::read(&mut $s)
        };
        (from $s:expr, $($t:tt),* $(,)?) => {
            ($($crate::read_value!(from $s, $t)),*)
        };
        ($($r:tt)*) => {
            $crate::input::STDIN_SOURCE.with(|s| {
                let mut s = s.borrow_mut();
                $crate::read_value!(from s, $($r)*)
            })
        }
    }
    #[macro_export]
    macro_rules! input {
        () => {
        };
        ($x:tt: $t:tt, $($r:tt)*) => {
            let $x = $crate::read_value!($t);
            $crate::input!($($r)*);
        };
        (mut $x:tt: $t:tt, $($r:tt)*) => {
            let mut $x = $crate::read_value!($t);
            $crate::input!($($r)*);
        };
        (from $s:expr, $x:tt, $t:tt, $($r:tt)*) => {
            let $x = $crate::read_value!(from $s, $t);
            $crate::input!(from $s, $($r)*);
        };
        (from $s:expr, mut $x:tt, $t:tt, $($r:tt)*) => {
            let mut $x = $crate::read_value!(from $s, $t);
            $crate::input!(from $s, $($r)*);
        };
        ($($r:tt)*) => {
            $crate::input!($($r)*,);
        };
    }
    pub trait Readable {
        type Output;
        fn read(source: &mut Source) -> Self::Output;
    }
    impl<T: FromStr<Err = E>, E: Debug> Readable for T {
        type Output = T;
        fn read(source: &mut Source) -> T {
            source.next_token().unwrap().parse().unwrap()
        }
    }
    pub mod marker {
        macro_rules! impl_readable {
            ($e:ident, $t:ty, $u:ty, $f:expr) => {
                pub enum $e {}
                impl $crate::input::Readable for $e {
                    type Output = $t;
                    fn read(mut source: &mut $crate::input::Source) -> $t {
                        $f($crate::read_value!(from source, $u))
                    }
                }
            };
        }
        impl_readable!(Usize1, usize, usize, |x| x - 1);
        impl_readable!(Isize1, isize, isize, |x| x - 1);
        impl_readable!(Chars, Vec<char>, String, |x: String| x.chars().collect());
        impl_readable!(Bytes, Vec<u8>, String, |x: String| x.bytes().collect());
    }
}

mod rand {
    pub(crate) struct Xoshiro256 {
        s0: u64,
        s1: u64,
        s2: u64,
        s3: u64,
    }

    impl Xoshiro256 {
        pub(crate) fn new(mut seed: u64) -> Self {
            let s0 = split_mix_64(&mut seed);
            let s1 = split_mix_64(&mut seed);
            let s2 = split_mix_64(&mut seed);
            let s3 = split_mix_64(&mut seed);
            Self { s0, s1, s2, s3 }
        }

        fn next(&mut self) -> u64 {
            let result = (self.s1.wrapping_mul(5)).rotate_left(7).wrapping_mul(9);
            let t = self.s1 << 17;

            self.s2 ^= self.s0;
            self.s3 ^= self.s1;
            self.s1 ^= self.s2;
            self.s0 ^= self.s3;
            self.s2 ^= t;
            self.s3 = self.s3.rotate_left(45);

            result
        }

        pub(crate) fn gen_usize(&mut self, lower: usize, upper: usize) -> usize {
            assert!(lower < upper);
            let count = upper - lower;
            (self.next() % count as u64) as usize + lower
        }

        pub(crate) fn gen_i64(&mut self, lower: i64, upper: i64) -> i64 {
            assert!(lower < upper);
            let count = upper - lower;
            (self.next() % count as u64) as i64 + lower
        }

        pub(crate) fn gen_u128(&mut self, lower: u128, upper: u128) -> u128 {
            assert!(lower < upper);
            let count = upper - lower;
            (self.next() % count as u64) as u128 + lower
        }

        pub(crate) fn gen_f64(&mut self) -> f64 {
            const UPPER_MASK: u64 = 0x3ff0000000000000;
            const LOWER_MASK: u64 = 0xfffffffffffff;
            let result = UPPER_MASK | (self.next() & LOWER_MASK);
            let result: f64 = unsafe { std::mem::transmute(result) };
            result - 1.0
        }

        pub(crate) fn gen_bool(&mut self, prob: f64) -> bool {
            self.gen_f64() < prob
        }
    }

    fn split_mix_64(x: &mut u64) -> u64 {
        *x = x.wrapping_add(0x9e3779b97f4a7c15);
        let mut z = *x;
        z = (z ^ z >> 30).wrapping_mul(0xbf58476d1ce4e5b9);
        z = (z ^ z >> 27).wrapping_mul(0x94d049bb133111eb);
        z ^ z >> 31
    }
}

const N: usize = 60;
const H: i64 = 1500;
const TL: f64 = 2.9;
const DIJ: [(usize, usize); 4] = [(!0, 0), (1, 0), (0, !0), (0, 1)];
const DIR: [char; 4] = ['U', 'D', 'L', 'R'];
// Actionは,(行動の種類, 方向)で持つ
type Action = (char, usize);

pub fn get_time() -> f64 {
    static mut STIME: f64 = -1.0;
    let t = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap();
    let ms = t.as_secs() as f64 + t.subsec_nanos() as f64 * 1e-9;
    unsafe {
        if STIME < 0.0 {
            STIME = ms;
        }
        // ローカル環境とジャッジ環境の実行速度差はget_timeで吸収しておくと便利
        #[cfg(feature = "local")]
        {
            (ms - STIME) * 1.5
        }
        #[cfg(not(feature = "local"))]
        {
            (ms - STIME)
        }
    }
}

const TRUE: &'static bool = &true;
const FALSE: &'static bool = &false;
#[derive(Clone, Debug)]
/// Efficient bool collection
pub struct BitSet {
    buf: Vec<u64>,
    size: usize,
}
impl BitSet {
    #[allow(dead_code)]
    pub fn new(size: usize) -> BitSet {
        BitSet {
            buf: vec![0; (size + 63) / 64],
            size: size,
        }
    }
    #[allow(dead_code)]
    pub fn set(&mut self, i: usize, b: bool) {
        assert!(i < self.size);
        if b {
            self.buf[i >> 6] |= 1 << (i & 63);
        } else {
            self.buf[i >> 6] &= !(1 << (i & 63));
        }
    }
    #[allow(dead_code)]
    pub fn count_ones(&self) -> usize {
        let sum: u32 = self.buf.iter().map(|x| x.count_ones()).sum();
        sum as usize
    }
    #[allow(dead_code)]
    fn chomp(&mut self) {
        let r = self.size & 63;
        if r != 0 {
            if let Some(x) = self.buf.last_mut() {
                let d = 64 - r;
                *x = (*x << d) >> d;
            }
        }
    }
}
impl std::ops::Index<usize> for BitSet {
    type Output = bool;
    fn index(&self, index: usize) -> &bool {
        [FALSE, TRUE][(self.buf[index >> 6] >> (index & 63)) as usize & 1]
    }
}
impl std::ops::ShlAssign<usize> for BitSet {
    fn shl_assign(&mut self, x: usize) {
        let q = x >> 6;
        let r = x & 63;
        if q >= self.buf.len() {
            for x in &mut self.buf {
                *x = 0;
            }
            return;
        }
        if r == 0 {
            for i in (q..self.buf.len()).rev() {
                self.buf[i] = self.buf[i - q];
            }
        } else {
            for i in (q + 1..self.buf.len()).rev() {
                self.buf[i] = (self.buf[i - q] << r) | (self.buf[i - q - 1] >> (64 - r));
            }
            self.buf[q] = self.buf[0] << r;
        }
        for x in &mut self.buf[..q] {
            *x = 0;
        }
        self.chomp();
    }
}
impl std::ops::Shl<usize> for BitSet {
    type Output = Self;
    fn shl(mut self, x: usize) -> Self {
        self <<= x;
        self
    }
}
impl std::ops::ShrAssign<usize> for BitSet {
    fn shr_assign(&mut self, x: usize) {
        let q = x >> 6;
        let r = x & 63;
        if q >= self.buf.len() {
            for x in &mut self.buf {
                *x = 0;
            }
            return;
        }
        if r == 0 {
            for i in 0..self.buf.len() - q {
                self.buf[i] = self.buf[i + q];
            }
        } else {
            for i in 0..self.buf.len() - q - 1 {
                self.buf[i] = (self.buf[i + q] >> r) | (self.buf[i + q + 1] << (64 - r));
            }
            let len = self.buf.len();
            self.buf[len - q - 1] = self.buf[len - 1] >> r;
        }
        let len = self.buf.len();
        for x in &mut self.buf[len - q..] {
            *x = 0;
        }
    }
}
impl std::ops::Shr<usize> for BitSet {
    type Output = Self;
    fn shr(mut self, x: usize) -> Self {
        self >>= x;
        self
    }
}
impl<'a> std::ops::BitAndAssign<&'a BitSet> for BitSet {
    fn bitand_assign(&mut self, rhs: &'a Self) {
        for (a, b) in self.buf.iter_mut().zip(rhs.buf.iter()) {
            *a &= *b;
        }
    }
}
impl<'a> std::ops::BitAnd<&'a BitSet> for BitSet {
    type Output = Self;
    fn bitand(mut self, rhs: &'a Self) -> Self {
        self &= rhs;
        self
    }
}
impl<'a> std::ops::BitOrAssign<&'a BitSet> for BitSet {
    fn bitor_assign(&mut self, rhs: &'a Self) {
        for (a, b) in self.buf.iter_mut().zip(rhs.buf.iter()) {
            *a |= *b;
        }
        self.chomp();
    }
}
impl<'a> std::ops::BitOr<&'a BitSet> for BitSet {
    type Output = Self;
    fn bitor(mut self, rhs: &'a Self) -> Self {
        self |= rhs;
        self
    }
}
impl<'a> std::ops::BitXorAssign<&'a BitSet> for BitSet {
    fn bitxor_assign(&mut self, rhs: &'a Self) {
        for (a, b) in self.buf.iter_mut().zip(rhs.buf.iter()) {
            *a ^= *b;
        }
        self.chomp();
    }
}
impl<'a> std::ops::BitXor<&'a BitSet> for BitSet {
    type Output = Self;
    fn bitxor(mut self, rhs: &'a Self) -> Self {
        self ^= rhs;
        self
    }
}

#[derive(Clone)]
struct Map {
    N: usize,
    D: i64,
    H: i64,
    S: Vec<Vec<char>>,
    M: usize,
    wall: Vec<bool>,
    interval_of_probe: Vec<i64>,
}
impl Map {
    fn new() -> Self {
        // 標準入力を受け取る
        input! {
            _N: usize,
            D: i64,
            _H: i64,
        }
        let mut S = vec![];
        for _ in 0..N {
            input! {
                Si: Chars,
            }
            S.push(Si);
        }
        // 壁位置を発見しておく
        let mut wall = vec![false; N*N];
        for y in 0..N {
            for x in 0..N {
                if S[y][x] == '#' {
                    wall[y * N + x] = true;
                }
            }
        }
        input! {
            M: usize,
        }
        let mut interval_of_probe = vec![0; N*N];
        for _ in 0..M {
            input! {
                yi: usize,
                xi: usize,
                di: i64,
            }
            interval_of_probe[yi * N + xi] = di;
        }
        Self { N, D, H, S, M, wall, interval_of_probe }
    }
}

// 時刻tにおける盤面の状態を保持する構造体
#[derive(Clone)]
struct State<'a> {
    turn: i64,
    is_done: bool,
    evaluated_score: i64, // 評価関数値
    jewelry_map: BitSet, // 宝石位置を表すbitset
    gunpowder_map: BitSet, // 火薬位置を表すbitset
    block_map: BitSet, // ブロック位置を表すbitset
    probe_map: BitSet, // 探知機位置を表すbitset
    pos: (usize, usize), // プレイヤーの現在地の(y, x)
    hp: i64, // プレイヤーの体力
    jewelry: i64, // 宝石の数 (これx10が実際のゲームスコア)
    gunpowder: i64, // 火薬の数
    D: i64, // 入力のD
    has_key: bool, // 鍵を持っているかどうか
    key: (usize, usize), // 鍵の位置(y, x)
    goal: (usize, usize), // ゴールの位置(y, x)
    output: Vec<(char, char)>, // 出力
    prev_pos: (usize, usize), // 前の位置
    actions: Vec<Action>, // 実際の行動を保持
    zobrist_hash: u128, // 各マップの状態をなるべく一意に定めるzobrist hash値
    zobrist_hash_map: &'a ZobristHash, // ある座標にプレイヤー・コインがあることを示すハッシュ値を持つ
}
impl<'a> State<'a> {
    fn new(map: &Map, zobrist_hash_map: &'a ZobristHash) -> Self {
        // zobrist_hashを初期化する
        let mut zobrist_hash = 0;
        // 各オブジェクトの位置を表すbitsetを初期化し,位置を記録していく
        let mut jewelry_map = BitSet::new(N * N);
        let mut gunpowder_map = BitSet::new(N * N);
        let mut block_map = BitSet::new(N * N);
        let mut probe_map = BitSet::new(N * N);
        // mapからプレイヤーの初期位置を探す
        let mut pos = (!0, !0);
        let mut key = (!0, !0);
        let mut goal = (!0, !0);
        'outer: for yi in 0..N {
            for xi in 0..N {
                if map.S[yi][xi] == 'S' {
                    pos = (yi, xi);
                    zobrist_hash ^= zobrist_hash_map.player_hash[yi][xi];
                } else if map.S[yi][xi] == 'K' {
                    key = (yi, xi);
                    zobrist_hash ^= zobrist_hash_map.key_hash[yi][xi];
                } else if map.S[yi][xi] == 'G' {
                    goal = (yi, xi);
                } else if map.S[yi][xi] == 'J' {
                    jewelry_map.set(yi * N + xi, true);
                } else if map.S[yi][xi] == 'F' {
                    gunpowder_map.set(yi * N + xi, true);
                } else if map.S[yi][xi] == 'E' {
                    probe_map.set(yi * N + xi, true);
                }
            }
        }
        Self {
            turn: 0,
            is_done: false,
            evaluated_score: 0,
            jewelry_map,
            gunpowder_map,
            block_map,
            probe_map,
            jewelry: 0,
            gunpowder: 0,
            D: map.D,
            has_key: false,
            pos,
            hp: H,
            key,
            goal,
            output: vec![],
            prev_pos: (!0, !0),
            actions: vec![],
            zobrist_hash,
            zobrist_hash_map,
        }
    }
    // 評価関数
    pub fn evaluate_score(&mut self, seen: bool) {
        self.evaluated_score = 0;
        // これまでの獲得宝石数
        self.evaluated_score += self.jewelry;
        // 残りの体力
        self.evaluated_score += self.hp;
        // ターンを無駄にかけることに対してペナルティ
        self.evaluated_score -= self.turn;
        // 同じところに留まることに対してペナルティ
        if self.pos == self.prev_pos {
            self.evaluated_score -= 100;
        }
        // 鍵の有無やゲームクリアしているかどうかについての評価
        if self.has_key && self.is_done {
            // 鍵を持っているかつゴールした
            self.evaluated_score += 10000;
        } else if self.has_key && !self.is_done{
            // 鍵を持っているかつゴールしていない
            self.evaluated_score += 1500;
            // ゴールへの距離が近いほどよいとしたい
            // self.evaluated_score -= self.get_distance_to_destination('G');
            // self.evaluated_score += (500 / self.get_distance_to_destination( 'G'));
            self.evaluated_score -= (self.pos.0.abs_diff(self.goal.0) + self.pos.1.abs_diff(self.goal.1)) as i64;
        } else {
            // それ以外は鍵を持っていないしゴールもしていない
            // 鍵への距離が近いほどよいとしたい
            // self.evaluated_score -= self.get_distance_to_destination('K');
            // self.evaluated_score += (500 / self.get_distance_to_destination('K'));
            self.evaluated_score -= (self.pos.0.abs_diff(self.key.0) + self.pos.1.abs_diff(self.key.1)) as i64;
        }
        // ゴールしてないかつ体力が切れたなら大幅減点
        if !self.is_done && self.hp <= 0 {
            self.evaluated_score -= 10000;
        }
        // すでに同一の盤面を見ているなら,評価値を下げる
        if seen {
            self.evaluated_score -= 1500;
        }
    }
    // 現在地から鍵やゴールへの距離を返す
    pub fn get_distance_to_destination(&self, destination: char, map: &Map) -> i64 {
        let mut dist = 0;
        // 各マップについて,幅優先探索で最も近いコインを探す
        let mut deque = VecDeque::new();
        let mut visited = vec![vec![false; N]; N];
        deque.push_back((self.pos.0, self.pos.1, 0));
        visited[self.pos.0][self.pos.1] = true;
        while let Some((frm_y, frm_x, dist_frm)) = deque.pop_front() {
            // 目的地なら位置を返す
            if destination == 'K' && frm_y == self.key.0 && frm_x == self.key.1 {
                return dist_frm;
            }
            if destination == 'G' && frm_y == self.goal.0 && frm_x == self.goal.1 {
                return dist_frm;
            }
            for d in 0..4 {
                let next_y = frm_y.wrapping_add(DIJ[d].0);
                let next_x = frm_x.wrapping_add(DIJ[d].1);
                // 移動: 範囲内で,訪問済みでも壁でもブロックても探知機でもないなら移動可能
                if next_x < N && next_y < N && !visited[next_y][next_x] && !map.wall[next_y * N + next_x] && !self.block_map[next_y * N + next_x] && !self.probe_map[next_y * N + next_x] {
                    deque.push_back((next_y, next_x, dist_frm + 1));
                    visited[next_y][next_x] = true;
                }
            }
        }
        // 目的地へたどりつけないときにここに来る.強烈なペナルティを与える
        10_000
    }
    // 全ての合法手を返す
    pub fn get_legal_actions(&self, map: &Map) -> Vec<Action> {
        let mut legal_actions = vec![];
        // 上下左右の4方向を調べる
        for d in 0..4 {
            let next_y = self.pos.0.wrapping_add(DIJ[d].0);
            let next_x = self.pos.1.wrapping_add(DIJ[d].1);
            // 移動: 範囲内で,壁でもブロックでも探知機でもないなら移動可能
            if next_x < N && next_y < N && !map.wall[next_y * N + next_x] && !self.block_map[next_y * N + next_x] && !self.probe_map[next_y * N + next_x] {
                // 前回の行動を見て,戻るに相当することをしようとしているなら枝刈りする(LRと動くなど.これはSで代替可能)
                // if self.prev_action.0 == 'M' && (self.prev_action.1 + d) % 4 == 1 {continue;}
                legal_actions.push(('M', d));
            }
            // ブロック生成: 範囲内で,空なら設置可能 (スタート地点も含む)
            if next_x < N && next_y < N && !map.wall[next_y * N + next_x] && !self.block_map[next_y * N + next_x] && !self.probe_map[next_y * N + next_x]
                && !self.jewelry_map[next_y * N + next_x] && !self.gunpowder_map[next_y * N + next_x] && !(next_y == self.goal.0 && next_x == self.goal.1)
                && !(!self.has_key && next_y == self.key.0 && next_x == self.key.1) {
                legal_actions.push(('B', d));
            }
            // ブロック破壊: 範囲内で,ブロックなら破壊可能
            if next_x < N && next_y < N && self.block_map[next_y * N + next_x] {
                legal_actions.push(('B', d));
            }
            // [todo] 火炎放射(=探知機破壊): 範囲内で,火薬があり,上下左右の延長方向に探知機があれば破壊可能
        }
        // 何もしないのは常に合法手
        legal_actions.push(('S', !0));
        legal_actions
    }
    // このターンにプレイヤーに当たる探知機の数を見つける
    // [todo] 高速化したい
    fn search_danger_probe(&self, map: &Map) -> i64 {
        let mut num_probe = 0;
        for p in (0..self.pos.0).rev() {
            // 探知機を見つけるまでに先に壁かブロックか作動しない探知機見つかればこのターンは安全
            if map.wall[p * N + self.pos.1] || self.block_map[p * N + self.pos.1] || (self.probe_map[p * N + self.pos.1] && self.turn % map.interval_of_probe[p * N + self.pos.1] != 0) {
                break;
            }
            if self.probe_map[p * N + self.pos.1] && self.turn % map.interval_of_probe[p * N + self.pos.1] == 0 {
                num_probe += 1;
                break;
            }
        }
        for p in self.pos.0+1..N {
            // 探知機を見つけるまでに先に壁かブロックか作動しない探知機見つかればこのターンは安全
            if map.wall[p * N + self.pos.1] || self.block_map[p * N + self.pos.1] || (self.probe_map[p * N + self.pos.1] && self.turn % map.interval_of_probe[p * N + self.pos.1] != 0) {
                break;
            }
            if self.probe_map[p * N + self.pos.1] && self.turn % map.interval_of_probe[p * N + self.pos.1] == 0 {
                num_probe += 1;
                break;
            }
        }
        for p in (0..self.pos.1).rev() {
            // 探知機を見つけるまでに先に壁かブロックか作動しない探知機見つかればこのターンは安全
            if map.wall[self.pos.0 * N + p] || self.block_map[self.pos.0 * N + p] || (self.probe_map[self.pos.0 * N + p] && self.turn % map.interval_of_probe[self.pos.0 * N + p] != 0) {
                break;
            }
            if self.probe_map[self.pos.0 * N + p] && self.turn % map.interval_of_probe[self.pos.0 * N + p] == 0 {
                num_probe += 1;
                break;
            }
        }
        for p in self.pos.1+1..N {
            // 探知機を見つけるまでに先に壁かブロックか作動しない探知機見つかればこのターンは安全
            if map.wall[self.pos.0 * N + p] || self.block_map[self.pos.0 * N + p] || (self.probe_map[self.pos.0 * N + p] && self.turn % map.interval_of_probe[self.pos.0 * N + p] != 0) {
                break;
            }
            if self.probe_map[self.pos.0 * N + p] && self.turn % map.interval_of_probe[self.pos.0 * N + p] == 0 {
                num_probe += 1;
                break;
            }
        }
        num_probe
    }
    // 指定したactionを各マップに施す
    pub fn advance(&mut self, map: &Map, action: Action) {
        // ターン加算
        self.turn += 1;
        // 1ターンで必ず体力を1消耗する
        self.hp -= 1;
        // 行動の種類によって分岐する
        match action.0 {
            'S' => {
                // outputへ入れる
                self.output.push((action.0, 'x'));
            },
            'M' => {
                let d = action.1;
                let next_y = self.pos.0.wrapping_add(DIJ[d].0);
                let next_x = self.pos.1.wrapping_add(DIJ[d].1);
                self.prev_pos = self.pos;
                // 移動前のプレイヤー位置に関して,zobrist hashのxorして消す
                self.zobrist_hash ^= self.zobrist_hash_map.player_hash[self.pos.0][self.pos.1];
                // 移動し,zobrist hashも更新
                self.pos = (next_y, next_x);
                self.zobrist_hash ^= self.zobrist_hash_map.player_hash[next_y][next_x];
                if next_y == self.key.0 && next_x == self.key.1 {
                    // 鍵の取得
                    self.has_key = true;
                    // self.map.S[next_y][next_x] = '.';
                    self.zobrist_hash ^= self.zobrist_hash_map.key_hash[next_y][next_x];
                } else if next_y == self.goal.0 && next_x == self.goal.1 && self.has_key {
                    // ゲームクリア
                    self.is_done = true;
                } else if self.jewelry_map[next_y * N + next_x] {
                    // 宝石の取得
                    self.jewelry += 10;
                    self.jewelry_map.set(next_y * N + next_x, false);
                    self.zobrist_hash ^= self.zobrist_hash_map.jewelry_hash[next_y][next_x];
                } else if self.gunpowder_map[next_y * N + next_x] {
                    // 火薬の取得
                    self.gunpowder += 1;
                    self.gunpowder_map.set(next_y * N + next_x, false);
                    self.zobrist_hash ^= self.zobrist_hash_map.gunpowder_hash[next_y][next_x];
                }
                // outputへ入れる
                self.output.push((action.0, DIR[action.1]));
            },
            'B' => {
                let d = action.1;
                let next_y = self.pos.0.wrapping_add(DIJ[d].0);
                let next_x = self.pos.1.wrapping_add(DIJ[d].1);
                // 生成・破壊は隣にあるかないかで分岐
                if self.block_map[next_y * N + next_x] {
                    self.block_map.set(next_y * N + next_x, false);
                    self.zobrist_hash ^= self.zobrist_hash_map.block_hash[next_y][next_x];
                } else {
                    self.block_map.set(next_y * N + next_x, true);
                    self.zobrist_hash ^= self.zobrist_hash_map.block_hash[next_y][next_x];
                }
                // outputへ入れる
                self.output.push((action.0, DIR[action.1]));
            },
            'F' => (),
            _ => unreachable!(),
        }
        // 今いる場所の上下左右の延長線上で,このターンに作動する探知機が存在するかどうか調べる
        if !self.is_done {
            let num_probes = self.search_danger_probe(map);
            self.hp -= num_probes * self.D;
        }
    }
    // 回答を出力する
    pub fn print(&self) {
        for &(action, dir) in &self.output {
            if action == 'S' {
                println!("{}", action);
            } else {
                println!("{} {}", action, dir);
            }
        }
    }
}
// Stateをbinaryheapに入れるとき,評価値の大きい順に取り出すため,partialordを実装する
impl<'a> Ord for State<'a> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.evaluated_score.cmp(&other.evaluated_score)
    }
}
impl<'a> PartialEq for State<'a> {
    fn eq(&self, other: &Self) -> bool {
        self.evaluated_score == other.evaluated_score
    }
}
impl<'a> Eq for State<'a> {} // ここは空でOK
impl<'a> PartialOrd for State<'a> {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.evaluated_score.partial_cmp(&other.evaluated_score)
    }
}

// ある座標にプレイヤー,ブロック,探知機,宝石,火薬,鍵が存在することをハッシュに変換する
#[derive(Clone)]
struct ZobristHash {
    player_hash: Vec<Vec<u128>>, // 座標pにプレイヤーが存在することに対応するハッシュ値
    block_hash: Vec<Vec<u128>>, // 座標pにブロックが存在することに対応するハッシュ値
    probe_hash: Vec<Vec<u128>>, // 座標pにブロックが存在することに対応するハッシュ値
    jewelry_hash: Vec<Vec<u128>>, // 座標pにブロックが存在することに対応するハッシュ値
    gunpowder_hash: Vec<Vec<u128>>, // 座標pにブロックが存在することに対応するハッシュ値
    key_hash: Vec<Vec<u128>>, // 座標pにブロックが存在することに対応するハッシュ値
}
impl ZobristHash {
    fn new() -> Self {
        let mut rng = Xoshiro256::new(8192);
        let mut player_hash = vec![vec![0; N]; N];
        let mut block_hash = vec![vec![0; N]; N];
        let mut probe_hash = vec![vec![0; N]; N];
        let mut jewelry_hash = vec![vec![0; N]; N];
        let mut gunpowder_hash = vec![vec![0; N]; N];
        let mut key_hash = vec![vec![0; N]; N];
        for y in 0..N {
            for x in 0..N {
                player_hash[y][x] = rng.gen_u128(std::u128::MIN, std::u128::MAX);
                block_hash[y][x] = rng.gen_u128(std::u128::MIN, std::u128::MAX);
                probe_hash[y][x] = rng.gen_u128(std::u128::MIN, std::u128::MAX);
                jewelry_hash[y][x] = rng.gen_u128(std::u128::MIN, std::u128::MAX);
                gunpowder_hash[y][x] = rng.gen_u128(std::u128::MIN, std::u128::MAX);
                key_hash[y][x] = rng.gen_u128(std::u128::MIN, std::u128::MAX);
            }
        }
        Self { player_hash, block_hash, probe_hash, jewelry_hash, gunpowder_hash, key_hash }
    }
}

// ビームサーチ
fn beam_search<'a>(state: &'a State<'a>, map: &Map, beam_width: usize, beam_depth: usize) -> Option<State<'a>> {
    let mut best_state = state.clone();
    let mut now_beam = BinaryHeap::new();
    now_beam.push(state.clone());
    let mut zobrist_hash_set = HashSet::new();
    for _ in 0..beam_depth {
        let mut next_beam = BinaryHeap::new();
        for _ in 0..beam_width {
            if now_beam.is_empty() {break;}
            let now_state = now_beam.pop().unwrap();
            if now_state.is_done {continue;}
            let legal_actions = now_state.get_legal_actions(map);
            for &action in &legal_actions {
                let mut next_state = now_state.clone();
                next_state.advance(map, action);
                // 同一盤面が評価済みかどうかで評価値を変える
                if zobrist_hash_set.contains(&next_state.zobrist_hash) {
                    next_state.evaluate_score(true);
                } else {
                    next_state.evaluate_score(false);
                    zobrist_hash_set.insert(next_state.zobrist_hash);
                }
                next_beam.push(next_state);
            }
        }
        if next_beam.is_empty() {
            eprintln!("empty");
            break;
        }
        if best_state.evaluated_score <= next_beam.peek().unwrap().evaluated_score && next_beam.peek().unwrap().is_done {
            best_state = next_beam.peek().unwrap().clone();
        }
        now_beam.clear();
        while now_beam.len() < beam_width {
            if let Some(state) = next_beam.pop() {
                now_beam.push(state);
            } else {
                break;
            }
        }
    }
    Some(best_state)
}

fn main() {
    get_time();
    let map = Map::new();
    let zobrist_hash_map = ZobristHash::new();
    let mut state = State::new(&map, &zobrist_hash_map);

    // ビームサーチを回す
    if let Some(bs_state) = beam_search(&state, &map, 50, H as usize) {
        bs_state.print();
        eprintln!("evaluated score: {}", bs_state.evaluated_score);
        eprintln!("game score: {}", bs_state.jewelry);
        eprintln!("turn: {}", bs_state.turn);
        eprintln!("time: {}", get_time());
    } else {
        eprintln!("error");
        eprintln!("time: {}", get_time());
    }
}
0