1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

オフラインリアルタイムどう書くE27の実装例(Rust)

Posted at

オフラインリアルタイムどう書くE27の問題「とあるカードゲーム」の実装例を、Rust で。

問題 : http://nabetani.sakura.ne.jp/hena/orde27cardgame/
実装リンク集 : https://qiita.com/Nabetani/items/cdc102d186faaf542574
イベント : https://yhpg.doorkeeper.jp/events/79705

次回のイベントは 12/8。
詳しくは
https://yhpg.doorkeeper.jp/events/82699
を御覧ください。
まだ問題も決まってないけど、 Rust での実装例を用意したいと思っている(未定) ので、Rust 好きの方も是非。

で。
https://qiita.com/Nabetani/items/751ceaa60614dff6aec7 の Go版を Rust に移植した。

Rust
use std::cmp;

fn single(nums: &Vec<u8>) -> bool {
    let max = nums.iter().max().unwrap();
    let min = nums.iter().min().unwrap();
    max == min
}

fn continuous(nums: &Vec<u8>) -> bool {
    let mut s = nums.clone();
    s.sort_unstable();
    (0..(s.len() - 1)).all(|ix| {
        let c = *s.iter().nth(ix).unwrap();
        let n = *s.iter().nth(ix + 1).unwrap();
        c + 1 == n
    })
}

fn is_story_or_kind(cards: &Vec<String>) -> bool {
    let rank = |s: &String| s.bytes().last().unwrap() - b'0';
    let suit = |s: &String| s.bytes().next().unwrap();
    let ranks = cards.iter().map(|c| rank(c)).collect::<Vec<u8>>();
    let suits = cards.iter().map(|c| suit(c)).collect::<Vec<u8>>();
    let is_kind = single(&ranks) && continuous(&suits);
    let is_story = single(&suits) && continuous(&ranks);
    is_kind || is_story
}

fn each_split(cards: &Vec<String>, mut f: Box<&mut FnMut(Vec<Vec<String>>)>) {
    match cards.len() {
        0 => {
            f(Vec::<Vec<String>>::new());
        }
        1 => {}
        2 | 3 => {
            if is_story_or_kind(&cards) {
                f(vec![cards.clone()]);
            }
        }
        _ => {
            let maxbits = 1i32 << (cards.len() - 1);
            for bits in 1..maxbits {
                let mut head: Vec<String> = vec![cards.iter().next().unwrap().clone()];
                let mut tail: Vec<String> = Vec::<String>::new();
                for b in 0..(cards.len() - 1) {
                    (if 0 != bits & (1 << b) {
                        &mut head
                    } else {
                        &mut tail
                    }).push(cards.iter().nth(b + 1).unwrap().to_string());
                }
                if !is_story_or_kind(&head) {
                    continue;
                }
                let mut closure = |mut hand: Vec<Vec<String>>| {
                    hand.push(head.clone());
                    f(hand);
                };
                each_split(&tail, Box::new(&mut closure))
            }
        }
    }
}

fn score_of(hand: &Vec<Vec<String>>) -> i32 {
    hand.iter()
        .fold(0i32, |acc, g| acc + ((g.len() - 1) * (g.len() - 1)) as i32)
}

fn solve(src: &String) -> String {
    let cards = src
        .split(",")
        .map(|x| x.to_string())
        .collect::<Vec<String>>();
    let mut score = 0i32;
    {
        let mut closure = |hand: Vec<Vec<String>>| {
            score = cmp::max(score, score_of(&hand));
        };
        each_split(&cards, Box::new(&mut closure));
    }
    if score == 0 {
        "-".to_string()
    } else {
        format!("{}", score)
    }
}

fn test(num: i32, src: String, expected: String) {
    let actual = solve(&src);
    let okay = actual == expected;
    println!(
        "{}, {}, src: {}, act: {}, exp: {}",
        num,
        if okay { "ok" } else { "**NG**" },
        src,
        actual,
        expected
    );
}

macro_rules! test {
    ($n:expr, $x:expr, $y:expr) => {
        test($n, $x.to_string(), $y.to_string());
    };
}

fn main() {
    test!(0, "A1,A2,A3,A4,B3,C3,D5,E5", "11");
    test!(1, "A1,B2,C3,D4,E5,F6,G7,A8", "-");
    test!(2, "A3,A5,A4,A6,A7,A1,A2,A8", "49");
    test!(3, "G2,G1,A1,F1,C1,E1,B1,D1", "26");
    test!(69, "A4,B4,F4,G4,E3,G3,E4,F3", "9");
    test!(70, "A8,D8,D6,D7,C8,B8,E8,C7", "4");
}

いつもどおりテストデータの大半は省略。

で。
型が合わなくて一時間以上苦しんだ。

合わなかったのは、each_split の第二引数。
最初は Box を使わずに書いて、テンプレート引数の無限展開みたいになってコンパイルエラー。
Box を使うことを思いついたものの、なにを「借用」して何を「mut」すればいいのかわからず試行錯誤を繰り返した。
テストを通すところまでは持っていけたものの、まだ良くわかっていない。

わかっていないといえば、空の Vec<Vec<String>> を作るときに Vec::<Vec<String>>::new() と書いたんだけど、vec! で作る方法があるのか無いのかわからなかった。

go からの移植なので、fold とか map がある幸せを噛み締めた。

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?