この記事はSYSKEN Advent Calender 2016 7日目の記事です。
こんばんは、るっちです。紅葉とともに単位も落ちる季節となりましたが、皆さんいかがお過ごしでしょうか。私はやばいです。
それはさておき。突然ですが、文字式って解くの面倒じゃないですか?例として、y=x-1(y=0の時,xの値は…)なんかであれば、簡単だから手でやった方が楽ですよね。ですが、ちょっと複雑な文字式になるとどうでしょう。次に示すのは、私が今回解こうと思った文字式です。
target score,notesを入力する
1010000-(target score)≦mx+ay+jz
m= 1010000 / (notes)
a= 510000 / (notes)
j= 10000 / (notes)
(0≦x,0≦y,0≦z)求めたい解
x=0,y=0の時,z=…(0から1ずつ増やしていく)→x=0,y=1の時…→…→x=1,y=0,…
はい。どう見ても面倒ですね。一応人力でも解けないことはないでしょうが、膨大な時間がかかるのは間違いないでしょう。だから、コンピュータにパパパっとやらせて、さっさと終わらせましょう。コンピュータは何でもやってくれます、どんどん使っちゃいましょう。
今回、なんでこんな式を解く必要があったのか話します。結論から言ってしまえば、あるゲームの検証用です。(ここから先は個人的な話のため、飛ばしていただいて構いません。)
私が現在夢中になっている音楽ゲームがあるのですが、やはり音楽ゲームには一定の目標が必要です。その一つの指標として、「スコア」という概念があります。このスコア、その譜面中のノーツ数(わからない人は、降ってくる音符の数と考えていただければOKです)によって、判定にかかるスコア倍率が変動するのです。それなら、「この楽曲(ノーツ数)」で「このスコア」なら、「どの程度のミスが許されるか」が計算できるのでは、と考えました。
計算したい文字式が立式できたところで、プログラムを書いていきましょう。言語はC言語を使用しました。今回主になるのはこの部分です。(コピペしたらインデントが消えたため、見づらいです。すみません。)
t = 1010000 - score; j = 10000.00 / notes; a = 510000.00 / notes; m = 1010000.00 / notes; for (cm = 0; t >= cm*m; cm++){ for (ca = 0; t >= cm*m + ca*a; ca++){ for (cj = 0; flag != 1 && flag != 2; cj++){ df = cm*m + ca*a + cj*j; if (t == df){ flag = 1; } else if (t < df){ flag = 2; } if (flag == 1){ printf("score:%7.0lf\nj:%d\na:%d\nm:%d\n\n", (1010000.00 - df), cj, ca, cm); } else if (flag == 2){ printf("score:%7.0lf\nj:%d\na:%d\nm:%d\n\n", (1010000.00 - (cm*m + ca*a + (cj - 1)*j)), cj - 1, ca, cm); } } flag = 0; } }
なにをやっているかは後で解説するとして、まずは出力させてみましょう。
入力1行目にnotes,2行目にtarget scoreを入力します。
出力1行目に2~4行目の判定でのスコアを出力します。
出力2~4行目に各判定の許容量を出力します。
(プログラムの関係上、計算結果のスコアの値が±1ずれている可能性があります。)
それでは、プログラムを読み解いていきましょう。
まず、値を代入していきます。tには理論上最大スコア(固定、今回対象のゲームは1010000)と目標スコアの差分、j,a,mにはスコア倍率を代入します。
ここから今回解く式の要にはいっていきます。
m=0のとき、→a=0のとき、j=…→a=1のとき、…→…m=1のとき、…と、順に計算させるために、定数の数だけ重ねたfor文を利用します。今回は三重for文ですね。
t >= cm * m は、スコア差分がm判定許容量を上回っている、という状況です。同様に、t >= cm * m + ca * a は、スコア差分がm許容量+a判定許容量を上回っている、という状況です。もし、tが下回ったら、目標スコアは達成できなくなります。そのため、その時点でループを抜け、条件に応じてcmかcaの値を1増やして次の計算を始める、といった具合になります。
それでは、三重for文の中身を見ていきましょう。
dfに、全判定のスコア倍率を代入します。1010000-df<=target score が成り立つまで、for文によるループを続けます。
t=dfが成り立った時、flagを1に、t<dfが成り立った時、flagを2にします。
flag=1(t=df)のとき、スコアは1010000-dfとなります。
flag=2(t<df)のとき、スコアは1010000-df-jとなります。
flagが1もしくは2になったので、このfor文を抜け、flagを初期化(値を0に)し、aを1増やします。
実行順として、図示したものが次のようになります。
この一連の動作を繰り返すことで、おそらく高速に、かつ確実に文字式を解くことができます。
いかがでしたでしょうか。プログラムの説明をするとここまで冗長になってしまうものなんですね。丁寧に解説したつもりですが、読みづらくなってしまい申し訳ございません。皆さんも、文章の長さには気をつけましょう。そもそもこんな記事に果たして読む価値はあるとは思えませんが
以上です。相変わらず拙い文章でしたが、ご閲覧ありがとうございました。
コメントを残す