はじめに
@Nemesis さんの Javaで湯婆婆を実装してみる が話題になっていたので便乗しました。
コード
yubaba.cpp
#include <algorithm>
#include <codecvt>
#include <iostream>
#include <locale>
#include <random>
#include <string>
using std::cout;
using std::endl;
int main()
{
setlocale(LC_ALL, "ja_JP.UTF-8");
std::string name_u8;
std::u32string name;
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> utf32conv;
std::random_device rnd;
cout << "契約書だよ。そこに名前を書きな。" << endl;
getline(std::cin, name_u8);
cout << "フン。" << name_u8 << "というのかい。贅沢な名だねぇ。" << endl;
name = utf32conv.from_bytes(name_u8); //名前を奪いやすくするためにUTF-32に変換
name = name.substr(rnd() % name.size(), 1); // 適当な一字を拾って名前を奪う
name_u8 = utf32conv.to_bytes(name); // UTF-8に戻す
cout << "今からお前の名前は" << name_u8 << "だ。"
"いいかい、" << name_u8 << "だよ。分かったら返事をするんだ、" << name_u8 << "!!" << endl;
return 0;
}
実行結果
千尋
契約書だよ。そこに名前を書きな。
千尋
フン。千尋というのかい。贅沢な名だねぇ。
今からお前の名前は尋だ。いいかい、尋だよ。分かったら返事をするんだ、尋!!
Chihiro
契約書だよ。そこに名前を書きな。
Chihiro
フン。Chihiroというのかい。贅沢な名だねぇ。
今からお前の名前はrだ。いいかい、rだよ。分かったら返事をするんだ、r!!
解説
名前を入力してもらう
getline(std::cin, name_u8);
std::cin >> name_u8;
としてそのまま入力を受けてもいいのですが、それだと何か入力しないと受け付けてもらえないので、getline()
を使用しました。
名前を奪う
name = utf32conv.from_bytes(name_u8); //名前を奪いやすくするためにUTF-32に変換
name = name.substr(rnd() % name.size(), 1); // 適当な一字を拾って名前を奪う
name_u8 = utf32conv.to_bytes(name); // UTF-8に戻す
入力されたマルチバイトな文字列から文字を抜き取るのには工夫が必要なため、一旦 1 文字あたりのバイト数が固定の UTF-32 に変換し、扱いやすくしてから一字奪っています。
一字奪うときの乱数生成機には random_device
クラスを使い、剰余演算で入力された文字列の中から奪えるように調整しています。
※剰余演算で調整すると偏りができるので、等確率な乱数を求めるなら uniform_int_distribution
クラスを使って値を絞ったほうが良いです。
おまけ
未入力時は rnd() % name.size()
の箇所で浮動小数点例外が発生し、ちゃんとクラッシュします。
契約書だよ。そこに名前を書きな。
フン。というのかい。贅沢な名だねぇ。
浮動小数点例外