日記

日本語の勉強のためのブログ

【C言語】scanf時の改行の処理に苦労した

AtCoder Beginner Contest 191のC - Digital GraffitiをC言語で解いたときのはなし。
例として以下の入力が与えられていた。

5 5
.....
.###.
.###.
.###.
.....

これを以下のコードで読み込もうとすると、最終行(.....の部分)を読み込むことができなかった。

int h, w, i, j;
char s[10][10];
scanf("%d %d", &h, &w);
for (i = 0; i < h; i++) {
  for (j = 0; j < w; j++) {
    scanf("%c", &s[i][j]);
    printf("%c", s[i][j]);
  }
}
//読み込み結果

.....
.###.
.###.
.###.

これはscanfでchar型変数を読み込む際、改行も1文字として読み込んでしまうことが原因である(上の読み込み結果を反転表示してもらえば、改行が読み込まれていることがわかるだろう)。

さらに次にscanfを使う場合に備えて,残った改行は%*cで読み飛ばします。これで保持されていた入力データはすべて処理されたことになります。但し,次に読み取るものが%dなど数値である場合には,これをしなくても特に影響はありません。
標準入力から安全に文字列を受け取る方法いろいろ - Qiitaより引用)

この解説に従い、以下のようにコードを書き換えると、改行を無視して読み込むことができた。

int h, w, i, j;
char s[10][10];
scanf("%d %d%*c", &h, &w);
for (i = 0; i < h; i++) {
  for (j = 0; j < w; j++) {
    scanf("%c", &s[i][j]);
    printf("%c", s[i][j]);
  }
  scanf("%*c");
}
/* 読み込み結果 */
......###..###..###......

pixiv検索時にusersタグを付与するブックマークレットを作る

やりたいこと

pixivには作品のブックマーク数に応じてタグをつける風習がある。
具体的には「艦これ1000users入り」や「アイマス1000users入り」のように、「[作品名][ブックマーク数]users入り」という形式のタグが追加される。
このタグを付けて検索すると人気の作品を見つけやすいため、個人的によく使っている。

しかし検索時に毎回そのタグを入れるのは面倒である。
なので今回は、押すだけで「1000users入り」タグを付与して検索してくれるブックマークレットを作ろうと思う。
具体的には、
https://www.pixiv.net/tags/電(艦隊これくしょん)/artworks?s_mode=s_tag
のようなURLを、
https://www.pixiv.net/tags/電(艦隊これくしょん) 1000users/artworks?s_mode=s_tag
のように整形して遷移するようにしたい。

準備

最初はwindow.location.replaceと正規表現を使おうかと思っていたが、window.location.replaceを使うと、遷移前のページに戻れないことが判明した。
Location.replace() - 現在のURLを置換する

結局、splitメソッドとwindow.location.hrefを用いる方が簡単そうなので、そちらを用いることにした。
ブックマークレットでURLを置換したいのですが、2箇所の任意の文字列を保持し... - Yahoo!知恵袋
String.prototype.split() - JavaScript | MDN

splitメソッドを使えば、文字列を指定した文字で分割してくれる。
また、window.location.hrefは現在表示しているページのURLを示す。これに遷移先のURLを入れれば、そのURLのページに移動できる。

作る

作ったらたった2行になった。
まず、

var splitedURL = location.href.split('/');

で現在表示しているページのURLを/で分割し、配列splitedURLに入れる。
次に

window.location.href = "https://www.pixiv.net/tags/" + splitedURL[4] + " 1000users/artworks?s_mode=s_tag";

で「1000users」タグを付与したURLに遷移させている。ここで、splitedURL[4]は現在の検索クエリである。

ブックマークレット

できた。

javascript:(function(){var a=location.href.split("/");window.location.href="https://www.pixiv.net/tags/"+a[4]+" 1000users/artworks?s_mode=s_tag"})();

いつものようにこのサイトを利用した。
Closure Compiler Service

IIDX CS15thの隠し曲解禁方法について

分かりづらかったところをまとめる。

次の2つのサイトから引用させていただいた。
[1] 【Playstation2】 beatmaniaIIDX 15 DJ TROOPERS DATABASE 小ネタ
[2] しおんらぼ | CS beatmania IIDX データベース [15 DJ TROOPERS 隠し要素]

なお、ネタバレ防止のため具体的な曲名については触れていない。知りたい場合は上のリンク[2]を参考にしてほしい。

基本情報

CS15thには隠し曲が11曲存在する。
そのうち5曲はSTANDARD / EXPERT / 段位認定の累計クリア数、もしくは特定の段位をクリアすることで解禁されるため、気づいたら解禁されていたということも多い。

しかし残り6曲は、解禁方法を知らないといつまで経っても解禁できない。 解禁には「勲章」を一定数取得し、ある「条件」を一定数満たした状態で、Military Splashに突入する必要がある。

※正確には5曲+全曲解禁後に特殊な操作をして解禁できる1曲。「特殊な操作」についてはリンク[2]を参照。

0. Military Splashへの突入条件

Standardモードにおいて、そのプレーで選曲した譜面の難易度の総和が

  • SPなら★合計21以上
  • DPなら★合計18以上

となれば、3曲目終了後にMilitary Splashフォルダが出現する。
クリアしてさえいればクリアランクは影響しない。ランクAAAでもBでも出現する。

1. 勲章取得条件

Military Splashフォルダ内の曲

  • Normal譜面→正規譜面でクリア
  • Hyper譜面→正規譜面+スコアランクAA以上でクリア(Easyオプション不可)
  • Another譜面→正規譜面+スコアランクAA以上+Hardオプションを付けてクリア

という条件を満たしてクリアすると、「勲章」が手に入る。この勲章が、隠し曲解禁の第1条件である。隠し曲全解禁のためには最終的に勲章を8つ取得する必要がある。
ちなみに上記条件を満たしていれば、FREEモードでクリアしても勲章はもらえる。そのため、STANDARDモードをプレイしたことがないのに持っていたということも十分あり得る。

※一応反転しておくが、一覧は→ Übertreffen、TRIP MACHINE PhoeniX、ミラージュ・レジデンス、TROOPERS、STEEL NEEDLE、four pieces of heaven、Do it!! Do it!!、ICARUSの8曲。

2. その他解禁に必要な「条件」

隠し曲解禁の第2条件は、以下の「条件」のいくつかを満たすことである。いくつ満たせばよいのかは後で説明するが、隠し曲全解禁のためには最終的にどれか5つを満たす必要がある。

  • SP/DP 合わせて10譜面以上 FULL COMBO 達成。
  • SP/DP 合わせて40譜面以上 ASSIST CLEAR 以上のクリアランプ取得。
  • 単曲で EX SCORE = MAX もしくは DJ LEVEL (AAA-E) ボーダークリア。BEGINNER でも OK。
  • 単曲でボーダークリア(グルーヴゲージ 80% でクリア)達成。BEGINNER でも OK。
  • 各モードで、STAGE 終了時に MAX COMBO 573 でクリア。
  • クラッチ判定の P-GREAT + GREAT 数、累計1000達成。

3. 解禁方法

いよいよ具体的な解禁方法について説明する。
まず、下に示すように勲章を取得+「条件」を満たした状態でMilitary Splashに突入する。
曲選択画面に移るが、このときにスクラッチを回してカーソルを何回か動かすと警報音が鳴り始める
さらにカーソルを動かしていくと、"UNKNOWN..."の表記とともに隠し曲の強制プレーに移行する。
これをプレーすると解禁できる。クリアする必要はないため、難しくても安心してほしい。
なお、強制プレー時の難易度は、Military Splashフォルダ内の曲選択時(強制プレー突入直前)に指定していた難易度となる。

  • 勲章4つ以上取得+上の「条件」を1つ以上満たす→1つ目の隠し曲解禁
  • 勲章5つ以上取得+上の「条件」を2つ以上満たす→2つ目の隠し曲解禁
  • 勲章6つ以上取得+上の「条件」を3つ以上満たす→3つ目の隠し曲解禁
  • 勲章7つ以上取得+上の「条件」を4つ以上満たす→4つ目の隠し曲解禁
  • 勲章8つ以上取得+上の「条件」を5つ以上満たす→5つ目の隠し曲解禁

どんな風に強制プレーに突入するかについては、動画を見てもらったほうがわかりやすいかもしれない。動画内・タイトルに隠し曲のネタバレがあるため、埋め込みはせずURLだけ貼っておく。
https://www.youtube.com/watch?v=Fh-TSv1yk9s&ab_channel=hl1auz

※おそらく動かす回数は5~10回でランダム。

ページを90°回転させるブックマークレットの作成

タイトルの通り。
画像を回転させたいときに、いちいち保存してギャラリーから回転させるのが面倒だったので作った。

といっても非常に簡単なコードなので特に説明することもない。
ただbody要素にstyle="transform: rotate( 90deg )"を追加するだけである。
style要素の追加方法についてはわからなかったので、以下のサイトを参考にした。
[JavaScript] Style要素の追加方法 | ぱんだシステム

実際に作成したコードは以下の通り。

var style = document.createElement('style');
document.head.appendChild(style);
var sheet = style.sheet;
sheet.insertRule('body {transform: rotate( 90deg )}');

これをブックマークレットとして使用可能な形に直すと以下のようになる。

javascript:(function(){var style=document.createElement("style");document.head.appendChild(style);var sheet=style.sheet;sheet.insertRule("body {transform: rotate( 90deg )}");})();

コンパイルにはこのサイトを利用した。
Closure Compiler Service

動作確認したところ、しっかり動くことが確認できた。
ある画像単体を回転させたいときは、画像を新しいタブで開いてからブックマークレットを作動させればよい。
また、回転方向や回転度合いを変えたい場合は、transform: rotate( 90deg ); の90degを-90degや45degなどに変えればよい。

次回は(次回があるかわからないが)このブックマークレットを、起動するごとに90°、180°、270°、360°と回転していくように改修したいと思う。

spotifyでクエリセレクタの練習

spotifyのプレイリスト内の楽曲のタイトルをまとめて取得したい。
丁度いい機会なので、クエリセレクタを用いて取得してみようと思う。

目次

セレクタの作成

まずセレクタを作る。Develop Toolsで取得したい要素(今回は楽曲のタイトル)を選択し、右クリックで Copy->Copy selector を押せばその要素のセレクタがコピーできる。

コピーで得たセレクタは以下のようになる。

#main > div > (中略) > div > div:nth-child(2) > div:nth-child(2) > div > div > div > div > span > span 

この方法で得られるセレクタは一番先頭の要素を基準としているので、必要な部分のみを取り出す必要がある。
今回はこのうち、後ろから5番目のdiv要素を基準として利用する。

<div class="(略)" role="gridcell" aria-colindex="2" tabindex="-1">

この要素に含まれるaria-colindex属性を目印にしようという魂胆である。

するとセレクタは、先程のdiv要素以下を用いて次のように作成できる。

div[aria-colindex="2"] > div > div > span > span

セレクタの作成については前回と同様に以下の記事を参照した。
意外と知らない!?CSSセレクタ20個のおさらい|Webpark

セレクタで要素を取得する

前回の記事と同じくdocument.querySelectorを使おうとしたが、これは1つの要素しか取得できない。
通常プレイリストには複数の楽曲が含まれているため、それらすべてのタイトルを取得するためには不十分である。

そのため、ここではdocument.querySelectorAllを使うことにする。
これはセレクタに合致するすべての要素を取得し、Nodelistという形式で返すメソッドである。
querySelector()を使うとjQueryみたいにセレクターで要素を取得できるよ。(DOMおれおれAdvent Calendar 2015 – 02日目) | Ginpen.com

といってもNodelistの処理以外はdocument.querySelectorと変わらない。

var elem = document.querySelectorAll('div[aria-colindex="2"] > div > div > span > span');

と書けば変数elemにNodelistが返される。

f:id:kalax:20210203224146p:plain
図1. Developer Toolsで実行した様子(1)

Nodelistの処理

Nodelistについてはこちらを参照。
NodeList - Web API | MDN

取得したNodelistからタイトルのみを抽出して表示するには、適当にfor文を書いて

for (var i = 0; i < elem.length; i++) console.log(elem[i].innerText);

とすればよい。

Developer Toolsで実行すると以下のようになる。しっかりタイトルが取得できていることが確認できる。

f:id:kalax:20210203225402p:plain
図2. Developer Toolsで実行した様子(2)

取得したタイトルを整形して表示する

以上までで目的は達成されたようなものであるが、図2のように箇条書きで表示するのではなく、カンマで区切ってまとめて表示してみたい。
javascriptでは文字列を+で連結することができるので、適当な変数txtを用意して、そこにタイトルを連結していく。
つまり、

var txt = "";
for (var i = 0; i < elem.length; i++) txt += elem[i].innerText + ", ";
console.log(txt);

としてやればよい(出力の末尾にカンマがついてしまうがご愛嬌)。

f:id:kalax:20210203231049p:plain
図3. Developer Toolsで実行した様子(3)

まとめ

以上のコードをまとめると次のようになる。

var elem = document.querySelectorAll('div[aria-colindex="2"] > div > div > span > span');
var txt = "";
for (var i = 0; i < elem.length; i++) txt += elem[i].innerText + ", ";
console.log(txt);

これを実行すれば、図3のようにカンマで区切ったタイトル文字列が得られる。

感想

なかなかよい勉強になった。 次はここで取得したタイトルを用いた楽曲ソートを作ってみたい (というかそれがもともとの目的で、この記事はその副産物である)。