━━━━
伺か
━━━━
配布ゴースト
・
伺かとは?
・
TIPS
TIPS
似非二次元汎用配列
伺かアドベントカレンダー2010
●似非二次元汎用配列
今回はネトゲちっくなゴースト作成中に、所謂RPGなどで使用されているMAPのシステムを作る為に作った、似非二次元配列です。
二次元配列(多次元配列)とは配列を二次元化した物(そのまんまやね)で、通常の一次元配列では1つの入れ物に対し、要素数を1つ
持ちますが(直線上に入れ物が並んでいるイメージ)、二次元配列では2つの要素数を持つ事になります。簡単にいうとエクセルの行と列ですね。
因みに一般的に例えば、要素数4個(入れ物4個)の一次元配列を表す場合
_hoge[4]の配列 と表されます。
もちろん各要素のデータにアクセスする場合は _hoge[0]〜_hoge[3] を使用します。YAYAでは配列の要素数を自由に変更できる為、あまりこういった表現を見ませんよね。
これと同じように二次元配列で例えば、要素数4個の配列を3つ重ねた物を
_hoge[3][4]の配列 と表され、データにアクセスする場合は _hoge[0][0]〜_hoge[2][3] を使用します。
通常この2つの要素を表す時に、最初の要素を
行数、後の要素を
列数として扱われる事が多く、値を代入する時には行単位を意識して代入する事が多いです。
さて、この二次元配列。いったいどんな事に使用されるかというと、先ほどのエクセルの例えの様に表のデータ作成や、ゲームではブロックを並べたり、始めに出てきたRPGのMAP等に使われます。
RPGのMAPでは、X座標(横)とY座標(縦)を列と行(または逆)に見立てて、それぞれ1マスずつに属性(値)を与えることで、そのマスが通行可能か、
海なのか陸地なのか等を設定するわけです。こういった物が詳細になってくると、三次元、四次元配列等を使用する事になるわけです。
このように色々と便利な多次元配列ですが、実はYAYAでは使用できません。ちょっとションボリ(´・ω・`)
なので、今回はせめて二次元配列を!という事で、一次元配列を使用して二次元配列もどきとして手軽に使用するための関数を作ってみました。
●二次元配列風への考え方
例えば初めにも例に出した_hoge[3][4]の二次元配列を作るとします。この配列の入れ物は合計で3*4=12個になりますよね?
つまり入れ物の数としては_hoge[12]の一次元配列と同じ訳です。後は二次元配列の行番号と列番号の指定でアクセスできればいいわけですよね。
では仮に上記の _hoge[3][4] の二次配列に対し _hoge[1][3] へアクセスしたいとします。
この場合、_hoge[12] の一次配列での要素番号は何番になるのかというと、その求め方は
アクセスしたい行番号 * その二次配列の列数 + アクセスしたい列番号 で求める事ができます。
解りやすいように要素数をアルファベットに置き換えると _hoge[A][B] の _hoge[a][b] にアクセスするには、
a * B + bで求めた要素にアクセスすればいいわけです。
今回の方法では要素数を求めるのにも使用した、その二次元配列の
列数が鍵になります。もちろん行数も重要ですが、その配列を作った時の列数を忘れてしまうとアクセスできなくなってしまいます。
例えば短い関数やイベント内でローカル変数として使用している場合なら兎も角、後々プログラムを変更する時や、グローバル変数としてこの配列を使用する場合などは大変です。
これを手っ取り早く解決する方法は、_hoge[3][4] の配列であれば _hoge_3_4[12] のように名前に書いてしまう事です。
が、今回紹介するコードではそれすら面倒だ!という方の為に、配列の作成から代入、取得まで関数を用意し、その上で行数と列数を配列の[0]と[1]に格納しておく形にしました。
つまり、_hoge[3][4] の配列を作るとすると _hoge[0] == 3 _hoge[1] == 4 の状態で _hoge[14] を作る形にしています。
●やっとコード紹介
先に言っておくと、今回のコードでは上記のように列数が鍵となる為、YAYAでは自由に行える配列の追加や挿入は紹介するコードだけでは行えません。
もちろん色んな関数を使用して追加で組めば出来ない事はありませんが、やりたい方は頑張ってみてくださいw
さて、今回の運用方法ではまず最初に作成・初期化関数を使って行数と列数を指定し、似非二次元配列をつくります。
以下がそのコードです。
//---- 作成初期化【引数:int Row数, int Column数, 初期値】【返り値:似非二次配列】
CA2D //(CreArr2d) ※第3引数の初期値は省く事ができます。その場合、各要素の中身は空になります。初期値は1つしか指定できません。
{
_arrSize = _argv[0] * _argv[1]
_arr[0] = _argv[0]
_arr[1] = _argv[1]
if _argc == 2{ //第3引数無し
_arr[(_argv[0] * _argv[1] + 1)] = _argv[2]
}else{ //第3引数有り
for _i = 2; _i < (_arrSize + 2); _i++{
_arr[_i] = _argv[2] //第3引数で[2]以降の要素を初期化
}
}
_arr //初期化した似非二次元配列を戻す
}
使用方法はコード内コメントにもあるように引数に
CA2D(行数,列数,初期値) と指定するだけです。
配列が返されるので、似非二次元配列としたい変数で受け取ってください。
ぶっちゃけこの関数は配列の各要素を全て同じ値で初期化する以外には利点はありません。
初期化が必要無い場合は似非二次配列にしたい配列に対して
_arr = (行数,列数)とするだけで、これ以降の関数は使用できますし、その方が処理は早いです。
次の関数は上記でも計算方法を書いた、二次元配列の要素番号から似非二次元配列(一次元配列)での要素番号を導き出す関数です。
//---- 要素数取得【引数:int R番号, int C番号, int R数, int C数】【返り値:int(要素数) エラー:-1】
GA2N //(GetArr2dNum)
{
_num = IARRAY
if _argv[0] >= _argv[2] || _argv[1] >= _argv[3]{ //オーバーフローしないけど・・・ポカミスチェック ※誤記修正 2011/3/8
_num = -1
}
_num = _argv[0] * _argv[3] + _argv[1] + 2 //実際にはこの式しか必要無い = 第3引数は使わない・・・
_num //擬似二次元配列用の要素番号を戻す
}
使用方法は引数に
GA2N(取得したい行番号,取得したい列番号,行数,列数) と指定します。
返り値が要素番号なので実際に使用する時は、_hoge[GA2N(1,2,_hoge[0],_hoge[1])] や、行数・列数が判っているなら第3,第4引数を直接入力しても構いません。
又、_hoge[GA2N(1,2,_hoge)] のように丸投げしても動作します。が、丸投げすると大きな配列になる程、余分なデータが無駄に渡される為、処理的にお奨めしません。
このコードもコメントに書いてありますが、式さえわかっていれば特に関数化が必要な物でもありません。第3引数はポカミスをしない為の保険で入れてあるだけです。
さて、このどうでもよさげな事を言った2つのコードですが、この2つだけで大抵の作業は済んでしまうと思いますが、一応おまけで4つほど似非二次元配列用の関数を紹介します。
まずは行代入と列代入です。一行、あるいは一列に対して一度に値を入力する時に使います。
//---- 行代入【引数:int R番号, 似非二次配列, 値,,,】【返り値:似非二次配列 エラー:-1】
SA2R //(SetArr2dRow)
{
_arrStaNum = _argv[0] * _argv[2] + 2 //指定R番号の開始要素
_valStaNum = _argv[1] * _argv[2] + 3 //引数の値開始要素
_ans = IARRAY
if (_argc - _valStaNum) > _argv[2] || _argv[0] >= _argv[1]{ //値数が列数を超えてないか、指定行が行数以上じゃないか
_ans = -1
}else{
for _i = 0; _i < (_argv[1] * _argv[2] + 2); _i++{ //元の中身を順にコピー
if _i == _arrStaNum{ //指定要素から値個数分、順に値を代入
for _j = 0; _j < (_argc - _valStaNum); _j++{
_ans[_i] = _argv[(_valStaNum + _j)]
_i++
}
_i--
}else{
_ans[_i] = _argv[_i + 1]
}
}
}
_ans //代入後の似非二次元配列を戻す
}
//---- 列代入【引数:int C番号, 似非二次配列, 値,,,】【返り値:似非二次配列 エラー:-1】
SA2C //(SetArr2dCol)
{
_valStaNum = _argv[1] * _argv[2] + 3
_ans = IARRAY
if (_argc - _valStaNum) > _argv[1] || _argv[0] >= _argv[2]{ //値数が行数を超えてないか、指定列が列数以上じゃないか
ans = -1
}else{
for _i = 0; _i < (_argv[1] * _argv[2] + 2); _i++{ //元の中身を全コピー
ans[_i] = _argv[_i + 1]
}
for _i = 0; _i < (_argc - _valStaNum); _i++{ //指定C番号から列数飛びに値を代入
_ans[(_i * _argv[2] + _argv[0] + 2)] = _argv[(_valStaNum + _i)]
}
}
_ans //代入後の似非二次元配列を戻す
}
使い方は・・・まぁコメントの引数の部分を見ていただければわかると思います。注意としては代入したい値の数がSA2Rは列数を、SA2Cでは行数を超える場合にエラーが返されます。
逆に足りない分には要素0番から詰めた形で値の個数分だけ代入をします。
例えば整数0で初期化された_hoge[3][4]の、行番号1に"a","r","r"の3文字をする場合、SA2R(1,_hoge,"a","r","r")をすると、その返り値は
3,4,0,0,0,0,"a","r","r",0,0,0,0,0 が帰ってきます。
最後に行取得と列取得です。指定した一行、または一列分の値を配列にして返します。
//---- 行取得【引数:int R番号, 似非二次配列】【返り値:1行分の配列 エラー:-1】
GA2R //(GetArr2dRow)
{
_valStaNum = _argv[0] * _argv[2] + 3
_ans = IARRAY
if _argv[0] >= _argv[1]{ //指定行が行数以上じゃないか
_ans = -1
}else{
for _i = 0; _i < _argv[2]; _i++{
_ans[_i] = _argv[(_valStaNum + _i)]
}
}
_ans //1行分の値を取得した配列を戻す ※似非二次配列ではない
}
//---- 列取得【引数:int C番号, 似非二次配列】【返り値:1列分の配列 エラー:-1】
GA2C //(GetArr2dCol)
{
_ans = IARRAY
if _argv[0] >= _argv[2]{ //指定列が列数以上じゃないか
_ans = -1
}else{
for _i = 0; _i < _argv[1]; _i++{
_ans[_i] = _argv[(_i * _argv[2] + _argv[0] + 3)]
}
}
_ans //1列分の値を取得した配列を戻す ※似非二次配列ではない
}
こちらも使い方は特に問題ないと思います。注意点は返り値が一行、一列分のただの配列です。
例えば先ほどの"a","r","r"を代入した_hoge[3][4]の、同じく行番号1だけを取得する場合、GA2R(1,_hoge) で、返り値に
"a","r","r",0 が帰ってきます。
さてさて、如何だったでしょうか。多分これらの関数にもう少し手を入れれば色々と使いやすい物が出来るかもしれませんが、
本当の二次元配列と比べれば、どうしても扱いづらい物になるので、人によってどこまで手を入れるのが扱い易い関数か、変ってくると思います。
後、ここまで書いておいてなんですが、似非とは言え配列の要素数は同じなのでその数が多く、それらを関数でやり取りしている為、どうしても処理的には負荷が高めです。
私のPCは8年程使っている、P4HT-3GHzの骨董品なので参考にはなりませんが、64*64で「おや?」と感じるくらい。恐らく最新のPCでも128*128辺りがギリギリ実用でき・・・るかどうか、難しい所だと思います。
因みに256*256(=65,536+2個の配列)で、うちのおんぼろPCに処理させた所、CA2D⇒SA2R⇒GA2Rの処理をするのに10分ちょっと掛かりましたが、SSPが落ちたりする事はありませんでした。w
原理がわかっていれば色々と改造できる関数だと思うので、本当の二次配列が実装される日を夢見つつっ!、皆さんも色々挑戦してみてください。
●伺かアドベントカレンダー2010
下記の記事はこちら、
伺かアドベントカレンダー2010という企画の元、
本日(2010/12/11)は私の当番と言う事で書かせていただきました。
私の他にも伺かのゴースト・シェルの作成に役立つ情報などなど、色んな記事がUPされていますので、伺かに興味をお持ちの方は是非、
他の方の記事もご覧になってください!
●人工無能!?ちょっと役立つ機能があってもいいじゃないっ! for YAYA
普段皆さんはPCでの作業中、片隅にゴーストは立っているでしょうか?
最近のPCであれば、作業アプリやゲームを起動させたままゴースト一体を立たせておくのは造作も無い事になってきたので、
PCを起動している間は何時も、伺かが起動してるよ〜って方も結構居るかもしれませんね。
では普段一番立たせている時間の長いゴーストは、どんなゴーストでしょうか?
お気に入りの可愛い、カッコイイゴーストを立たせている方もいれば、便利機能を持っているゴーストを立たせている人もいると思います。
私は普段、PCでの作業中にはお手製のプライベートゴーストを立たせていて、これには日常の作業や生活の中でちょっと便利な機能をいくつか持たせてあります。
今回は実際にこのプライベートゴーストで使用している
『ちょっと便利』な機能を実装する為のソースコードを紹介しようと思います。
あ、先にお断りしておきますが、題名にある通り、何時ものようにYAYAのコードです。その他の栞をお使いの方すみません。(私がYAYAしか使えないので><)
それと今回紹介する機能には配布用ゴーストには不向きな物もありますのでご了承ください。その他、プログラムとして結果が精度に欠ける物もありますが、
あくまで「日常作業・生活でちょっと便利」程度の機能ですので、生暖かく見守ってやってください。
と言う訳で、早速本日ご紹介する機能はこちらっ!
・関数テスト
・式入力電卓
・キッチンタイマー
以上の3つです。
簡単に説明していくと、関数テストはその名の通り、辞書内、及びYAYAの関数を直接使用するだけの機能です。
ゴースト制作時に新たに作った関数の実験のみならず、実際にはゴースト内で使用していなくても関数だけ使用する事ができるので、
関数次第で普段の作業の力強い助っ人となるっ!・・・かも?
次に式入力電卓、これは関数テストの応用で、直接スクリプトを入力できるんだから計算もできるよね!という事でつけた機能。
例えば普段電卓で計算する時に、120*5+260*3なんて計算はどうしてます?掛算をする度にメモしたり、
メモリー機能が付いてる電卓でも、いざその機能を使おうと思ったらどの手順でボタンを押せばいいのか忘れてたり・・・。
そんな面倒な事はこの機能で一気に解決!ゴーストに式を渡せば括弧や演算子の優先順位を判断して計算してくれます!
そして最後は、作業をしてたらお腹すくよねって事でキッチンタイマーです!機能の説明は・・・要らないですね。
一応今回のコードでは分単位でしか計測できません。
●関数テスト
それでは早速コードの説明。
今回紹介する3つの機能はいずれもインプットボックスの入力から始まるので
\![open,inputbox,*,*,*]から開始します。
// インプットボックスオープン。メインメニュー等、適当な場所からこのコードで開始させます。
{
"\0関数入力して下さい。\![open,inputbox,functiontest,0]\e"
}
// インプットボックス反応
OnUserInput
{
if reference0 == "functiontest"{
_ans = EVAL(reference1) // 入力された文字列をコードとしてそのまま実行
"\0結果は %_ans です。\![open,inputbox,functiontest,0,%_ans]\e"
}
}
使い方は簡単。例えばインプットボックスに
STRLEN("あいうえお")と、入力すると
結果は 5 です。と返ってきます。
え〜っと・・・・・・以上です。
うわっ、空き缶飛んできた!?次、次行ってみましょう!!
●式入力電卓
先にも書いたように関数テストの応用です。
さて、この機能を実装するにあたりポイントになるのは、プログラムでの決まり事をどう切り抜けるかです。プログラムでは割り算をする時、
整数÷整数=整数になってしまいます。例えば
5/2=2になってしまいます。
これを回避する為にはどちらか一方、あるいは両方を実数にする必要があります。
その為、今回のコードでは式の文字列のうち、整数部分を実数に置き換える作業をしています。例えば
3/2+1.5であれば
3.0/2.0+1.5に置き換えます。
それともう1つ、答えが仮に整数だったとしてもこのままでは実数として表示されます。例えば答えが
5であれば
5.000000という形です。
これでは見た目的に若干よろしくないので、末尾の"0"を削除するコードも紹介します。(これを使うかどうかはお好みですが)
それではコードです。こちらもインプットボックスから開始します。
// インプットボックスオープン。メインメニュー等、適当な場所からこのコードで開始させます。
{
"\0式を入力して下さい。\![open,inputbox,dentaku,0]\e"
}
// インプットボックス反応
OnUserInput
{
if reference0 == "dentaku"{
_ans = EVAL(Y.ChangeRealNumEx(reference1)) // 入力された式を実数化した式にして実行
_ans = Y.CutEndZero(_ans) // 実数の末尾0を削除
"\0答えは %_ans です。\![open,inputbox,dentaku,0,%_ans]\e"
}
}
//-------- 以下、関数群 ---------------------------------------------------------------------------
// 計算式実数化【引数:計算式文字列】【戻り値:実数計算式文字列】
Y.ChangeRealNumEx
{
_len = STRLEN(_argv) // 式の文字数を取得
_strArray = Y.StrToArray(_argv) // 式を一文字ずつ簡易配列化
for _i = 0; _i < _len; _i++{ // 文字数分繰返し
if _strArray[_i] >= "0" && _strArray[_i] <= "9" && _i == _len-1{ // 最後が数字なら".0"を追加
_strArray[_i] += ".0"
}elseif _strArray[_i] == "."{ // "."なら数字の間読み飛ばし
for _i += 1; _i < _len; _i++{
if !(_strArray[_i] >= "0" && _strArray[_i] <= "9"){
break
}
}
}elseif !(_strArray[_i] >= "0" && _strArray[_i] <= "9") && _strArray[_i-1] >= "0" && _strArray[_i-1] <= "9"{ // 記号の1つ前が数字なら".0"を追加
_strArray[_i-1] += ".0"
}
}
// 簡易配列を文字列化
_size = ARRAYSIZE(_strArray)
_str = ""
for _i = 0; _i < _size; _i++{
_str += _strArray[_i]
}
_str
}
// 文字列簡易配列化【引数:文字列】【戻り値:一文字区切りの簡易配列】
Y.StrToArray
{
_len = STRLEN(_argv)
for _i = 1; _i < ((_len-1)*2); _i += 2{ // 一文字毎に","を追加
_argv = INSERT(_argv,_i,",")
}
_argv
}
// 実数ショート化【引数:実数or実数表記の文字列】【戻り値:末尾"0"無し文字列】
Y.CutEndZero
{
// 引数タイプチェック
_type = GETTYPE(_argv)
if _type == 2{
CVSTR(_argv)
}elseif _type == 0 || _type == 1 || _type == 4{
_argv
return
}
_len = STRLEN(_argv)
_strArray = Y.StrToArray(_argv)
for _i = (_len-1); _i>0; _i--{ // 末尾から先頭に向けチェック
if _strArray[_i] == "0"{ // "0"なら削除
_argv = ERASE(_argv,_i,1)
}elseif _strArray[_i] == "."{ // "."なら削除
_argv = ERASE(_argv,_i,1)
break
}else{
break
}
}
_argv
}
使い方は、例えば
3*(2+3)/2と、入力すると
答えは 7.5 です。と返ってきます。
一応、関数テストと同じ方法で実行しているので、式に関数を使用する事も可能ですが、関数名、又は関数に渡す引数に整数が入っている場合、
実数に置き換えられてしまう為、意図しない動作・結果が出る場合があるので、何らかのエスケープする手段を追加した方がいいですね。
注意
プログラムに詳しい方は解っていると思いますが、コンピューターの世界で例えば
1と
1.000000は、数字の上で必ずしも等しいとは限りません。
無限に存在する小数点以下の桁数を、一定の桁数で計算・表示する為に丸めが行われます。YAYA(SSP?)では小数点以下6桁までが表示されるので、
今回のコードでは仮に答えが
4.9999995や、
5.0000004だった場合、どちらも
答えは 5 です。と返ってくる事になります。
ただし、内部的にはもう少し小さい桁まで計算はしているので、仮に
4.9999992+0.0000008を計算させると、答えは
5が返ってきます。
私もプログラマではないのでちゃんと理解しているわけではありませんが、詳しくは『浮動小数点 精度』などをググって下さい。
そういった意味で、計算精度がそれほど高くは無い事に注意してください。実際の日常生活内では問題ない精度だとは思いますが。
●キッチンタイマー
そろそろ記事を書いてる私もお腹が減ってきました。と、言う事で最期はキッチンタイマー機能です!
多分ですがこれも精度はそれほど高くないと思います。どの道サンプルコードは分単位でしか計測してませんけど・・・。
それでは早速コードに行ってみましょう。
// インプットボックスオープン。メインメニュー等、適当な場所からこのコードで開始させます。
OnTimerInput
{
"\0計測する時間を入力してください。\n入力可能単位は分です。\![open,inputbox,OnTimerStart,0]\e"
}
// タイマー反応
OnTimerStart
{
reference0 = ZEN2HAN(reference0) // 全角→半角
_intflag = ISINTSTR(reference0)// 整数チェック
if _intflag == 1{
// 変数初期設定 timerFlag/timerCount_s/timerCount_mはグローバル変数宣言します。
reference0 = TOINT(reference0)
timerFlag = 0
timerCount_s = 0
timerCount_m = 0
// 計測開始メニュー
"\0\![set,choicetimeout,0]\0\s[0]%reference0分ですね。\n/
計測を開始します、スタートをクリックしてください。\n\n\n\n/
\q[スタート,OnTimerCounter,%reference0]\n/
\q[時間再入力,OnTimerInput]\n/
\n/
\q[キャンセル,CANCELTIMER]\n/ // OnChoiceSelectイベント等でキャンセル処理してください。
\e"
}else{ // 整数じゃない場合は再入力
"\0数字以外の文字列が含まれてます。\n/
整数のみで再入力してください。\![open,inputbox,OnTimerStart,0]\e"
}
}
// 計測処理
OnTimerCounter
{
if timerFlag == 0{ // 計測開始
reference0 = TOINT(reference0)
timerSetMinutes = reference0
"\0計測を開始します。\e"
timerFlag = 1
}elseif timerFlag == 1{ // 計測中処理
timerCount_s++
if timerCount_s == 60{ // 60秒経過処理
timerCount_s = 0
timerCount_m++
if timerCount_m == timerSetMinutes{ // 計測終了処理
timerFlag = 0
"\0%timerSetMinutes分経過しました。\n計測を終了しますね。\x\e"
}else{ // 分毎お知らせ
"\0%timerCount_m分経過しました。\e"
}
}elseif timerCount_m == timerSetMinutes-1 && timerCount_s == 30{ // 残り30秒お知らせ
"\0"
--
if timerCount_m != 0{
"%timerCount_m分"
}
--
"%timerCount_s秒経過しました、\n/
残り30秒です。\e"
}
}
}
// デフォルトで1秒ごとに呼び出されるイベントです。
OnSecondChange
{
if timerFlag == 1{
OnTimerCounter
}
}
使い方はインプットボックスに何分計測するか、整数を入力すると計測を開始します。1分ごとと、設定時間30秒前にお知らせがでます。
お知らせや、設定時間になった時に音などを鳴らすとわかりやすくなりますよ。
あ、あと計測中にゴーストを終了してしまった場合、timerFlagに
1が入ったままになってしまうので、終了イベントやブートイベントなど、
どこかでリセット(timerFlag = 0)するようにして下さい。
OnSecondChangeイベントを使用しているのであまり重い事はさせられませんが、このくらいなら大丈夫・・・かな?おっと、カップラーメンがのびるところだった。
●さんぷるー
さて、如何だったでしょうか。今回のコードを纏めたテストゴーストをこちらに置いておきます。
いつもどおり何かが起きても責任は持ちませんがコードはご自由にお使いください。
ストーリー性の高いゴーストだと便利機能付きってどうなの・・・?って場合もあるかもしれませんが、
便利機能に限らず、ユーザーさんに1秒でも長くデスクトップに置いてもらえるように、色んな工夫をして素敵なゴーストにしましょう!
【サンプルコード収録テストゴースト】:
yasi_test.nar
Hobbyへ
TOPへ