aburi6800のブログ

コンピュータのプログラミング、ゲームに関するニッチな情報を書いていくブログです。

【MSX】MSX-BASICの研究(1) 条件分岐ロジック

この記事について

MSX-BASICでプログラムを書いていて思い出すのが、昔ベーマガでDr.Dが「論理式を使うのじゃ」と口を酸っぱくして言っていたこと。 確かに論理式を使うとIF文は無くなるが、しかし、論理式は本当に速いのだろうか?
というのも、ネットでBASICの高速化テクニックを探していたところ、ON〜GOTO〜を使うほうが速い、という記事を見つけたためで、本当はどれが速いのだろうか?と考えた次第。
それじゃあ実際に比較してみようか、というのがこの記事の趣旨になります。

今の時代にどれだけの人が必要としている情報なのかわかりませんが(汗

※(2021/8/9)パターン4として、移動量を配列変数に定義した場合をテストしてみました。
記事の最後に追記しています。

テスト方法

今回のテスト方法は、以下の方針としています。

  • WebMSXで実施。
  • 一般的に使用される、SCREEN 1、WIDTH 32、DEFINT A-Zで初期化した状態で実行する。
  • ロジックは、実際の実装を考慮して4方向の判定処理とする。
  • カウントダウン終了から画面右端までの移動時間を測る。

パターン1

素直に入力値をIF文でひとつひとつ判定していくコードです。

プログラムリスト

100 ' *******************************
110 '   CHARACTER MOVE LOGIC TEST 1
120 ' *******************************
130 ' *** COMMON INITIALIZE ***
140 SCREEN 1:WIDTH 32:DEFINT A-Z
150 LOCATE 0,0:FOR I=3 TO 1 STEP-1:PRINT I;:FOR J=0 TO 999:NEXT J,I:PRINT " START"
160 X=0:Y=10:TM=TIME
200 ' *** LOGIC ***
210 S=STICK(0):VX=0:VY=0
220 IF S=1 THEN VY=-1:GOTO 260 
230 IF S=3 THEN VX=1:GOTO 260
240 IF S=5 THEN VY=1:GOTO 260
250 IF S=7 THEN VY=-1
260 LOCATE X,Y:PRINT " ";:X=X+VX:Y=Y+VY:LOCATE X,Y:PRINT "A";
270 IF X<32 GOTO 200
300 ' *** RESULT *** 
310 LOCATE 0,1:PRINT "TIME="; TIME-TM
320 END

実行結果

f:id:aburi6800:20210808125512p:plain
パターン1の実行結果

パターン2

ON〜GOTO〜で分岐させるコードです。

プログラムリスト

100 ' *******************************
110 '   CHARACTER MOVE LOGIC TEST 2
120 ' *******************************
130 ' *** COMMON INITIALIZE ***
140 SCREEN 1:WIDTH 32:DEFINT A-Z
150 LOCATE 0,0:FOR I=3 TO 1 STEP-1:PRINT I;:FOR J=0 TO 999:NEXT J,I:PRINT " START"
160 X=0:Y=10:TM=TIME
200 ' *** LOGIC ***
210 S=STICK(0)
220 ON S+1 GOTO 270,230,270,240,270,250,270,260,270
230 VX=0:VY=-1:GOTO 270
240 VX=1:VY=0:GOTO 270
250 VX=0:VY=1:GOTO 270
260 VX=-1:VY=0:GOTO 270
270 LOCATE X,Y:PRINT " ";:X=X+VX:Y=Y+VY:LOCATE X,Y:PRINT "A";
280 IF X<32 GOTO 200
300 ' *** RESULT *** 
310 LOCATE 0,1:PRINT "TIME="; TIME-TM
320 END

実行結果

f:id:aburi6800:20210808125735p:plain
パターン2の実行結果

パターン3

論理式を使ったコードです。

プログラムリスト

100 ' *******************************
110 '   CHARACTER MOVE LOGIC TEST 3
120 ' *******************************
130 ' *** COMMON INITIALIZE ***
140 SCREEN 1:WIDTH 32:DEFINT A-Z
150 LOCATE 0,0:FOR I=3 TO 1 STEP-1:PRINT I;:FOR J=0 TO 999:NEXT J,I:PRINT " START"
160 X=0:Y=10:TM=TIME
200 ' *** LOGIC ***
210 S=STICK(0)
220 VX=(S=7)-(S=3)
230 VY=(S=1)-(S=5)
240 LOCATE X,Y:PRINT " ";:X=X+VX:Y=Y+VY:LOCATE X,Y:PRINT "A";
250 IF X<32 GOTO 200
300 ' *** COMMON RESULT *** 
310 LOCATE 0,1:PRINT "TIME="; TIME-TM
320 END

実行結果

f:id:aburi6800:20210808125608p:plain
パターン3の実行結果

比較結果

結果は以下のようになりました。

パターン 処理方法 処理時間
1 IF文のみ 56
2 ON〜GOTO〜 51
3 論理式 59

総括

ON〜GOTO〜で処理するのが一番速いですね。実際にテストしていても、体感できるくらいに違いました。
また、パターン3の論理式がパターン1のIF文のみより遅かったという結果は、ちょっと意外でした。
しかし、パターン1のプログラムを見ていただけるとわかりますが、IF文で判定とは言え、条件に合致したときに他のIF文に入らないようにGOTOで飛ばしていますので、速くなっていたのでしょう。
論理式ではすべての値を評価する必要があるので、全てのIF文を実行しているのと同じことになっているのだと考えます。

実際、パターン1で各IF文のGOTOを書かなかった場合、処理時間は64と最も遅い結果になっていました.。

プログラムで条件分岐書く時は、ON〜GOTO〜で書けるように変数の値も考えると良いですね。

また気になる事があったら、検証してみたいと思います。


※2021/8/9 追記

パターン4

入力に対する各方向への移動量を配列変数に定義したコードです。

プログラムリスト

100 ' *******************************
110 '   CHARACTER MOVE LOGIC TEST 4
120 ' *******************************
130 ' *** COMMON INITIALIZE ***
140 SCREEN 1:WIDTH 32:DEFINT A-Z
142 DIM VX(9),VY(9):FOR I=0 TO 8:READ VX(I),VY(I):NEXT I
144 DATA 0,0,0,-1,0,0,1,0,0,0,0,1,0,0,-1,0,0,0
150 LOCATE 0,0:FOR I=3 TO 1 STEP-1:PRINT I;:FOR J=0 TO 999:NEXT J,I:PRINT " START"
160 X=0:Y=10:TM=TIME
200 ' *** LOGIC ***
210 LOCATE X,Y:PRINT " ";:S=STICK(0):X=X+VX(S):Y=Y+VY(S):LOCATE X,Y:PRINT "A";
220 IF X<32 GOTO 200
300 ' *** COMMON RESULT *** 
310 LOCATE 0,1:PRINT "TIME="; TIME-TM
320 END

実行結果

f:id:aburi6800:20210809132336p:plain
パターン4の実行結果

比較結果

パターン4を含むと、以下の結果になりました。

パターン 処理方法 処理時間
1 IF文のみ 56
2 ON〜GOTO〜 51
3 論理式 59
4 配列変数 43

配列変数が抜き出て速いですね。
パターン1や2にあるGOTOが無いのでこの結果になったのだと考えます。
BASICはインタプリタ言語なので、命令数が1つ増えただけでも処理速度に影響するというのは認識ありましたが、実際に結果を見ると相当違いますね。

なお、このテストでは、座標の上限値/下限値を1方向しか判定していないので、実際にゲームに組み込んだ場合の性能とは異なります。
あくまで指標として捉えていただけると幸いです。

【MSX】GitHubに置いた.dskファイルをWebMSXで実行する方法

今日書いたこちらの記事の補足です。

aburi6800.hatenablog.com

最初、WebMSXでGitHubに置いた.dskファイルを読めなかったので、フリーのWebサーバに.dskファイルを置いていたのですが、URLパラメタの指定方法を見直したらGitHubの.dskファイルでも実行できましたので、書いておきます。

具体的には、以下のように指定します。

https://webmsx.org/?MACHINE=MSX1J&DISKA_URL=<対象の.dskファイルのURL>&FAST_BOOT

「DISKA_URL」パラメタで与えるのがミソです。
なお、最後の「FAST_BOOT」は気持ち起動が速くなるようなので付けていますが、任意です。(この指定の仕方でいいのか自信がない…)

そして、この「DISKA_URL」に与える「対象の.dskファイルのURL」ですが、GitHubで対象の.dskファイルのページにある「Download」ボタンのURLになります。
このボタンを右クリックした「URLをコピー」することで取得したURLを指定してください。
f:id:aburi6800:20210802001812p:plain

他、多数のオプションを指定できますが、詳細は以下のGitHubリポジトリのドキュメントを参照してください。

github.com

ちなみに、使用する.dskファイルがあるリポジトリは、publicにしておいてくださいね。

ではでは。

【MSX】BLOCK BREAKER

唐突ですが、MSX-BASICの練習で、ブロック崩しゲームを作ってみました。
(ボールの挙動が若干怪しいのと、パドルの移動にイライラしますが…)

以下のURLで遊べます。
(※GitHubの.dskをWebMSXに直接渡せたので、URLを変更しました)

webmsx.org

しばらく待って、タイトルが出たらSPACE or Aボタンでスタート。

f:id:aburi6800:20210801174251p:plain
タイトル画面

カーソルキー or コントローラの左右でパドルを動かしてボールを落とさないようにして、ブロックを壊してください。

f:id:aburi6800:20210801174325p:plain
ゲーム画面

ボールを3回落とすとゲームオーバーです。

f:id:aburi6800:20210801174354p:plain
ゲームオーバー画面

githubでもソースとディスクイメージ、某雑誌風のドキュメントを公開しています。 詳細については、こちらのドキュメントを参照してください。

github.com

このプログラムを元にアセンブラ化していきたいと思っていますが、小数点の計算をどうするかな…

ではでは。

【git】GithubにSSH接続する

Githubリポジトリへのアクセスは、主にhttpsSSHがある。
一度cloneするだけであればhttps接続で問題ないが、GitHubではSSH接続が推奨されており、アクセスするたびにユーザー名とアクセストークンを入力することも不要になるため、普段から利用するリポジトリへのアクセスはSSHが望ましい。
このSSHの設定方法については既に多数の記事が存在するが、ようやく自分の中で手法が固まったので、まとめ直したものを記載する。

0. SSHキー生成の準備

基本的に、1つの端末に1つ作成するイメージで良いと思う。
Windowsの場合は自分のホームディレクトリに.sshフォルダを作成後、スタートメニューからGit Bashを起動する。
Linuxの場合は、ホームディレクトリでそのまま作業を行う。

以下のコマンド実行例については、WindowsのGit bashを使ったものになります。

1. SSHキー生成コマンド

以下のコマンドを実行する。

$ ssh-keygen -t rsa -C <メールアドレス>

作成先のディレクトリの確認。
デフォルトで問題ないため、そのままEnterを押す。

Enter file in which to save the key (/c/Users/hitoshi/.ssh/id_rsa):

作成先に既に作成されている場合は以下のメッセージが表示される。
上書きして良いならyを入力する。

/c/Users/aburi/.ssh/id_rsa already exists.
Overwrite (y/n)? 

続いてパスフェーズの入力を求められる。
何も入力せずEnterを入力する。

Enter passphrase (empty for no passphrase):
Enter same passphrase again:

パスフェーズを設定するとVS Codeからのpush/pull操作が上手くいきませんでした。
回避方法があると思われますが、個人利用であれば未設定で構わないと思います。

以下のメッセージが表示され、公開鍵が生成される。

Your identification has been saved in /c/Users/hitoshi/.ssh/id_rsa.
Your public key has been saved in /c/Users/hitoshi/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:<暗号化文字列>
The key's randomart image is:
+---[RSA 2048]----+
|    +o*o         |
|   . *.=.        |
|    O o          |
|   + = =         |
| .. * o S        |
|+..+ + = +       |
|==o . + =        |
|#... ..o..       |
|@O   ... E       |
+----[SHA256]-----+

後半のランダムアートの内容は、生成するごとに変わります。

2. 作成結果確認

.sshディレクトリにid_rsa.pubが存在していれば成功。

$ ll
total 6
-rw-r--r-- 1 hitosh i 197121 1671 2月   3 01:51 id_rsa
-rw-r--r-- 1 hitoshi 197121  401 2月   3 01:51 id_rsa.pub
-rw-r--r-- 1 hitoshi 197121  405 2月   3 01:35 known_hosts

3. Githubに公開鍵を登録

生成した公開鍵の内容をコピーする。
id_rsa.pubテキストエディタで開き中身をコピーするか、以下でコンソールに出力したものをコピーする。

$ cat ~/.ssh/id_rsa.pub

Git Bashでは以下コマンドでファイルの内容をクリップボードにコピーできるため、楽で確実です。

$ clip < ~/.ssh/id_rsa.pub

githubのアカウントの「Settings」ページを開き、「SSH and GPG keys」を選択、「SSH keys」の右側にある「New SSH key」ボタンを押す。
名前は適当に、「<端末名>@<OS名>」のようにすると良いかもしれない。
f:id:aburi6800:20210725121553p:plain

4. GithubとのSSH接続確認

接続の確認は以下で行う。

$ ssh -T git@github.com

成功すると以下のメッセージが表示される。

Hi <ユーザー名>! You've successfully authenticated, but GitHub does not provide shell access.

5. トラブルシューティング

失敗した場合は、以下を確認する。
1. そもそもネットワークに接続されているか。wifiや有線LANの接続を確認する。
2. Githubに登録したSSH公開鍵の内容。一度削除して登録しなおしてみる。
3. ssh公開鍵の作成内容。暗号化方式が誤っていないかなど。一度.sshディレクトリ内のファイルを削除し、再度作り直してみる。

【Obsidian】Obsidianのモバイルアプリを試してみた

f:id:aburi6800:20210725005439p:plain

Obsidianは動作が軽く使いやすい、マークダウン形式で記述するメモアプリだが、これまでモバイルデバイス向けのアプリケーションが存在しなかった。
そのため、個人的にはAndroidのGitJournalというアプリを使用してきたが、見た目や操作感が違うためあまり快適とは言えず、またまともに使うためには課金が必要(しかもサブスクのため払い続けなければならない)であるため、活用できていなかった。
しかし、ようやくObsidianのモバイルアプリが2021/7/12にリリースされた。
早速試してみたので、所感などを記しておく。

この記事は導入した時点でのものになります。
今後本格的に使用し、細かいことが判明次第、加筆修正していきます。

ファーストインプレッション

軽く触ってみた感想。あくまで個人的な感想です。

  • 動作も軽く、デスクトップ版の雰囲気そのままで使える。
  • ただし、操作が若干複雑。これは設定で何とかなるのか。
  • テンプレートの場所、日付書式、プラグインといった設定については、デスクトップアプリの設定を参考に設定しなおさなければならない。
  • 多言語対応がまだされていない。ただ、デスクトップアプリを使用していればそれほど困らず、そのうちサポートもされることだろう。
  • PCでObsidianを使っている人なら、データ同期の問題を何とかさせてでも使った方がいい。

データの同期について

「なんとかしてデータ同期させて」と書いたのは、Obsidianがローカルにデータを保持するアプリケーションであり、単体ではクラウドストレージに対応していない為である。
ここでは、現状選択肢として選択可能なクラウドストレージサービスと、簡単な概略を記載する。

Obsidianのモバイルアプリのみを使う人はこの限りではありません。 たとえばタブレット端末でしか使わない、というのもアリで、その場合は単純にValutを作成するだけになります。

obsidian sync

obsidianオフィシャルの同期サービス。
プラグインでサポートされているため、OSやデバイスを意識せずに使える。
また、Obsidian publishというWeb公開サービスも使用できる。
有料となっているが、ヘビーに使いたい場合は選択肢に入れても良いかもしれない。

git(github)

デスクトップ版では「Obsidian git」というサードパーティープラグインがあり、githubを介して複数PC間のデータ同期ができていた。
しかし、スマートフォン(自分の場合はAndroid)ではこのプラグインがサポートされていない。
また、gitクライアントアプリもあるが、手動でpush/popする必要があり、操作性に欠ける。
そのため、今回はgitの使用を見送ることとした。

手動でgitリポジトリからpopする運用で良ければ、この手段でも問題ないです。

google drive

PC(Windows)では、googleドライブアプリを入れることで、クラウドであることを意識せずに使える。
Androidでは、google製の「ドライブ」はクラウドストレージの中を見るだけでローカルとの同期は取れず、個別にファイルをダウンロードするしかない。
この目的を達成するものとしては「FolderSync」や「Autosync」というアプリがある。
なお、ubuntu(Linux)での使用については、google製のアプリがリリースされていないため環境駆逐の問題が残るのと、googleはサービス自体に突然大きな仕様変更を行う不安定さがあるのが難点。

Linuxでのgoogleドライブのマウントは、google-drive-ocamlfuseというアプリケーションでマウント可能になる模様。
これについては別記事にしようと思います。

OneDrive

PC(Windows)の操作感はgoogle Driveと同様、クラウドであることを意識せずに使える。
Androidは「FolderSync」というアプリでクラウド上(OneDrive)のファイルと端末のディレクトリ(任意指定可能)の同期をとることができるらしい。

らしい、というのは私がこのアプリ(FolderSync)を使用していないから。レビューを見ると不安定なようだったので、見送りました。

また、Microsoftのサービスであるためgoogleほどの不安は無いが、ubuntu(Linux)ではまだMicrosoft製のアプリがリリースされていないため、google driveと同様に環境構築の問題がある。

Dropbox

過去はクラウドストレージの筆頭となるサービスだったが、無料での利用台数が3台まで、他のストレージに比べて容量が少ないといった使い勝手の悪さと、有料の価格設定の高さから、ヘビーユーザーにはお勧めできない。
しかし、公式にubuntu(Linux)への対応を行っていること、モバイル端末でのアプリも様々なものがあるため、利用台数が少ない場合(PC1台、モバイル端末1台など)は、Dropboxを選択するのはアリだと思う。

その他

その他のサービスも同様で、使用しているOSとモバイルデバイスでのサポート状況を確認し、問題なければ使用できる。
問題がある、ないの判断は「ローカルとクラウドでデータが自動的に同期できるか」で良いと思う。

FAQ

Valut(書庫)を開こうとするとエラーが発生して開けない

SDカードのディレクトリをValutに指定したのが原因。
本体ストレージにディレクトリを用意すること。

バージョン 1.0.3時点。
恐らくSDカードへのアクセス権限付与漏れで、将来的に解消されると思われる。

エディタの文字サイズが大きい

プレビューは普通なのに、エディタに入ると文字が大きくなる。
これは設定メニュー(歯車のアイコン)から「Appearance」に入り「Font size」で指定できる。
指定後は、一度アプリを終了させること。

【Python】Pythonの仮想環境を使う

例えば、複数のアプリケーションを開発していて、導入パッケージがそれぞれ異なる場合、各アプリケーションで最低限必要なものだけを導入した環境が欲しくなることがある。
また、Pythonの基本的なバージョンは上げつつ、開発中のアプリケーションは特定のバージョンに固定したいことがある。
このようなときは、Pythonの仮想環境を使用し、Pythonのバージョンと導入パッケージをアプリケーションごとに固定する。
ここでは、Python標準機能として用意されているvenvを使用する。

仮想環境の作成

バージョンを指定せずに作成

プロジェクトのルートディレクトリで以下を実行する。 (以下は仮想環境名を.venvとした場合)

python -m venv .venv

こうすることで、パスが通ったPythonのバーションで作成される。

Linuxなどの場合は、pythonコマンドではなくpython3コマンドの場合があります。 ubuntu20.04だとpythonコマンドで大丈夫です。

仮想環境のバージョンを指定する(Windows

WIndowsの場合はpyコマンドがあるので、プロジェクトのルートディレクトリで以下を実行する。

py -3.6 -m venv .venv

最初の引数(-3.6)は、Pythonのバージョンとなる。

仮想環境のバージョンを指定する(macLinux

macLinuxの場合は、pyenvを使うのが良さそう。
githubに公開されているので、インストールする。
使い方などは以下を参照。(そのうち追記します)

参考

[https://www.mathpython.com/ja/ubuntu-python/:embed:cite]

仮想環境の使用

Windows(コマンドプロンプトPowerShell)の場合

プロジェクトのルートフォルダで以下を実行する。

./.venv/Scripts/activate

MacLinuxの場合

プロジェクトのルートディレクトリで以下を実行する。

source ./.venv/bin/activate

運用

仮想環境でパッケージをインストールする

以下コマンドを実行することで、その仮想環境にパッケージが導入される。

pip install [package name]

仮想環境に導入されているパッケージを確認する

仮想環境に切り替え後に以下コマンドを実行する。

pip freeze

仮想環境を配布する

venvしたディレクトリをそのまま渡すのではなく、各端末で仮想環境を作成しパッケージを導入する方法をとる。
まず、配布元で導入するパッケージのリストを作成する。

pip freeze > requirement.txt

作成されたrequirement.txtを配布し、構築する端末では以下コマンドでパッケージを導入する。

pip install -r requiments.txt

切り替え後のPythonのバージョンを確認する

仮想環境に切り替え後に以下コマンドを実行する。

python -V

Vは大文字

仮想環境を終了する

WindowsMacLinux共に、以下のコマンドを実行する。

deactivate

仮想環境を削除する

作成されたディレクトリを削除するだけ。

所感

pip freeze > requirements.txtをする際に余計なパッケージを含めたくないときなどは、同じpythonバージョンでも別環境を作った方が良い。
また、複数の環境が作れるので、例えば古いバージョンでバグ報告があった場合の検証環境も簡単に用意できる。
OSごとに若干操作方法が異なるが、標準で使えてお手軽だし、とても便利なので、個人開発でも積極的に使っていきたいと思う。

【Python】PyInstallerの警告に対する対処方法について

環境

発生した環境は以下。

発生事象

上記環境にて、pyxelpackagerでアプリケーションの単体実行ファイル生成時、以下警告が発生。
単体実行ファイルが作成されない。

12467 WARNING: Tcl modules directory /usr/share/tcltk/tcl8.6/../tcl8 does not exist.

原因

pyxelpackagerでは、引数(アセット、実行ファイル)を作成する処理のみを行い、PyInstallerに渡す。
単体実行ファイルを作成する処理はPyInstallerにて行われるが、このPyInstallerでのパスの指定がおかしい。
(tcl8ディレクトリはtcl8.6の下に存在しているが、その上の階層を参照しようとしている)

対処

~/.local/lib/python3.8/site-packages/PyInstaller/hooks/hook-_tkinter.py の 206行目に余計な引数が指定されているのを削除。

(修正前)

206    modules_path = os.path.join(tcl_root, "..", modules_dirname)

(修正後)

206     modules_path = os.path.join(tcl_root, modules_dirname)

その後、pyxelpackagerを実行すると、単体実行ファイルが作成された。

補足

venvで仮想環境を作成している場合、修正対象ファイル(hook-_tkinter.py)は以下に配置される。

./lib/python3.8/site-packages/PyInstaller/hooks/hook-_tkinter.py

なお、Macは手元に環境が無いため未確認だが、恐らく同様の原因だと思われる。