UfoCtrl ランダムパターン自由度向上について考察
この度、拙作『UfoCtrl』アプリのランダムパターン自由度向上について要望をいただきました。
内容的には「なるほど確かに」と頷けるものでしたので実装に取り掛かり始めたのですが、単純に要望を取り込むだけですと仕様が煩雑になるだけになりがちですので、改めて整理したいと思い、ここにまた思考の記録残しを書いています。
設定の記述を変えてしまう変更となるため、筆者の中では割と大掛かりな改修という認識をしておりますので、できるだけ綺麗に収めたいと思っております。
UfoCtrlがランダム動作を完全なランダムではなく「パターンを複数用意してそこからランダムで動作を選択する」という形態をとっているのはひとえに、完全ランダムでは人にとってつまらないパターンが延々と続いていまう可能性が少なからずあり、ランダムと言いながらも人にとって都合の良いランダムであって欲しいがゆえに様々な仕様や制約を設けて現在に至っているわけです。
モーターの回転方向とその力。つまり電圧ないしは電流量ですが、アプリからでは-100〜100という相対値でしか指示出来ません。
しかしこれを、どれくらいの値をどれくらいの時間指示するのか、良き加減にコントロールするために、やれPowerPatternだTimePattenだGradualPattenだPWMPattenだ、なんて仕組みを考えてきたわけなんですね。
その一方で、設定の記述量は極力増やしたくない。
そのため、現時点では基本各Pattern(Power,Time,Gradual,PWM)はそれぞれにランダムで選択してきてその組み合わせで指示を出しています。
ただし、そのままだと「このPowerのときにはこのTimeは選択して欲しくないよ」という状況があることは筆者が予め思っていたので、TimePatternの制限をTimeIndexRangeという形で記述していました。
また、回転方向も完全ランダムとしていて記述は正の値だけで良いとしてきました。
頂いた要望の1つ目は「アタッチメントが回転方向によって特性が変わるものがあり、回転方向に完全対称な今のランダム設定では自由度が不足なため正負で別のパターンを設定出来るようにして欲しい」というものでして、「あ、確かに」でした。
それでなくても、回転方向の好みというものが人によってはあるかもしれないものだろうに、完全ランダムかもしくは完全に方向を限定するという手段しか今までのUfoCtrlでは持っていませんでした。
これは記述量が増えてしまってでも、回転方向の完全ランダムを廃止して負のPowerPatternも記述した方が良いであろう、と考えました。
設定は大変になりますが、回転方向によって異なるランダムパターンが設定できるようになるのでメリットの方が上回るでしょう。
もう1つの要望が、筆者が要約してしまうと元の要望から少々ズレてしまうかもしれませんが、「選択されたPowerによって現状TimePatternの制限はできるが、GradualPattenやPWMPattenも指定なり制限が出来るようにして欲しい」(大分咀嚼してしまったの原型留めてないかも)というものして。これも「まあ、確かに」でした。
現在のUfoCtrlは画面上のボタンにより一律『Gradual制御させない/PWM制御させない』ということはできるのですが、このPowerのときはこのGradualPattenに絞り込みたいとかPWMPattenに絞り込みたいというようなことが一切出来ませんでした。
現在のTimePatternの制限の仕方に倣うならば、「TimeIndexRange」設定行と同じように「GradualIndexRange」設定行と「PWMIndexRange」設定行を追記するというやり方もあります。
ただ、以前から薄々感じていたことではあるのですが、現在のやり方では1〜3あるVolumeに各PowerPatternの要素数を揃えて、上記「TimeIndexRange」「GradualIndexRange」「PWMIndexRange」も要素数を揃えて、各要素と応対させることになります。
すると、例えばVolume1,2,3の最初に記述したPowerが選択されるときは全く同じ「TimeIndexRange」「GradualIndexRange」「PWMIndexRange」の制限を受ける、という縛りがあるんですね。
これちょっとしんどくない?
ただこの問題、極論すると、Power,Time,Gradual,PWMの組み合わせを欲しいパターンの分だけ全て書く、というのが一番自由度が高くなるのですが、やっぱり記述量がとんでもないことになるので、流石にそれは厳しかろうと思うんですね。
この自由度最大化は昔の筆者も一度検討してみたことがあって、やっぱ設定そのものが大変過ぎてNGという結論を当時の筆者は出しています。
例えば、3つのVolumeに7つのPowerを書いて、それぞれに5種類のTimeがあり、5種類のGradualがあり5種類のPWMがあるとしましょう(これでもわざと少なく書いています。厳選して書く前提でね)。
記述しなくてはならないパターン数が3×7×5×5×5=2,625ですよ。正負のPowerを書くようにするとさらに倍で5,250パターンとか、そういうレベルの記述量になるわけですよ。
いくら自由度が欲しくても、そこまでしたら設定時点で心が折れますよね。
逆に言うと、今の記述量でも面倒いですが筆者が提供している設定でも3×13(+0と-0はガッチャンコして)×8×9×6=16,848通りの組み合わせを生み出せているわけです。
ただ、現在の記述の仕方だとちょっとランダムに過ぎる、逆に言うと望ましくない組み合わせが選出されることが多くある、ということなんですよね。
また、今回の要望ではありませんでしたが、筆者が割と常々気にしている問題として、確率を偏らせることが難しい、という問題があります。
ランダムと言いながらも好みのパターンが多めに出てきて欲しい、って欲求もあるんじゃないかと思うんですよ。
すると、『各パターンに対して重み付けを加える』なんて案も何度も考えたりしたのですが、やっぱりそこまで記述量増やしてまですることか?ってなって堂々巡りなんですよね。
現時点では同じ記述を複数書くことを許容してそれで多めに出てきて欲しいパターンの確率を上げられるようにはしているのですが、ここについてはやっぱり現状維持かなあ。
パラメタ増え過ぎても人間側が追い付けないですよね。
んでですね、上記の全てを勘案して各Powerを正負数値で書くようにして、そこに「TimeIndexRange」「GradualIndexRange」「PWMIndexRange」の制限情報を付随させたらどうかと思うんですね。
上記同様に概算してみますと、各Volumeに対してPower13個、「TimeIndexRange」「GradualIndexRange」「PWMIndexRange」は範囲制限情報なので書く数としてはいずれも1つ扱いになります。
すると、記述量的には3×13×1×1×1=39パターン書けば済むことになります。
各「IndexRange」を考えるのも骨が折れるでしょうから、1個1個の記述は大変にはなるでしょうが、それでも数千とか数万のパターンを記述することを考えたらよっぽどマシですよね。
しかし、それですら面倒臭いという気持ちも分かります。
筆者自身が規定値として提供するパターンを書こうとして既にもう面倒臭いですもん。
なので、省略できる記法というのも十分に考えなければなりませんね。
例えば「TimeIndexRange」「GradualIndexRange」「PWMIndexRange」の制限をする必要が無いなら書かなくて良いとか、「n-」(後方制限なし)「-m」(前方制限なし)「n」(1種に限定)記法を許すとか。
この辺りはプログラムが苦労すれば良いところなので、筆者の頑張りどころかなぁ。
あと、全ての制限情報をPowerに付随して書くようにすると、各Volumeに書くPowerの要素数を揃える必要が無くせます。
プログラムはまた若干ややこしくはなりますが、正直言って「各VolumeのPower要素数を揃えろ」って今となってはイケてない仕様だと思います。
元々は本当にVolumeで単純に倍率変更だけしていたところから各VolumeでPower数値の自由化を図ったという経緯がある他、記述量削減のためという言い訳があったわけですがが、今ここに書いている案を採用するとその言い訳が意味を無くしますので。
ここも筆者の頑張りどころ。
そして、もう一つ要望があったのですが、「特定のPowerが選択されたときに次から別のVolumeへと遷移させたい」、というのがありました。
これは、「うーん、そういうのも分からんではない」という感想だったのですが、要するに現在UfoCtrlで「Volume」1〜3と呼んでいるものはもうどちらかというと「Mode」つまりパターンの傾向そのものをグルーピング化している集合体に過ぎないわけで、そのモードを遷移させたい、例えば「まったり」モードでしばらく動いていたものが、稀にモード遷移を起こして「ガシガシ」モードで動き続けるようになる、みたいなこと、これをランダムの機構の中で発生させたい、ということだと筆者は受け取りました。
元々、ランダムパターン動作って「チクニー中はオンオフ操作以外したくねえ」という発想でして、「Volume」変更は「オンオフ操作」の一派として今まで捉えてきたのですが、これも自動ランダムで遷移できるようにしちゃおう、というのはアリといえばアリなのかなと思いました。
記述はさらに増えてしまいますが、これもPowerに付随して書けるように、また、必要無いときは省略して書けるように頑張りたいところです。
んで現時点でそこまで言及する必要は無いかもしれないですが、筆者が良く使用している「近接センサー」でオンオフ。これは「前のVolume」を覚えていて、それと停止状態を切り替えるものなのですが、こういうオンオフ系で記憶している「前のVolume」にはランダム遷移は含めない、ユーザーが指示したVolumeだけを記憶する、という形が望ましいかな、と思います。
以上を踏まえまして現状考えている記載方法の例としては
-25/3-8/4-/-7/2
[Power]/[TimeIndexRange]/[GradualIndexRange]/[PWMIndexRange]/[NextVolume]
これを一塊としてカンマ区切りで並べてもらってそのVolumeのパターンの記述か完成。んでそれを3Volume分。要素数を揃える必要無し。ってな感じでしょうか。(従来のTimeIndexRangeの行は削除)
なお、筆者みたいに「普段はVolume1しか使ってないし」、なんて人はVolume2,3の記述を
0/1/1/1/1
みたいに1つだけ設定書いて、Timeの1要素目の時間を短く、Gradual,PWMの1要素目は0にしておくと、すぐにVolume1に遷移してくれるので、仮に操作を間違ってVolume2,3にしてしまってもオートで1に強制してくれる。なんて書き方もできるようになるわけです。
ナニコレめっちゃ優秀じゃん?(でもVolume2,3も活用できる方が楽しいと思う。筆者は出来てないけど……)
んで制限なんて要らんし、Volume遷移も不要よ、という場合は従来どおり
-25
と、Powerだけ書けば良いように出来るのが理想。
現在と同様にTImePatternの制限だけする場合は
-25/3-8
とだけ書けば済むようにしたいところさん。
一行にカンマ区切りで書ききるのも大変そうだから、コメント行でVolume区切る扱いにして複数行書けるようにした方が良いかな?
そうなると、現時点で考えている設定イメージとしては(実際の設定値の部分は大変なのでかなり端折ります)(あと横に長いのでスクロールして見てください)
#Random
Random(U.F.O. TW)
#Start,Stop,Displacement Threshold: int(0〜100): Gradual動作開始時,減速時,似たようなPowerを続けないための最低変化量, CounterWeight: int(0〜): モーター稼働積算計に加算するときの乗数
1,1,5,1
#TimePattern: int(1〜): 要素数は自由(ランダムに選ばれる) 単位100msec
10,16,22,28,34,40,46,52
#GradualPattern: int(0,1〜){/int(1〜)}: 要素数は自由。Power変化値/時間(単位100msec)の分数型で記載。時間が1のときは分母省略可能。約分せず。0はGradualさせない(確率を変えるために複数使用)
0,0,0,0,1,3/2,2,3,1/2,3/4,1/3,2/3
#PWMPattern: int(0,2〜){/int(2〜)/int(0〜)}: 要素数は自由。On時間(単位100msec)/Off時間(単位100msec)/OnのときPowerを何%に増(減)速するか(増減させないとき/100を省略可)。約分せず。0はPWMさせない(確率を変えるために複数使用)
0,0,0,0,0,2/2/175,4/2/150,2/4/200,4/4/125,4/8/175
ここまでは従来どおり。ただし、省略記法実現のためTimePattern、GradualPatten、PWMPattenを先に書くようにしまして。
#Vol.1 PowerPattern :int(-100〜100)/TimeIndexRange :int(1〜 "n-m"制限不要な場合"n-"|"-m"|""のように省略可"n"は単独限定)/GradualIndexRange :int(〃)/PWMIndexRange :int(〃)/NextVolume :int(0〜3 以外は変化させない) 要素数は自由(ランダムに選ばれる)
0/1-5/1-12/1-10/9,1////
5,10,15,20,25
#Vol.2 PowerPattern/TimeIndexRange/GradualIndexRange/PWMIndexRange/NextVolume 要素数は自由(ランダムに選ばれる)(Vol.1の終わりを表す必須コメント行でもある。コメント内容は自由だが行頭に#が必要)
0
7,14,21,28,35,42
-7,-14,-21,-28,-35,-42//3-
#Vol.3 PowerPattern/TimeIndexRange/GradualIndexRange/PWMIndexRange/NextVolume 要素数は自由(ランダムに選ばれる)(Vol.2の終わりを表す必須コメント行でもある。コメント内容は自由だが行頭に#が必要)
20,25,30,35,40
45,50
#PattenEnd(Vol.3の終わりを示すために必要なコメント行)
のような感じをイメージしております。
さあて、残る問題は筆者がこれをちゃんと実装できるのか?ってところだ。
ぶっちゃけランダムだし、パターン煩雑だし、きっちりとしたテストとかもう出来てないんですよ。(割と最初からだけど)
Indexとか間違わずにちゃんとプログラムを書くことと机上デバッグで今までのほとんどを乗り切ってきております。(あ、ランダムパターン動作アルゴリズムのところの話ね)
もう相当に煩雑なものに結構なメスを入れるので、ちょっと苦戦するかもですが、お待ちくださいませ。
あ、余談ですけど、ついでに設定の中にポツンと残されているVolumeボタンの高さ調整の項目が今となっては違和感バリバリ(他が全て動作に関する設定なのにこれ1つだけ画面の設定)なので、これを機にSwithesの画面に項目を移すつもりでいます。
昔は逆にSwitchesの画面が無くて、設定の全てをSettingsの画面に文字列で書いてたんだけど、今は画面制御周りの設定はSwitchesに集まっているのでその方が自然でしょう。