2023年03月20日
攻略編から長い時間が流れ何度も書こうとして書いたり消したり消えたりしましたが一応記録に残しておこうと思います。
僕がTODでカンストを目指そうと思ったのは新着さんの発言がきっかけでした。
その原因になったツイートがこちらです。
実際に9000を出してみるとeighさんが9512点で成仏してしまう気持ちも少し分かったけど、それでも俺の中ではeighさんが最もカンストに近い
— 1ip (@1ip) March 3, 2014
このツイートにより「もしかして自分でも1位を狙えるかも?」と思ったのでした。
これを見る前は9512点という点数で十分満足してましたし、当時の実力以上のスコアだったこともあり完全燃焼したつもりでいました。
しかしカンストという特性上達成したら「抜かれない1位」を達成できるフィールドが自分にあるということを自覚したことで目指す気になったのです。
しかしこの「抜かれない1位」というのはdqmaniacさんが達成したことにより「どうやっても達成できない1位」となってしまいました。
目指す1位がなくなってからやるかどうかは迷う時期もありましたが自分が1位を目指せるフィールドというのは中身がなくてもフィールドがなくなることはなく、なんとなく続ける日が続きました。
最寄りのTODでVERY EASYでカンストを複数回達成したり、秋葉原に行ったときにはNORMALをなんとなくプレーしてたりしました。
しかしなんとなくNORMALをプレーしても自己ベストすらも出ずモチベが上がらずクイズや敵を倒す順番を忘れたりすることもありました。
そんなこともあっても続けてきたのはやはり「自分が1位を目指せるフィールドがあった」ということです。
「1位」というゴールが消えてしまっても走り始めたフィールドでは走り続けるしかなかったのです。
こんなことになったのも新着さんのツイートが原因です。
よく言われる「呪い」というやつです。
そんなこんなでなんとなく走り続けてきたTODスコアアタックというフィールドですがなんとなく続けていたらポロッとカンストが出てしまいました。
出たときは「やっと終わった」という安堵感だったと思います。
1位ではなかったので当初の目的は達成できませんでしたが呪いのフィールドは脱出できました。
一応ローマ字だと1位だし、そうじゃなくても2位だし良かった良かったということで。
こんなところまで続けてこれたのもいろいろな人のおかげです。
dqmaniacさんを始めスコアアタックをやったり、スコアアタックのための情報提供をしていただいたタイパーの皆様
古いゲームにも関わらず長い間プレー可能な状態にしていただいたゲーセン、特にプレイ環境もよくよくプレーしていて達成する場所にもなったセガ秋葉原3号館
自分が1位を目指せるフィールドとなるゲームを作っていただいたTyping of the deadの制作チーム、特にプロデューサーのしいのまさみつさん
そして自分に世界一を目指せるフィールドが存在するということを気づかせてくれた東方新着マイリストるさん
に感謝したいと思います
2023/3/20 eigh
2020年04月14日
なんとなくJuliaのFizzBuzzでのコードゴルフをやってみました。
たまたまこのページ( 最短であってほしい Julia の FizzBuzz )を見つけたのでやってみようかと思いました。
この記事で最後に到達したのがこのコード
(i->println((x="Fizz"^(i%3÷2)*"Buzz"^(i%5÷4))>"" ? x : i+1)).(0:99)
67文字ですが2バイトの"÷"を使っているため69bytesです。
これを元になんとか縮めていきます。
まずこの"÷"の2バイトを1バイトの文字でどうにかできないか考えます。
ここでの(i%3÷2)
というのはiを3で割ったときのあまりが2のときだけ1でほかは0というのを表しています。
2値ということはtrue,falseで代用できそうです。
幸いJuliaのtrueとfalseはIntegerのサブタイプで実質的に1と0のみ取る整数という扱いができます。
というわけでjuliaのREPLでテストしてみます
julia> "Fizz"^(true)
"Fizz"
julia> "Fizz"^(false)
""
となり(1,0)じゃなく、(true,false)でも同様の結果が得られることがわかります。
というわけで(i%3÷2)
は(i%3>1)
と書き換えることができます。
これで67bytes(67文字)です。
(i->println((f="Fizz"^(i%3>1)*"Buzz"^(i%5>3))>"" ? x : i+1)).(0:99)
次になんかスペースが多いので減らせないかなと考えました。
というわけで三項演算子を使わず配列とインデックスで2つの分岐を行う作戦にしてみます。
そこでできたのがこれ
(i->println([i+1,"Fizz"^(a=i%3>1)*"Buzz"^(b=i%5<1)][a|b+1]).(0:99)
これは実は短くなっていなくて同じ文字数です。
残念でした。
インデックスのためのa,bを定義したりカッコが多くなってしまい短くはなりませんでした。
うーん、残念。
このコードを見て次の作戦を考えます。
i+1
の+1
って長いな。
というわけでインデックスをずらして(意図的にずらされていたのをもとに戻して)やってみます
(i->println([i,"Fizz"^(a=i%3<1)*"Buzz"^(b=i%5<1)][a|b+a])).(1:100)
これで1文字減って66bytesです。
やったぜ。
実は1:100のインデックスのやつ最初の三項演算子のバージョンでも同様に使えて1文字短くできます。
次に考えたのは「aとbの定義で結構長くなってるので省略できない?」です。
i
と"Fizz"^(i%3<1)*...
を比べてどちらかをいい感じに選んでくれれば良さそうです。
で考えたのはこれ
(i->println(max(i,"Fizz"^(i%3<1)*"Buzz"^(i%5<1)))).(1:100)
かなり短くなりましたが実はこれは動きません。
よく考えるとIntとStringをmax関数に突っ込んでます。
JavaScriptやPHPあたりならどっちか出してくれそうな雰囲気ですが…
まあこれはできないとしてこの路線を試します。
iをStringにしてやればmax関数が使えるのでこちらを使います。
(i->println(max(string(i),"Fizz"^(i%3<1)*"Buzz"^(i%5<1)))).(1:100)
これはちゃんと動くぞ!
ただし同じ66bytes…
なぜかコードゴルフをやってるとこういういい感じにブレイクスルーしたつもりが文字数が縮まってないことがよく起こります。
ここでstring()が長すぎるぞということで別のを探します。
いいのがありました。
(i->println(max(repr(i),"Fizz"^(i%3<1)*"Buzz"^(i%5<1)))).(1:100)
これで2文字縮まって64bytes
結構縮まりました。
ここで終わりかと思ったんですがこんなことしなくてももっと簡単に数を文字列に変更する方法がありました。
というわけでこれ。
(i->println(max("$i","Fizz"^(i%3<1)*"Buzz"^(i%5<1)))).(1:100)
というわけで61bytesになりました。
(追記)
ここまでできたのですがなんかもう少し調べてみたところ61bytesの物を見つけました
1バイト削るのに2日かかった…61bytes
— あんちもん2 (@antimon2) March 1, 2019
julia -e '1:100 .|>i->println((:FizzBuzz,:Buzz,:Fizz,i)[2i^4%5-~i^2%3])'
(てかまだやってたw)https://t.co/mmLqdvYqOY
パイプライン演算子を使うのは自分も考えたのですがなんか短くならなかった(というか長くなった)ので見送っていたのですがこうすると関数定義のカッコを減らすことができるため1文字短くできるようです
というわけでこれを取り入れると最終的に次のコードになります
60bytes
1:100 .|>i->println(max("$i","Fizz"^(i%3<1)*"Buzz"^(i%5<1)))
2018年12月14日
タイパーアドカレですがプレイヤー側の話が多いのでタイピングソフト制作側の話を書いてみたいと思います。
タイピングゲーム製作者がタイパーとは限らないですがタイピングソフトを作ってみたいタイパー向けです。
作る気がなくてもタイピングゲームの中身を知りたい人にもおすすめです。
ローマ字による日本語タイピングでは英語タイピングやかな入力による日本語タイピングと違って複数の入力パターンがあることが特徴です。
今回はそこそこ簡単に、まあまあいい感じにローマ字の打ち分けをする方法を考えていきたいと思います。
1. 英語タイピングの判定
まず表示されるワードと入力すべき文字が同じで、対象の文字を打つ方法が1つしか無い英語タイピングについて考えましょう。
タイピングゲームにはいろいろな方式がありますが、今回考えるのは正しい文字を打ったら次の文字へ進み、ミスタイプした場合はミス数が増えて行くものを考えます。
「word」という単語があった場合「w」を打ったら次の文字oが打つ対象になり、「o」を打ったら次へ進むといった感じですね。
用意された文字列の今見る位置の文字と入力された文字が同じだったら進む、違っていたら進まない(ミス)という比較的簡単な方法で作ることができます。
最後まで行った時間を測るか時間内にどれだけ進んだかをスコアにすればタイピングゲームの完成です。
ポイント1. 用意した文字と同じ文字が入力されたら次に進む
2. 日本語をやってみる
日本語入力タイピングと英語タイピングの違いとして表示する文字列と打つ対象のキー入力に対応する文字列が別であるということです。
「価格」というワードがあったら入力対象は「kakaku」という感じで別になっています。
「kakaku」というローマ字だけ扱うタイピングソフトは一般的ではないですね。
また、日本語タイピングでは同じワードでも複数の入力パターンがあります。
そのため、英語とは同じようにはいきません。
「価格」というワードですが
kakaku
kakacu
kacaku
cakaku
kacacu
cakacu
cacaku
cacacu
と8通りの入力パターンがあります。(今の所「qu」→「く」のパターンは無視します)
まず簡単に打ち分けなしで一番上の「kakaku」を入力するソフトを考えましょう。
「価格」というデータを用意してもコンピュータは勝手に「kakaku」というデータに変換してくれないので、打つ対象のワードとして「kakaku」をセットで用意します。
{「価格」,「kakaku」}こんな感じですね。こうすることで表示用は左側の漢字、入力用は右側のローマ字を使うようにします。
右側のローマ字は英語タイピングと同じように判定することができます。
ポイント2. 表示用のワードと入力用のワードを分けてペアにする
3. ローマ字を選べるようにする
このままでは「kakaku」1通りの打ち方しかできないので打ち分けに対応する方法を考えていきましょう。
「価格」には8通り(quに対応するともっと)のパターンがありますがそれぞれは「かかく」という読みをそれぞれ変換したものと考えることができます。
なので
{「価格」,「かかく」}
という漢字とひらがなのペアを用意してひらがなの方をうまく変換してローマ字を作ることにします。
よくあるタイピングソフトでは複数のパターンが存在するひらがなからローマ字への変換の方法を選ぶものがあります。
例えば今回の対象では「か」と「く」をそれぞれ「か」→「ka」と変換するか、「か」→「ca」と変換するかを選んでローマ字に変換します。
例えば「か」→「ca」、「く」→「ku」を選んだ場合「かかく」→「cacaku」と変換されます。
このように打つ対象をひらがなとして、ローマ字に変換するロジックを挟むとひらがなに対応するローマ字を選ぶことができるようになります。
実装するときの小ネタとしては「きょ」を変換したいときに先に「き」→「ki」と変換してしまうと「きょ」→「kiょ」→「kilyo」となってしまうので先に「きょ」→「kyo」と変換するようにする必要があったりします。
このような方式ではひらがなとローマ字が1対1で対応してしまうため「kacaku」など同じひらがなを異なるローマ字で打つパターンはできません。
同じひらがなをワードによって違うローマ字で打ったりとかもできません。
ただしひらがなとローマ字の対応を選べるようになりました。
ポイント3. ひらがなをローマ字に変換することでパターンを選ぶことができる
4. 打ち分けに対応する
やっと本題です。
上に上げた方法ではひらがなとローマ字が1対1対応してるため同じひらがなでもワードによって違うパターンで入力するといったことができません。
8通りのパターンを用意してそれを選んでもらうという手もなくはないですがすべてのワードを予めパターンを選ぶのは非常に大変ですし、毎回場合によって違うパターンを使う場合などには対応できません。
簡単に用意でき、毎回どのように打っても大丈夫なようにするにはどうすればいいでしょうか。
「かかく」というワードに対応する各パターンで重要な点は「cかk」どちらでも正解という点です。
なので「かかく」というワードがあれば最初の文字はcまたはkだったら1文字進め、それ以外はミスという扱いにすればいいということになります。
でどうするか結論を言ってしまうと「kakaku」と「cacacu」を用意してどちらかのローマ字に入力文字が一致したら次の文字へ進むという方法です。
まず1文字目に「k」が入力された場合「kakaku」の1文字目と一致されているので正解として1文字進めます。また、「c」を打った場合でも「cacacu」の1文字目と一致しているので1文字進めます。
kでもcでもなければミスタイプとして扱います。
ここで重要なのは「k」を打って「kakaku」の1文字目と一致して1文字進んだ場合に次の判定は正しく打った方の「kakaku」だけでなく「cacacu」の方も2文字目が対象となることです。
こうしておくと8パターンすべてで正しく文字を進めることができます。
このような判定方法を使うと今まで無視してきた「kakaqu」というパターンにも簡単に対応することができます。
「kakaku」,「cacacu」,「kakaqu」を用意しておけば「cakaqu」のように打っても問題ないということになります。
「kakaku」,「cacacu」,「kakaqu」の3つのパターンは「かかく」からそれぞれプログラムで作ることができます。
このようにひらがなを「か行をkで打つパターン」と「か行をcで打つパターン」と「くをquで打つパターン」を用意しておけば入力したときにパターンを選ぶことができるようになります。
ここまででローマ字の打ち分けが完成です。
ポイント4. 複数の打ち方を用意しておき、どれかと一致したら正解とする
このままでもいいのですがいくつか問題があります。
細かい実装方法は書きませんがいくつか問題点のみ挙げていきます。
1. 2文字以上のまとまりで打ち分けがある場合
例えばこのような方法では「かちょう」というひらがなでは「kachou」と「catyou」のパターンが作られますが「kathou」と入力しても正解とされてしまいます。
「kathou」と打った場合に変換されるひらがなは「かてょう」です。ミスとして扱ってほしいですね。
他にも「がっこう」とかも「gakcou」で通過できてしまったりします。
なのでこういう場合はどちらを打ったか固定するなど細かい対応が必要になったりします。
2. 文字数が変わる場合
「じゃ」をローマ字に変換する場合「ja」と「zya」がありますが文字数が違うため最初にjを打ってもzを打っても反対側のパターンでは文字数がずれてしまいます。
このような場合ではこれまでの方法は使えません。
タイプウェルの場合はzを打つと表示されていたjがyに変わってyを打つようになってますね。
他にも「ち」→「ti」,「chi」などがあります。
1文字程度の文字数の違いは例外を入れて対応できますが、大きく文字数が変わる場合(「jilya」など)はこれまで書いてきたロジックを細かく対応させるより別のロジックを使ったほうがいいでしょう。
タイピングソフト制作の方針としてこのように文字数が変わる場合は対応しない(少ない文字数のものを強制する)というのもアリでしょう。
以上そこそこ簡単に、まあまあいい感じにローマ字の打ち分けをする方法でした。
これを見てタイピングソフトを作る人が増えればいいなーと思います。
ソフトを作らない人も一見正しいローマ字しか受け付けないように見えるよくできたタイピングソフトでも実は間違ったパターンで入力できたり面白い部分が発見できるかもしれないのでいろいろ試したり、ロジックについて考えてみるのも楽しいのではないでしょうか。
2017年10月13日
まずTODでスコアを上げるためにはどうやったらいいかがわからない場合はdqmaniacさんがスコアアタックについてまとめたページを読んで下さい。
大雑把に言うと
1. 正確に(1ミスもせず )
2. 速く(ミッションがクリアできる程度に)
3. 運良く(得点になるアイテムを入手する)
クリアすればカンストが出来ます。
それを上げるためにどうやって来たかを書いてみます。
まずTODスコアアタックで一番重要なのが正確性です。
とにかく1ミスでもすると場所によっては2000点程度の失点になることがあります。
ミスしないようにしましょう。
と書くのは簡単ですがそれのためにどうするかを書くべきですね。
基本的に僕の正確性と言うのはタイプウェルのミス制限練習でベースを作って後はTODのスコアアタックを何回もやって鍛えた感じです。
ミスを抑えて打つ練習ではなくミスをしないように打つ練習をするのがコツです。
タイプウェルのミス制限練習は4でしたがミスは基本的にしないつもりで練習してました。
正確性を上げる練習をするのはどれでも良いと思いますがミスをしないで打つ能力を鍛える練習をまずしましょう。
重要なのはミスをしないで打てる速さを把握するということです。
ある程度正確性が上がったらミスをしないで打てる速さを把握するための練習をするのもいいでしょう。
速度に関してはTODは一部以外はかなりゆっくり打っても大丈夫です。
自分の感覚ではタイプウェルのXFくらいのペースで打ってるつもりです。
Bランクを出すと少し点数は下がりますがコンボを切る失点に比べると微々たるものなので気にせず行きましょう。
次は正確性を上げる練習ではなく正確に打つための方法です。
まずTODで注意しなければ行けないのは一部の組み合わせの同時押しをすると正しく打鍵した場合でもミス判定されるものです。
以下の組み合わせ(他にもありますが把握してません)を同時に押すとミス判定されてしまいます。
ASI,AMO,AYO,RYO,EBI,AJI,SA-,SETなど
基本的には3つ以上同時にキーを押さないかパターンを覚えてそこだけ同時押しを避ける方法があります。
他にも基本的に同時押ししない速さで打って速さが必用なところでは同時押しで死なないパターンを待つという運に頼る方法も一応あります。
まあ危険なパターンが存在するというのは覚えておいた方が良いです。
あと基本的に打ち分けができる物ではYよりHを使うパターンのが安全な事が多いです。
SYASYUSYO,TYATYUTYOよりSHASHUSHO,CHACHUCHOなどを使うほうが安全です。
プレーするときも同時押しでミス判定されるパターンがあるということを覚えておいてミスったつもりがないのにミス判定されたといった場合には文字列を覚えておいて同時押しで死ぬパターンを覚えていきましょう。
次に最適化のお話です。
僕は標準運指ベースで最適化を取り入れて結構最適化をしている方ですがTODではタイプウェルとは違う最適化を結構使っています。
タイプウェルでは速さのための最適化をしていますがTODではミスしない最適化をしています。
TODの運指で意識しているのはKYを同時押しするとかなりの確率で巻き込むのでK Yと別に打ったりしています。
Yを左手で打つという選択肢もありますが(タイプウェルでは基本的にこっちを使っています)左手からYが遠くミスしやすいのでTODではタイプウェルほどYを左手で打ってません。
左手からYより遠く見えるHやNですがプレーしてるときの感覚としては近いので結構左手でも打ってます。
左手で打つ場合YよりもHやNの方が指の動き的にミスしにくいような気がします。
右手だけでガチャガチャやってる中に1回だけ左手を使うようなワードでは左手を使っても(使ったほうが?)安全です。
TODには左手で打つのが有効なワードも多いです。
TODでは基本的にローマ字は自動認識してくれて打ち分けも大体出来ますがAC版ではCACUCOが使えません。
僕はタイプウェルでは普通にCACUCOを使って無意識に使い分けてますがTODで使わないように練習しました。
C打ちが無意識に出てしまう状態からK打ちを意識的に打っている状態です。
流石にもう慣れてCを打ってしまうことはもう無いです。
Cが打てないことで非常に遅くなっているのがKO-です。
タイプウェルではCO-を打っているので打ちやすい(Oを中指、-は薬指)のですがTODではKを使うため違う運指で打たなければなりません。
KO-を人差し指、中指、薬指で打つという方法もありますがSUKO-RU,ANKO-RUなど運指が破綻してしまう単語もあり迷わないようにKO-人中中で打つように練習しました。
速くは打てない運指ですがこれで間に合わないようなパターンは無いのでOKです。
まあこういう最適化の中身は個人に合ったものを選べばいいでしょう。
とにかくミスをしないように最適な運指を新しく練習する必要があるということです。
速さは二の次、とにかくミスをしない運指を練習する。
速さが間に合わないなら次のチャンスを待てばいいだけです。
次に速さについてです。
速さに関してはミッションはXSくらいの速さ、他はXFくらいの速さがあれば大丈夫なんじゃないでしょうか。
個人的にはこのくらいの感覚で打ってます。
エンペラーはもう少し速いかもしれません。
スコアアタックを始めた頃には充分な速さが身についていたのでスコアアタックのために速く打つ練習はしてません。
速さが厳しいところでは初速を意識するくらいでしょうか。
速さというかダメージを避ける方法についてですが基本的には数をこなしてどこが速く打たないとダメージになるかをしっかり覚えておくことが重要です。
ダメージのタイミングなどは回数をこなして覚えるしか無いですが難所くらいは把握しておきましょう。
それ以外の場所では速く打つ必要はありません。
どの程度でダメージになるかを覚えておけばどのくらいのペースが必要かを把握できるので便利です。
クイズは覚えましょう。
最後に運です。
ミッションで外れワードを引いて殴られても
同時押しで死ぬワードに引っかかっても
タワーでよくわからないクイズが来ても
エンペラーで得点にならない形態が連続しても
ミッションで鎮静剤を引いても
やり続ければいつかは当たりを引けます。
やり続けましょう
2017年09月13日
eigh@eigh8_t自己ベスト更新です https://t.co/0fDGW1dXSm
2017/09/09 20:22:57
場所はクラブセガ秋葉原3号館(新館でしたが最近名前が変わりました)です。
同じ場所で9512を出してから3年以上経ちましたがここまで続けてきてよかったです。
--プレー前後の記憶--
最近はTODをプレーするのもそれほど「カンストしたい」みたいな感じはなく適当にやっていい感じの記録が出たらいいなーとかそろそろ自己べは更新したいなーとかそんな感じでした。
この日もなんとなくプレーをはじめました。
比較的最近キーボードが交換されたのに意外と表面がすり減って滑りにくくなったなあと思ったのは覚えています。
とりあえずいつも通りに5章からスタート。
特に書くようなこともなく「意外とマジシャン後半で失敗するから気を抜かないように」くらいしか思ってませんでした。
とりあえず普通に89コンボでクリア。
89コンボで5章クリアは5割くらいの成功率があるので普通です。
6章序盤では普通にミスしないように慎重に打ちました。
ここで打ちにくいワードで2つくらいBランクを出しましたがこれはちゃんとブレーキが効いているのでいい傾向です。ちなみに遅すぎたのでEscしたワードを打ち直したらBランクというのもありました。
ちなみに9512点を出した時は確か最後のワードをBランクで打ったために豆大福さんの記録に15点及ばなかったという事があります。
エレベーター前後で謎のミスからEsc打ち直しで被ダメージということも少なくないのですが今回はハズレワードを引かなかったため難なく突破。
次に難関のミッション1です。
ミッション1では少しギアを上げて打つ。運良く外れワードを引かずクリア。アイテムはダイヤ。ここまでを完璧に進めることは非常に稀でこの辺りからカンストが狙えることを意識し始めました。
ダイヤを引いたので次の攻撃でダメージを受けないように注意しました。ワードが出る場所とタイミングに注意して反応が遅れないように。
研究所のアイテムはライフ。ハズレを引かず一安心です。直後のゾンビも大丈夫でした。
次は運ゲーのタワー。アメリカ大使館を見落としそうになりながら(日本大使館をEscして)も運ゲーを抜ける。「運ゲー突破した」の感想が強すぎてここではカンストのことは忘れていました。
タワー直後の雑魚は「ここはちょっと油断するとダメージだから反応が遅れないように」と思いつつ抜け。
その次はダメージの危険は非常に低いものの間違えないように慎重に行きました。6章序盤と同じようにブレーキの効きが非常によく、危ないところで停止することがありました。Bランクも出してます。
ミッション2。
この辺りでは完全にカンストのことは頭にありました。ただここはコンボをつなげるために慎重に打とうとして指が迷って間違えることが怖かったのであえて攻めました。自信を持って集中して打てば少し攻めてもミスらないだろうみたいな感覚がありました。あと撃破数が少ないと点数が足りないかもしれないという考えもありました。
結果13体撃破(ギリギリだったので14体目の撃破ボーナスは入らず)。アイテムはダイヤ。
ここまでくれば後はエンペラーをいい感じに倒せばカンストです。
指が震えることもなく、心拍数が上がることもあまり感じませんでした。
エンペラー戦開始
最初の3連
エンペラーで非常に怖い最初の3連ですが、いい感じに打ち切りかなり気が楽になりました。
剣
スコアアタック的には美味しい剣攻撃が来ました。ミスのリスクもありますがブレーキをかけすぎると空回りしてミスしたり普通に殴られるリスクがあるので集中して打ちました。
剣
連続で剣が来てスコア的に嬉しい半面やはりダメージやミスが怖いです。打ち切れました。
剣
3連続で来てしまいました。スコア的には…。このあたりになると速く4同時で外れるパターン来て難易度下げてくれと思ってました。
剣
なんと4連続で来てしまいました。とにかくミスしないで打ち切るしか無いです。打ち切らないと次のはみ出し回復はありません。反応が遅れないように注意しつつ打ち切り。
3連
最後は3連が来ました。2つ目のワードではみ出してスコアにならない3つ目を打たずに剣を待つという手もありましたが剣をたくさん打っていたのとそんなことを考える余裕がなかったので打ち切ってしまいました。
エンペラー後半
タワー
ここまで来るともうはみ出し回復は出来ないのでミスしないことを注意してました。ただ長いワードを打ったほうがお得なので普通に(あえてダメージで難易度を下げたりせず)打ちました。そしたらなんと相打ち。これスコア的にはかなり美味しいんですよね。
何か(短め)
短い文はスコア的には少しアレですがゴールに近づきやすいのともうすでにカンストにはスコアは足りていた感じだったので嬉しかったです。
ストレングス
相打ちを一番狙いやすい(タイミングを合わせやすい)のでちょっと狙おうかと思いましたがそんな余裕もなく普通に打ちきれず被ダメージ。ライフに余裕があるので大丈夫です。こういうところで焦ってミスしないあたり非常に落ち着いていたと思います。
残り
あまり記憶に無いのですがライフもあったしすでに2被ダメージで難易度下がっていたのでミスを巻き込んだままワードを消さないようにやや慎重に打っていました。ような気がしますがあまり記憶がありません。
最後のクイズ
いつもどおりのワードを打ちました。ミスを巻き込んだまま打ち切らないように最後には一時停止をしてコンボ数を確認してからキーボードを見ながらキーを打ちました。
最後の問題は「超能力を使えるとしたら〜」でした(たぶん)。
最後のキーはちゃんと目で確認しながら打ち切り。プレーは終了。結果を待ちます。
せっかくだったのでスタッフロールも打ちます。スタッフロールで1回間違えた(ミス音が聞こえた)のですがまあ得点に関係が無いのでどうでもいいでしょう(一応Escして打ち直しました)。
スタッフロールが終わり結果画面が表示されるのを待ちます。
5章からフルコンボ、アイテムやエンペラーの運もよくカンストが出るのは確信していました。
スマホを取り出しカメラを起動して結果画面が表示されるのを待ちます。
結果画面を見た瞬間にガッツポーズが出ました。
ランクインした時のコメントではあまり考えられずにシンプルに喜びを残しておきました。
ランキング画面で1位9999ptsを確認。
ずっと見たかった画面。
2016年03月19日
面倒だったしあまり意味はなさそうなので写真などは無いです。
とりあえず長くなったので買うか迷ってる人のためのまとめ
見た目はあまり悪くないです。1万円の時計と言われてがっかりするような質感では無いです(activite popより高級感はあります)。
連携アプリはかなり怪しいです。Androidは権限の管理とかすればギリギリ?iOSは審査も通ってるのである程度安心?
最初から電池が減っている場合があります。電池が減るとただの時計になります。
歩数計はやや少なめに出る気がします。
電池の問題かわかりませんが一部の機能が動かないです。
結論:セキュリティ的に問題ない端末とペアリングして時計として使えるおもちゃが欲しい以外なら買わないほうが良いです。
電池を交換したら歩数計の誤差が少なくなって通知類も来るようになりました。交換費用はヨドバシカメラで2000円ほどでした。最初から電池がかなり減っている場合があるので調子が悪い場合は電池の交換を検討してみましょう。
ここから適当な話
値段はexpansysで送料含めて1万円ほどです。
なんか安かったので勢いで注文。
注文したら3日か4日ほどで届きました(詳しいことは忘れました)。
これだけ買った場合は税金はかかりませんでした。
届いてから
まず箱を開ける。
高級感はそれなりにあります。
1万円の時計としてがっかりするような質感ではないです。
意外と悪く無いです。
バンドも革製で安っぽい感じはないです。
まあ気に入らなければ交換すればいいでしょう。
同梱品は分解用の工具と説明書です。
説明書を読んで連携アプリの確認。
ここから連携アプリの話
googleplayにはなく野良アプリです。
まあ想定内です。
説明書にQRコードがあるのでそこから適当にダウンロードする感じです。
apkのダウンロード先が怪しいですURLが
***.com/***.com/***.apk
みたいな感じです。この時点で怪しさマックスですね。
とりあえずダウンロードしてインストールします。
インストール時に怪しい権限が要求されます。予想通りですね。
ちなみに要求される権限は予想通りに
・電話帳
・SMS
・ネットワークのフルアクセス
・bluetoothの設定
・システム設定の変更
は当然のようにありますが更に
・ストレージへのアクセス
があります。珍しい権限ですね。危ない匂いがすごいです。
まあスマートなウォッチとして使えないのでインストールしちゃいます。
権限は止めちゃえばいいし。
と、ここで思いましたが権限をコントロールできるような端末以外にインストールするのはかなり危険です。
よくわからない人はやめておきましょう。
とりあえずインストールしたら開く前に権限の設定をしておきます。
まあその前にバックグラウンドで権限を止める前に怪しいデータを送信される危険性はあります。
慎重になるならネットワークを止めてからインストールをしてそこから権限を止めるくらいしても良いかもしれません。
まあここまで進んだらアプリを起動します。
メニューとかが中国語です。
とりあえずペアリングをします。
よくわかりませんがとりあえず見れる設定とかを見たら電池残量が20%ほどでした。倉庫に眠っている間もスマートウォッチとして動いていたと考えるとまあしょうがないような気もしますがペアリングするまでは止めておいても良いような気もします。損した気分です。
アプリをいろいろいじっていたらなんかアプリのバージョンアップができるところを見つけました。
とりあえずアップデートしてみます。
そしたらなんとそのアップデートのapkのダウンロード先が内部ストレージです。怪しさしかありません。
URLがhttp://***ではなくstrage0/***みたいな感じです。非常に危ない感じしかありません。
ちなみに最初入れた端末だとこれに気づかず違う端末に入れた時にこの事に気づきました。
Android6.0以上(だったっけ?)で権限設定でストレージのアクセスの権限を止めておくとアップデート出来ません。もうこの時点でアプリに対する信頼度は任意のε未満です。
まあとりあえずアップデートします。危ないですが。
アップデートしたら電池残量が%表記ではなくnormalと表示されるようになりました。しばらく使ったらlowみたいな表示になったのでなんかわかりにくくなったのでアレですね。
ちなみにバックグラウンドで通信していたので安全性は完全にアウトですね。
とこのくらい書けば連携アプリがどのくらい危ないかわかると思います。わからないようならそういう人は買わないほうが良いです。
iOSの連携アプリは審査も通っているのである程度は安心できるかもしれませんがまあ自己責任でお願いします。(ちなみにアップデート確認でアップデートが存在しなくてもappstoreに飛ばされます)
ここまで連携アプリの話
ここから機能の話
はい、連携アプリの話はこのくらいにしておいて実際に使った感じの感想です。これまでに使ったスマートウォッチ的なデバイスだとhuaweiのtalkband b1があるのでそれとの比較した感じで。
スマートウォッチ的な機能として以下のものがあります。
・活動量計
・電話の通知
・スマートアラーム
くらい?買ってから気づいたんですが睡眠ログとか無いんですね。この手のものには標準装備なので意外でした。
活動量計ですが歩数がやや少なく出るようです。huaweiのtalk band b1とくらべて2割から3割ほど少なく出ます。まあ相対的に測るなら問題なさそうですね。時計をダブルタップするとおおまかな活動量がLEDで表示されます。talkbandでボタンを何回か押して歩数を表示させるより簡単なので便利ですね。細かくは出ませんが。
電話の通知はちゃんとしてくれるようですが電池が少ないとダメみたいです。購入直後に交換が必要かもしれません。ただ振動が弱めで気づきにくいです。
スマートアラームはなんか設定しても動かないです。不良品?と思いつつもなんかいろいろやってたら振動はしたのでモーターが壊れているわけではなさそう。
電池を交換したらちゃんと機能するようになりました。
結論:機能としてはまあまあですがアプリが非常に危険なので取り扱いには注意が必要ですね。
2015年11月17日
1. ローマ字入力関連
TOLの入力はちょっと特殊で入力されたアルファベットをひらがなに変換して打つべき文字と同じかどうかを判定しているようです(たぶん)。
それの判定アルゴリズムの問題で正しくひらがなに変換できないようなローマ字入力でもゲームとしては正しい入力として処理される場合があります。
というわけで見つけたローマ字的には正しくなくてもゲーム的には正しい入力方法で見つけたやつをまとめます。
1.1 大文字の扱い
英語を打つときは大文字小文字は区別しないといけませんがひらがなを打つときは大文字でも大丈夫です。なので「ねこ!ねこ!ねこ」というワードをシフト押しっぱなしで「NEKO!NEKO!NEKO!」と打っても問題無いです。
1.2 「ん」関連
普通のローマ字入力では「あ行」や「な行」や「や行」や「にゃ行(?)」では「ん」を打つためには2回Nを打つ(またはXN)必要がありますがこのゲームでは1回で打てる場合があります。
「な行」や「にゃ行」のNを三連打するパターンでは普通に3回打たなければいけませんがそれ以外では1回のNで打てませんがそれ以外だと「んい」を「NI」と打てたり「んや」を「NYA」と打てたりします。
Nを三連打する場合も「n;」などと関係ない文字を打つことで「ん」が確定されて「んの」を「n;no」と打てたりします(;はミス扱いされるので注意)。
あと大半のIMEや一部のゲームで使える「n'」という入力方法は使えない(とはいえ上の例の;のようにミスが増えて「ん」が確定されるので一応使えないとはいえない)です。
1.3 「っ」関連
基本的に「っ」は同じキーを連打したら「っ」に変換されるようです。というわけで次に打つべきキー以外の適当なキーを二連打しても大丈夫な場合があるようです。
大半の場合は次の文字を打つときにミス扱いされて速くもないのであまり使えませんが有効な例が少しあります。
「っい」→「lli」
「っ?」→「ll?」「???」
「っ!」→「!!!」
「っな」→「nna」
等
どういうわけかわかりませんが上のように打てます。「l」の代わりに「k」とか適当なキーでも大丈夫だったと思いますが「aiueo」あたりはダメだった気がします(大丈夫かもしれないです)。
「っな」なんて出てくるワードあるの?と思うかもしれませんが翡翠の反転技で出てきます。
2. 状態を変えるSP技について
さつきのパワーアップやアルクや式のデッドライン固定、琥珀のブラインドなどステータス異常(自分のでも相手のでも)を起こすSP技は基本的に上書きができます。
というわけで琥珀のブラインドをアルクのデッドライン固定で解除できますし、さつきのパワーアップをシエルの無色化や翡翠の反転で上書きすることができます。
唯一の例外はアルクのデッドライン固定は式のデッドライン固定で上書きができないというパターンだけです。式のデッドライン固定中にアルクのデッドライン固定は上書きできます。
なので特技を発動するタイミングが悪いとあまり効果が無いということもあります。
これらを知っておくとちょっと対戦で対戦で有利になるかもしれません。
2015年09月19日
デスマコロシアムはコードゴルフとその他諸々の要素を組み合わせて戦うトーナメントです。
詳しくはググればいいと思います。
結果は目標の言語内最短を出すことができました。
そこまでにどうやって縮めていったかを書いていきます。
最終的なコードが見たい人はCodeiqMAGAZINEを見れば大丈夫です。
今回の問題は次のようなものでした。
abCDEfghIjklmnOpQrstuvwxyz
abcDEFghiJklmnoPqRstuvwxyz
abcdEFGhijKlmnopQrStuvwxyz
abcdeFGHijkLmnopqRsTuvwxyz
abcdefGHIjklMnopqrStUvwxyz
abcdefgHIJklmNopqrsTuVwxyz
abcdefghIJKlmnOpqrstUvWxyz
abcdefghiJKLmnoPqrstuVwXyz
abcdefghijKLMnopQrstuvWxYz
abcdefghijkLMNopqRstuvwXyZ
abcdefghijklMNOpqrStuvwxYz
abcdefghijklmNOPqrsTuvwxyZ
abcdefghijklmnOPQrstUvwxyz
abcdefghijklmnoPQRstuVwxyz
abcdefghijklmnopQRStuvWxyz
abcdefghijklmnopqRSTuvwXyz
abcdefghijklmnopqrSTUvwxYz
abcdefghijklmnopqrsTUVwxyZ
abcdefghijklmnopqrstUVWxyz
abcdefghijklmnopqrstuVWXyz
abcdefghijklmnopqrstuvWXYz
abcdefghijklmnopqrstuvwXYZ
を出力。実際にはアルファベット1周ごとの改行は必要ないです。というかしてはいけません(見やすいように勝手にアルファベット1周ごとに改行を追加してます)。
※最後に改行はあってもなくても良い。
内容は以下のとおりです(CodeiqMAGAZINEの記事より引用)。
"はじめは、aからzの文字でcodeiqに一致する文字のみを大文字に変換します。
次にaからzの文字でdpefjr(codeiqの次の文字)に一致する文字のみを大文字に変換します。
という風に、ループする度に大文字に置換する位置をずらす処理をaからzに対して22回繰り返します。"
ちなみにこの"codeiq"に一致したら大文字というルールは45byteの解ができるまで気づかずランダムなパターンだと思ってました。
まあこれが結果的に良かったのかもしれません。
まず考えたのは次のようなコードです。
a=[]
[2,3,4,8,14,16].map{|i|a[i]=1}
22.times{|i|26.times{|j|$><<(a[j-i]?j+56:j+97).chr}}
(改行が2byteとして全部で90byte)
これを動かすと間違った出力になります。
aは
[nil,nil,1,1,1,nil,nil,nil,nil,1,nil,nil,nil,nil,nil,1,nil,1]
となっていてa[j-i]が真(Rubyではnilとfalse以外は真です)だったら大文字、nilだったら小文字を出力です。
この配列の2,3,4,...に入れる値はnilとfalse以外ならなんでも良いですが文字数が短いので1桁の数を入れます(0でも大丈夫です)。
と思ったのですがj-iがマイナスの値になると配列の後ろからアクセスしてしまうためほしくない1が取得されてしまいます。
これを避けるためにa[99]=nilを加えて回避ということもできますが結構長くなりますね。
しかしaを配列ではなくハッシュにするだけで回避できます。
というわけでa={}とすればちゃんと動きます。
次に考えたのはループを二重ではなく1重(?)にする方法です
22.times{|i|26.times{|j|(なんとか)}}
ではなく
572.times{|i|(なんとか)}}
とするとまあ変数の使い方は変わってしまいますが結構ループの構造を短くすることができます。
a={};[2,3,4,8,14,16].map{|i|a[i]=1}
572.times{|i|j=i%26;$><<(a[j-i/26]?j+65:j+97).chr}
(87byte)
と3byte縮めることが出来ました。(と思ったら1つ改行の代わりに;を使っている(なぜ?)ので実質2byteです。←たぶんまだ途中だと思っていたので適当にやっていたんでしょう)
とこんな面倒な方法を考えていたのですが、このコードが何をしているかというとj-iが[2,3,4,8,14,16]に含まれているかどうかを探しているだけという事に気づきました。
というわけで直接探せる
[2,3,4,8,14,16].index(j-i)
を使います。
これなら扱うものはハッシュではなく配列ですがj-i<0でも全く問題ありません。
返ってくる値は先ほどと違いますがnil,falseとそれ以外が判断できればいいので問題ありません。
面倒な配列の宣言や書き換えがなくなって一気に短くできます。
というわけで次のようになりました。
572.times{|i|j=i%26;$><<([2,3,4,8,14,16].index(j-i/26)?j+65:j+97).chr}
(70byte)
ブロックも一つになっていい感じです。ここで満足して1回目の提出をしました。
テキストエディタを見るのも飽きてきたので過去のデスマコロシアムのまとめを読んでいたらputcを使う方法を見つけました。
$><<a.chr
を
putc a
と書く方法です。この例だと3文字しか短くなりませんが今回の場合は()も減らせるので5byte短縮です。
というわけでこうなりました
572.times{|i|putc (j=i%26)+(([2,3,4,8,14,16]&[j-i/26])[0]?65:97)}
65byteで提出です。
a.index(n)と(a&[n])[0]が同じ文字数だったので適当に書き換えてみました。
その他にも色々(jの宣言とか)違いますが5byte縮めた要素はputcを使っただけです。
これを出した直後にもう1つ縮める要素を見つけました。
出した直後に改善案が見つかるのはマーフィーの法則からすれば妥当なところです。
jはわざわざ宣言する必要はなくi%26をそのまま使えばよかったのです。
というわけでjに関するところをi%26に書き換えれば1byte短くなります。
というわけで64byteで提出しました。
ここで1回目の集計です。
できれば言語内最短、あわよくば全言語内最短という目標でしたが後者は達成できず(Perlで60がいた)、前者だけ。まあ目標が達成できたので良しとします。
次の目標はPerl(60)です。なんとか4文字短くしてみましょう。
とりあえずいまのコードを短くする方針を考えます。
なんとかPerl(60)に追いつこうと色々考えていたらちょっと短くする方法が見つかりました。
([2,3,4,8,14,16].index(i%26-i/26)?65:97)
を
([i%26-i/26,97,65]-[2,3,4,8,14,16])[1]
に置き換えます。配列の引き算を使います。
i%26-i/26が右側の配列に含まれるときはその要素が消え[97,65]という配列が返り、含まれない場合は[i%26-i/26,97,65]になります。この配列の(0から数えて)1番目の要素を取れば97か65の欲しい方が取得できるわけです。
これで2byte縮まります。62byteでまだ目標の60byteには届きません。
なんかすごい綺麗になったと思ったのですが意外と縮まらないものですね。
綺麗なアルゴリズムでコードが縮んだと思った時に限って文字数はあまり変わらない法則の発動です。
次に気になるのはi%26が2回出てるということです。できれば1文字の変数でなんとかしたいですね。
jを用意するのは失敗だったのでループ内変数のiを破壊してなんとかする方法にします。
次のループでは新しく正しい値が生成されるので破壊してもあまり問題無いです。
i%=26とすれば次にiを使うときには最初でいうi%26と同じになります。
というわけでこれらを用いるとループの中身は
putc ([i%26-i/26,97,65]-[2,3,4,8,14,16])[1]+i%26
から
putc ([-i/26+i%=26,97,65]-[1,2,3,7,13,15])[1]+i
になりました(1byte短縮で61byte)。
ちなみに-i/26は-(i/26)ではなく(-i)/26なので探す数は1つずれます。
"a"を出力するときだけずれないので問題ありそうですが今回はaが大文字になるケースが無いので問題無いですね。
60byteが目標でしたがここで力尽きてこの日はこれで提出。
ここで2回目の集計です。
Perlは(59)になり、Rubyは全言語最短の(56)になりました。言語内最短も陥落です。
ボツネタ
1,2,3,7,13,15って,使いすぎなので文字列にしてつないだらどうだろう?
"12371315".index(n.to_s)
と.to_sが長いためアウトです。しかもこれn==5で引っかかってしまいます。ダメ。
10進数を使うからダメなんだというわけで16進数を使います。幸いRubyでは16進数とかに簡単に変換できます。
"1237df".index(n.to_s 16)
1文字長くなってしまいましたがこれなら通ります。しかし
[1,2,3,7,13,15].index(n)
のが短いです。ダメでしたね。
"0111000100000101"[n]
を使ってみる。
→返ってくる値は"1","0"の文字列です。このまま使うのは厳しそうですね。to_iすれば使えますが長いですね。
"0"や"1"は数値と四則演算はできません。
あとn<0の時は逆からアクセスしてしまうのでダメです。
ここで一つひらめきました
このビットパターンを持つ数値とビット演算すればいいじゃん。
上の2進数の文字列は左から数えているので逆にしないといけませんがその2進数で表される数値を使います。
というわけで上の文字列を逆順にした物を10進数の数値に変換してみましょう。
irbを使って計算してみました。
#1と0で書かれた文字列を2進数と見て数値に変換する
"1010000010001110".to_i(2)
=>41102
というわけでこの数値を使います。
これの右からnビット目が1か0を調べたら良さそうです。というわけで
41102&(2**n)>0?
とかでしょうか。9byteも縮めることができます。やったぜ。と思ったらn<0でエラーですね。
41102&(1<<n)>0?
なら問題なく動くのでこれで良いようです。
ここまでかと思いましたがRubyにはもっと便利なメソッドがありました。Integer#[]というメソッドがあり、これは右から(0から数えて)nビット目の数値(返ってくるのは0または1の数値)を取得することができます。
さらにnがマイナスの場合は常に0が返ってきます。というわけで
41102[n]>0?
と更に短くすることができました。
572.times{|i|putc (41102[-i/26+i%=26]>0?65:97)+i}
(49byte)
というわけで50byteも切って大満足です。最高記録です。
ここでまた一つ気付きました。
返ってくるのは0,1(数値)なので配列のインデックスとして使えばいいじゃんということです。
572.times{|i|putc [97,65][41102[-i/26+i%=26]]+i}
(48byte)
短くなりました。すごく綺麗になったと思うのですが綺麗になった割には1byteしか縮まってないですね。
まだ括弧が多いような気がしますね。
さらにもう一つ気付きました。
0,1は配列のインデックスじゃなくて計算式に突っ込めるじゃん。
というわけで最終的に出来たのが次のコードです。
572.times{|i|putc 97-41102[-i/26+i%=26]*32+i}
(45byte)
48byteのコードのほうが美しいと感じますが短くなったので正解です。
もうこれ以上はムリだろうと思って提出しました。
ここで3回目の集計。
集計時点で全言語最短を達成です(この時点では単独1位でした)。
目標の(途中経過での)全言語最短の達成です。
オツカレサマデシタ。
ちなみにこの全言語最短記録は一晩でRubyで追いつかれ、数日でPerlによって抜かされてしまいました。
残念でしたね。
Rubyが全言語最短になれる問題だと思ったのに…
時は流れ…
最終結果はこちら
というわけで結果はRuby内最短でした(トーナメントはいきなり負け)。
目標の言語内最短と途中集計で全言語最短ができたので良かったです。
おまけ
1.Ruby(45)の別解
別解というほどでもありませんが
572.times{|i|putc 41102[-i/26+i%=26]*32^97+i}
としても同じ結果になります。中身がどうなっているかは簡単なビット演算の問題なので自分で考えてみましょう。
2.Cに移植してみる
なんか行けそうだったのでCにも移植してみました。
で、コードはこれ
i;main(){for(;i-572;putchar(i%26+(82204&1<<i%26-i++/26?65:97)));}
ループの中でiを破壊できなかったりするのでちょっと長いですが65byteでまあまあです。
ideoneで動かしてみました。
http://ideone.com/PuAQ0X
なんと最後の方で無駄に大文字変換されてしまっています。
どうやら32bitでループしてるようです。
適当にビットシフトでテストした結果がこちら。
http://ideone.com/TAgqBw
シフトしているわけではなく2のn乗をかけているのでしょうか。
オーバーフローしているように見えます。
もう少し詳しく調べてみたくなりますね。
2015年09月08日
最終回だからという理由ではなくちょうどCodeIQとコードゴルフを始めたのでちょうどよかっただけですが。
現在開催中のデスマコロシアムはもうこれ(Ruby(45))以上短くできそうにないので過去問を解いてみました。
今回解く問題はデスマコロシアムの第9回です。
問題は
AbcdefghIjklmnopQrstuvwxYzabcdefGhijklmnOpqrst...
という文字を出力(a..zを8回繰り返し、8個ごとに大文字に変換)するという問題です。
Rubyだと簡単に
208.times{|i|putc (i%8<1?65:97)+i%26}
(37byte)
こういう解答ができますね。意外と簡単に思いつきますがなかなか縮められそうに無いですね。
Rubyの最短解もこれとほぼ同じでした。
なんとか削れそうなのはカッコと三項演算子でしょうか。
三項演算子を使う限りは短くできそうに無いのでビット演算でなんとか削る方法を考えます。
幸いなことに(?)大文字と小文字の整数コードの差は32で、a..zの間に6ビット目は変化しないのでここのビットをオン・オフすることでなんとかする方針を考えます。
i%8が0の時とそれ以外の時で6ビット目を切り替えられるようにします。
どこか一つのビットだけを見て0か1~7を判断する方法はありませんが一つ方法があります。
0と1~7の判断は難しいですが0と-1~-7は比較的簡単にできます。
-1~-7は4ビット目以上は1が並びます。当然6ビット目も1です。
というわけで
(-(i%8)&32)==(i%8>0?32:0)
はtrueになります
-i%8の結果は0~7になってしまうので-(i%8)とします。
というわけで-(i%8)&32でiが8の倍数の時は0、そうでなければ32になる計算式が出来ました。
これを使うと
208.times{|i|putc -(i%8)&32^65+i%26}
(36byte)
というわけで1文字縮まりましたね。大勝利ですがここで終わりではありません。
実はもう少し削れます。-i%8とカッコを消すと違う結果になってしまって使い物になりませんがi%-8とすることで(計算結果の値は違いますが8の倍数かそれ以外かを判断する上では)同じ結果を得られます。ということで。
208.times{|i|putc i%-8&32^65+i%26}
(34byte)
こうなりました。というわけでここで終了です。3文字縮まりましたね。i%-8&32と65+i%26はそのまま足し算しても大丈夫なのですが+を使うと演算の優先度の関係で違う結果になってしまう(もしくはカッコが必要)ので排他的論理和の^を使ってます。^じゃなくても|でも同じ結果になりますね。
2015年07月16日
今回の問題はyukicoderの192です。
この問題は101から1000の整数Nを入力してN-100からN+100の範囲に含まれる合成数を求める問題です。
範囲内の数を適当に選んで素数判定とかすれば良さそうですがもっと単純に考えられますね。
素数判定をしなくても最後の桁が4の整数は全て合成数です。というわけで入力した文字の最後を4に書き換えて終了です。
という解法で最初は通しましたがもっと簡単な方法があります。
解説(ログインが必要です)の通り近い偶数を見つければいいだけです。
最初Nの範囲が1からだと勘違いしていたので最後の桁を4にするという方法を使いましたが入力される数は101以上なので入力された数が奇数なら1を引いて出力、偶数ならそのまま出力すればいいようです。
Rubyで書くと
p (a=gets.to_i)%2<1?a:a-1
(25Byte)
とかでしょうか。aに足す数は-99から99までの奇数なら問題無いと思います。ちなみに3以上なら入力が一桁とかでも大丈夫のようですね。
こんなことしなくても単純に解説通りに
p gets.to_i/2*2
(15Byte)
とすれば終了です。ここまでならまあ普通に計算式がわかれば書けますね。入力が3以下だと合成数以外が出力されますが入力は3桁以上あるので問題無いです。
これをビット演算で縮めます。
この計算がどのようなことをしているかというとNが奇数ならN-1,偶数ならそのままを出力というのは解説通りですが内部のビットの様子をちょっと見てみましょう。入力が181だった時2進数で表した時の様子を見てみましょう。書くのが面倒なので下位8ビットのみ書きます(その左側の24ビット(?)は全部0です)
10110101
これを/2すると
01011010
となり、そこに*2すると
10110100
となります。10進数で言うと180になりました。
一番右の1が消えましたね。
というわけで/2*2というのは一番右の1のみを消していることになります。これをビット演算で行う場合どうやら最初の数の最後の1ビットを0にする操作を行えばいいようです。
というわけでXORを表す記号の^を使ってN ^ 1とすれば良さそうです。と書きましたがこれはNが奇数の場合は有効ですが偶数だとダメです。1回これでWAを出してしまいました。奇数の場合はN-1になってくれますが偶数だとN+1となってN+1が素数の場合はアウトです。
というわけで違う方法を使いましょう。ビット演算に引き算があればN(引き算の記号)1と書けば終わるのですが引き算の記号は残念ながらありません(たぶん)。同じことをやる方法としてN&(~1)が使えます。Rubyのコードにする時はカッコはいらないのでN&~1と書けますビット反転した分1文字長くなってしまいましたがまあこれはしょうがないですね。~1は-2なのでどちらを書いても同じです。というわけで
p gets.to_i&-2
(14Byte)
と書けば終了です。
p gets.to_i/2*2
より1文字短くすることが出来ました。
とここまでやってもう1文字短縮する方法がありました。-2ではなく~1を使う方法です。同じ数を表すもので同じ文字数なのですがpとの間のスペースを消すことができます。ということで
p~1&gets.to_i
(13Byte)
で終了です。
1の代わりに3,7,15,31,63などを入れれば同様に下位の何ビットかを消すことができますね。どこかで使えそうです。逆に同じ桁を1で埋めたい場合はAND演算の&ではなくOR演算の|です。
ここでRubyの話は終了で次はC言語。C言語は古くからある言語でショートコーディングの世界でも結構人気があります。なんとなくやってみました。
main(n){scanf("%d",&n);printf("%d\n",n&-2);}
という感じでしょうか。C言語のショートコーディングテクニックを適当にググればこの程度は普通ですね。ちなみにreturn文を省略してますが一部のオンラインジャッジなどでは通るようですが残念ながらyukicoderではランタイムエラーになってしまいます。
上のコードは削れないように見えますが少しだけ短くできます。scanfは入力するための関数で返り値を利用することはほとんどありませんが関数なのでちゃんと返り値があります。成功すると代入した数(今回は代入する変数が1個なので1)が、失敗したら-1が返ってきます。この-1という値は比較的よく使われてfor(;~scanf(););とすることでscanfが成功するうちはループを回す使い方です。今回は失敗した時の-1ではなく代入した時に返ってくる1を利用します。
ここまで来るとわかると思いますがこの1と言うのは意味のある数ですね。というわけでこの1をビット反転して-2にして使います。
main(n){printf("%d\n",~scanf("%d",&n)&n);}
先ほどのコードに比べて一つの式になったことで;が一つ減ったのと-2がなくなったので3文字減、ビット反転の演算子で1文字使ったので合計で2文字減らすことが出来ました。
ちなみに自分ではよくわかってませんがn&~scanf("%d",&n)と書いても動きます。
まあどっちにしてもyukicoderで通すにはreturn 0;する必要があります。