オフラインリアルタイムどう書く E27の参考問題「灯りと鏡」の実装例を、Rust で。
問題 : http://nabetani.sakura.ne.jp/hena/orde27ligmir/
実装リンク集 : https://qiita.com/Nabetani/items/0b2f459ec128c89948f4
次回のイベントは 12/8。
詳しくは
https://yhpg.doorkeeper.jp/events/82699
を御覧ください。
まだ問題も決まってないけど、 Rust での実装例を用意したいと思っている(未定) ので、Rust 好きの方も是非。
で。
実装例は以下の通り。
const W: isize = 5; // width
const H: isize = 5; // height
const PWS: isize = W + 1; // positive width with separator
const NWS: isize = -PWS; // negative width with separator
fn pos_to_xy(pos: isize) -> (isize, isize) {
(pos % PWS, pos / PWS)
}
fn step(curpos: isize, dir: isize) -> Option<isize> {
let (x, y) = pos_to_xy(curpos);
let okay = match dir {
NWS => 0 < y,
PWS => y + 1 < H,
-1 => 0 < x,
1 => x + 1 < W,
_ => panic!("unexpected direction"),
};
if okay {
Some(curpos + dir)
} else {
None
}
}
fn mirror0(dir: isize) -> isize {
match dir {
PWS | NWS => dir / PWS,
1 | -1 => dir * PWS,
_ => panic!("unexpected direction"),
}
}
fn run(src: &String, passed: &mut Vec<isize>, mut curpos: isize, mut dir: isize) {
for _i in 0..(W * H * 2) {
passed.push(curpos);
let nextpos_opt = step(curpos, dir);
match nextpos_opt {
Some(nextpos) => {
let nextcell = src.chars().nth(nextpos as usize).unwrap() as u8 as char;
dir = match nextcell {
'.' | 'Y' => dir,
'0' => mirror0(dir),
'1' => -mirror0(dir),
'x' => return,
_ => panic!("unexpected direction"),
};
curpos = nextpos;
}
_ => {
return;
}
}
}
}
fn solve(src: &String) -> String {
let you = src.find('Y').unwrap() as isize;
let mut passed = Vec::<isize>::new();
run(src, &mut passed, you, -6);
let mut places = passed
.iter()
.map(|pos| {
let (x, y) = pos_to_xy(*pos);
((x + y * W + 'a' as isize) as u8 as char).to_string()
}).collect::<Vec<String>>();
places.sort_unstable();
places.dedup();
places.join("")
}
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, "x...x/.1.0./..0../.Y.../0..x.", "ghilnqs");
test!(1, "..Y../...../...../...../.....", "c");
test!(2, "..x../..Y../...../...../.....", "h");
test!(3, "..Y.x/..1x0/11.../....0/1..1.", "c");
// 中略
test!(
45,
"1...0/.1.0./..1../..01./Y0..1",
"abcdefghijklmnopqrstuvwxy"
);
}
いつもどおりテストの大半は省略。
問題が簡単だったせいか、わりとすんなり書けた。
今回書き方がわからなかったのは
let okay = match dir {
MWS => 0 < y,
PWS => y + 1 < H,
-1 => 0 < x,
1 => x + 1 < W,
_ => panic!("unexpected direction"),
};
の部分。本当は
let okay = match dir {
-(W+1) => 0 < y,
(W+1) => y + 1 < H,
-1 => 0 < x,
1 => x + 1 < W,
_ => panic!("unexpected direction"),
};
って書きたかったんだけど、エラーだった。
=>
の左辺に式は書けないのかと思って定数を書いたら通った。そういうものか。
あと、Rust 全般についての感想。
すぐにコンパイルエラーになって、そこは怖いんだけど、エラーメッセージはわりと親切だしわかりやすいと思う。
今回は、コンパイルエラーが出なくなったらいきなり動いて、いきなり全部テスト通った。びっくりした。恐れ入った。
VSCode で書いているんだけど、環境は今ひとつかな。
Go みたいに
- まずいところは下線が付く
- 保存時に勝手にフォーマット
というようになると幸せになると思うけど、拡張機能の探し方が悪いのかそうはなっていない。残念。
※皆様はどんな エディタ / IDE で Rust 書いてますか?