悲劇は突然に
ちょっと前に「ズンドコチェック」と呼ばれるプログラムが流行った。
Javaの講義、試験が「自作関数を作り記述しなさい」って問題だったから
「ズン」「ドコ」のいずれかをランダムで出力し続けて「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら「キ・ヨ・シ!」って出力した後終了って関数作ったら満点で単位貰ってた— てくも (@kumiromilk) March 9, 2016
もう旬を過ぎたネタだが, せっかく Rust の勉強を始めたところなので Rust で実装してみよう。 先駆者がもうすでにいるけど。
ではまずズンドコを出力するために乱数を発生させて……と, Rust は標準ライブラリに疑似乱数がないのか。 外部クレートを持ってくるのが正しいやり方なんだろうけど, 正直こんなネタプログラムのためにそこまでするのも面倒だし, 簡単な疑似乱数モジュールを自前で作ってしまおう。
……よしできた。 あとはこれを利用すればいいだけだ。
fn zundoko() {
let mut rand = Rand::new();
rand.set_seed(753);
let mut zun_count = 0;
loop {
let result = rand.get() % 2;
if result == 0 {
print!("ズン");
zun_count += 1;
} else {
print!("ドコ");
if zun_count > 3 {
break;
} else {
zun_count = 0;
};
};
};
println!("キ・ヨ・シ!");
}
fn main() -> Result<(), Box<dyn Error>> {
zundoko();
Ok(())
}
こんな感じでいいかな。 では実行してみよう。
ドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコドコ
うわああああああああああああ┗(^o^)┛ドコドコwwwww┏(^o^)┓ドコドコwwwww
やり直すことができたなら
僕は死んだ。 実行した UNIX が極限負荷に耐えきれずばくはつし, それに巻き込まれたのだ。 もうゲーセンに行って音ゲーをプレイすることも, けものフレンズ 3 で遊ぶことも, Qiita でクソ記事を書くこともできないのだ(かなしい)。
こうなってしまった原因は, 疑似乱数モジュールに「線形合同法」を使用したことにほかならない。
struct Rand {
inner_state: u32,
}
impl Rand {
fn new() -> Rand {
Rand{inner_state: 12345}
}
fn set_seed(&mut self, seed: u32) {
self.inner_state = seed;
}
fn get(&mut self) -> u32 {
let temp = (self.inner_state as u64 * 1103515245 + 123456) % (1 << 32); // 線形合同法
self.inner_state = temp as u32;
temp as u32
}
}
カルドセプトサーガというゲームをご存知だろうか。 ゲームそのものの話題よりも, その乱数システムの不備で話題に登るゲームだ。 このゲームのサイコロは奇数と偶数が順番に出るのだ。
線形合同法では, 各定数を慎重に選ばないと, 奇数と偶数が交互に出たり, 奇数や偶数ばかりが出たりしてしまう。 Wikipedia にも書いてある。 そうなってしまったらズンドコチェックは永遠に停止することはない。 あなたは未来永劫氷川きよしに転生しつづけるのです!
ぼくは決心する。 必ずこの悲劇の運命を変えてみせる。
星の海を渡っていこう 振り向くことなく, 光を追い越し, 時を翔んで, いつまでも どこまでも
そしてたどり着く
ではまずズンドコを出力するために乱数を発生させて……と, Rust は標準ライブラリに疑似乱数がないのか。 外部クレートを持ってくるのが正しいやり方なんだろうけど, 正直こんなネタプログラムのためにそこまでするのも面倒だし, 簡単な疑似乱数モジュールを自前で作ってしまおう。
待ちなさい……。
うわあ, なんだこの「恋声」で無理やり女の子の声っぽくしたような音声は……。
乱数モジュールを線形合同法で作るのはやめるのです……。 なんかこう, XorShift とか使いなさい……。
ずいぶん曖昧なお告げだが, 下手に関わり合いになりたくないやつだし黙って従っておこう。
struct Rand {
inner_state: u32,
}
impl Rand {
fn new() -> Rand {
Rand{inner_state: 12345}
}
fn set_seed(&mut self, seed: u32) {
self.inner_state = seed;
}
fn get(&mut self) -> u32 {
let mut temp = self.inner_state as u64;
temp ^= temp << 13;
temp ^= temp >> 17;
temp ^= temp << 15; // XorShift
self.inner_state = temp as u32;
temp as u32
}
}
……よしできた。 あとはこれを利用すればいいだけだ。
fn zundoko() {
let mut rand = Rand::new();
rand.set_seed(753);
let mut zun_count = 0;
loop {
let result = rand.get() % 2;
if result == 0 {
print!("ズン");
zun_count += 1;
} else {
print!("ドコ");
if zun_count > 3 {
break;
} else {
zun_count = 0;
};
};
};
println!("キ・ヨ・シ!");
}
fn main() -> Result<(), Box<dyn Error>> {
zundoko();
Ok(())
}
こんな感じでいいかな。 では実行してみよう。
ズンドコドコドコドコドコドコズンズンズンズンズンドコキ・ヨ・シ!
よし, 完璧である。
こうして Qiita にクソ記事を投稿できる日々が戻ってきた
役割を終え, 剪定事象となった僕のアストラル体は徐々に崩壊していく。 薄れゆく意識の中で僕は思った。
停止しない可能性がある関数を提出されたら単位をやる前に一言くらい突っ込んでほしいぜ教授。