第43回目:プチコン3号(BASIC)でさめがめ(Part2)2015-03-14筆者:村田こんにちは。先週の話ですが、とあるロックバンドのライブを見に行ってきました。オルタナロックって感じでカッコ良かったです。ライブ中の演出として映し出されていた背景映像も面白かったです。周期的デザインやBOIDシミュレーションなどプログラミングツールで作られたと思われる映像は、トリップ感があって、どんなプログラムなんだろうと興味を惹きます。ところで、ライブ会場への入場って10年以上も前から変わっていない気がします。人手でチケットをもぎり、ワンドリンク代金の徴収にも列を作って全員並ぶことになります。スマホなどのツールでスムーズな入場システムやゲートとかあれば助かるのにな〜と。それではプチコンの続きです。最近プチコンのバージョンアップがありました。命令の動作変更もあったようですが、さめがめには影響なさそうです。今回の最初の目標は十字キーでカーソルを移動させ、コマを選択できるようにすることです。ゲームのメインループを作ります。ループを抜けるまではゲームが続きます。前回の初期化ルーチン呼び出しとプログラム終了のEND呼び出しの間にWHILE無限ループを作ります。シュウリョウでは十字キーの値を取ってみましょう。BUTTON関数で取得できます。引数の1はボタンが押された瞬間に値を取得するためのパラメータです。BUTTON関数のようなプチコンが用意している関数、命令の説明はヘルプボタンですぐに確認できます。1WENDVSYNCは指定されたフレーム数分、プログラムを停止して同期させる命令です。上記例ではプログラムの実行を1フレーム分待機させています。これを行わないとループが高速過ぎます。十字キーに応じてカーソルを移動させる処理を作りましょう。今回はサブルーチンではなく、関数(プロシージャ)で作ります。END"という構文になります。戻り値の無いプロシージャを作成する場合は、引数の宣言に()を付けません。CS,CX*16+MX,CY*16+MYEND十字キーの入力に基づいてカーソル位置(CX,CY)を更新します。後はメインループでMOVEを呼びましょう。1WENDこれでカーソルが移動するようになりました。次の目標に行く前に、プログラミング中のテンションを上げるために音楽を聴こうと思います。ネット上にはプログラミング専用(?)のBGMストリーミングやMP3などがありますが、今回はプチコンで音楽を聴きながら作業したいと思います。TOOLSを使用して40曲以上のファミコンゲーム風BGMを試聴できます。27のように記述するだけです。それでは次の目標はカーソル選択したコマと、同種の隣接コマを消す処理を書きたいと思います。選択したコマを削除するプロシージャを作ります。管理番号"で指定したスプライトを削除することができます。ボードマップ上(SMAP,PMAP)の管理番号も忘れずにクリアしておきます。隣接コマを消しましょう。色々な消し方がありますが、隣接コマ(上下左右)を探して、さらにそのコマの隣接コマを・・という風に考えると再帰アルゴリズムが向いていそうです。CEND今回は戻り値に削除したコマの数を返すので、関数となります。関数定義の場合は引数リストを()でくくります。変数Cはローカル変数です。したがって関数NDELを再帰して呼び出すごとに新しいローカル変数が作られて、削除したコマの数が累積します。それとローカル変数を使うときはVARで変数宣言を行いましょう。VARがなくてもDEF〜END内で変数を宣言できますが、もしグローバルに変数Cが存在していたら、C=1という記述はローカル変数Cの生成ではなく、グローバル変数Cへの代入ということになってしまいますので。グローバル変数も含めて全てきちんと変数宣言(VAR)を書いた方がいいかもしれませんね。ところで、少し横道に逸れますが、練習でさめがめを書いていた時の話です。BASICのマニュアルをほとんど読んでおらず(早く遊びたかったし・・)、DEF文による関数や、関数引数、ローカル変数を知りませんでした。そのため、サブルーチンとグローバル変数で再帰サブルーチンを書いていました。関数スタックの代わりに配列を用意したり、グローバル変数の名前管理に気を付ける必要がありますが、サブルーチンだけでさめがめを作るのも結構面白いです。その後、日経ソフトウエアの記事でDEF文を知り、あらためてローカル変数は便利だなと思いました:Pここまでで、さめがめのルール1(3種類のコマの敷き詰め)、ルール2(隣接コマの削除)が実装できました。次はルール3「消えたコマの上にあったコマは下に落ちる」を実装しましょう。垂直方向の圧縮ということでVPACKというサブルーチンにまとめました。1BREAKENDIFNEXTENDIFNEXTNEXTRETURN3重ループ(--;)です。消えたコマ(隙間)の上のコマを落とす際は、SPOFSでスプライトの移動、それと忘れずにボードマップ(SMAP,PMAP)の値も上下入れ替えます。SWAP命令は2つの変数の値を交換してくれます。VSYNCを入れたのは、一つ一つのコマが落ちる間に少しウェイトを入れて、アニメーションっぽい動きにしたかったからです。本日最後はルール4「縦一列のコマが全て消えると右隣の列が左に移動する」の実装です。水平方向の圧縮ということでHPACKというサブルーチンにまとめました。内容はVPACKとほぼ同じなので省略します。冒頭に作成したメインループ内で、NDEL、VPACK、HPACKを呼び出すコードも省略します。本コラムページの一番下のリンク(GitHub)を参照してください。Code閉鎖してしまうようですね。GitHubやBitbucketに移行してねというメールを見て、以前自分がそこにコードを置いていたことを思い出しました。サイトを覗いてみると、GitHubと比べてUIが分かりにくかったり、デザインも寂しいなと感じます。WEBサービス」ですね。次回はスコアやゲームクリア判定などを実装したいと思います。第42回目:プチコン3号(BASIC)でさめがめ(Part1)2015-03-01筆者:村田こんにちは。今週はほんのり暖かい日が何日かありました。話は変わり、先日データマイニングの勉強会に参加してきました。統計学を中心にデータ分析の動機付け、思考過程についての講義でとても参考になりました。統計学は初心者なので辛いかなとも思いましたが、平均、分散といった基本統計量からt検定など様々な検定手法について、実習を通じて学ぶことができました。時々このような勉強会に参加すると、直接人から学べるのはやっぱり楽しいなと思います。統計学を業務にどう使っているのかなどの体験談や、他の参加者から出てくる面白いアイデアなどは勉強会ならではという感じです。勉強会に限った話ではありませんが、実習で手を動かすことは大切だと改めて感じました。数時間真剣に話を聞いて理解したつもりでしたが、演習データをもらって実習を始めるといかに理解できていないかが分かります。それでも何とか結果を出そうと足掻く過程で、学んだツールの意味を少しずつ理解し始めます。プログラミングも同じですね。本やネット、公開されたコード、これらをもちろん読むべきですが、それだけでは自分のものに中々なりません。簡単そうに見えるサンプルコードを実際に自分の手で打ち込んでみると、これが意外とうまくいかないんですよね・・・さて、タイトルにあるプチコン3号の紹介から始めます。2014年11月に発売されたニンテンドー3DS用のソフトなのですが、ご存知の方も多いかもしれません。SMILEBASIC(スマイルベーシック)という、いわゆるBASIC言語を使って3DS(DSiは非対応)でプログラミングができちゃうシロモノです。私はVBやVBAは業務で使ったことがありますが、BASICは初めてです。8ビットマイコンでBASICを使っていた方、ベーマガを読んでいた方にとっては、懐かしい(?)ソフトかもしれません。BASICは代表的な手続き型プログラミング言語です。オブジェクトはありませんし、基本スペックのBASICには関数もローカルスコープもありません。命令を順に記述し、繰り返し(FOR,WHILE)や分岐(IF)、あるいはジャンプ(GOTO)など基本的な構造化ステートメントを使ってプログラミングします。型は数値型(浮動小数点)と文字列型があります。BASICのBはBeginnerの意味であり、プログラミング初心者の学習のしやすさを考慮して設計された言語です。BASICが開発された1960年代、他の言語といえば、FORTRAN、COBOL、ALGOL、LISPといった顔ぶれですので、確かに学びやすい言語だったのかもしれません。COBOLやALGOLは宣言が多いですし、FORTRANはシンタックスに制限が多く、LISPはハードよりも理論寄りで取っつきにくさがあります。BASICは中カッコやピリオドなどの記号は無く、宣言も不要、簡単に使える命令群、すぐに実行して結果が分かるインタプリタなど、学習に向いた環境であることが分かります。今回遊ぶプチコン3号は、上記のBASICという学びやすい言語をベースに、さらに快適・楽しい環境だと思います。3DSの下画面にキーボードが表示され、タッチペンでプログラムを入力するのですが、そもそもBASICは記号が少ないですし、アルファベット入力時の予測入力が気持ち良く動くため、意外とコード入力は苦労しません。ワークスペース(スロットと言う)が4つあるため、メインスロットでメインのコードを書きつつも、別スロットでちょっとした実験コードを書くこともできます。実行(RUN)と編集(EDIT)の切り替えもスムーズで、インタプリタ言語らしく、ちょくちょく結果を確認しながらプログラミングできます。ヘルプ機能も使いやすく、ちょっと引数を調べたり、関連機能を調べたりといった操作が簡単にできます。嬉しいのは、スプライト(キャラ画像)や背景画像、BGM、効果音などがあらかじめたくさん用意されていることです。プログラミングを学ぼうとするときの一つの壁として、絵や音楽など楽しい要素になかなか辿り着けないということがあります。いうわけで、これらの要素も初心者の学びやすさを支えるというBASICの思想を受け継いでいるように思います。次はさめがめの紹介です。Gameという英語をローマ字で読んだのが名前の由来です。有名なゲームですが、実は遊んだことがありません。テトリスやオセロのように自分でルールを知っているゲームを実装する方が楽ですが、さめがめもルールは簡単ですので何とかなるでしょう。(BASIC同様、さめがめも様々なアレンジがあるようですが・・・)・升目上に区切られたボードに3種類の駒が敷き詰められている・プレイヤーが一つの駒を選ぶと、隣接した同じ種類の駒が消える(たくさん消すと高得点)・消えた駒の上にあった駒は下に落ちる・縦一列の駒が全て消えると右隣の列が左に移動する・ボード上に消せる駒がなくなったらゲーム終了(一つ残らず全部消せたらボーナス得点)さて、まずは実際に遊んでルールを確かめてみようと思い、Ubuntuのパッケージを試してみました。Ubuntuソフトウェアセンターで探してみると2つほど見つかります。原作に近いシンプルな実装と、もう少し味付けした実装があり、後者のKlicketyというソフトをインストールしました(参考画像1)。ルールを確認するつもりが結構面白くて何度もプレイしちゃいます。興味ある方はお試しあれ:Pではプログラミングを始めましょう。簡単にスプライトを表示させてみます。以下の3行をEDITモードで打ち込みます。0,100,100打ち込んだらDIRECTモード(実行環境)に移行し、RUNと打ち込んでENTERキーorAボタンを押します。このRUN手順のショートカットもあります。EDITモードで編集が終わったら、3DSのスタートボタンを押すだけです。ショートカットを使う方がサクサク開発できます。RUNすると3DSの上画面に勇者っぽいキャラが表示されます。ACLSコマンドは画面初期化コマンドです。管理番号,定義番号と2つの番号を指定し、定義番号のスプライトを管理番号に割り当てて使えるようにします。管理番号は0~511の任意の数字を選びましょう。いうわけで同時に512個までスプライトを表示することができます。定義番号501を他の数字に変えてみると違うキャラが表示されます。TOOLという付属ツールでスプライト画像と定義番号の一覧を確認できますので、どんなスプライトがあるか見てみるといいでしょう。管理番号,X,Y[,Z]となっています。先程割り当てたスプライト管理番号とXY(Z)座標を指定し、スプライトの表示位置を設定します。今回の目標はさめがめのボードマップを作ることです。ボードマップといったらテトリス、オセロと同様、X,Y座標で簡単にアクセスできるように2次元配列を使うのが定番ですね。BASICではDIM命令で配列を作成できます。マージンこのように2次元配列も簡単に作れます。添え字は0から始まります。シングルクォートの後ろはコメントです。雰囲気を出すためにカタカナを使ってみました。最後のマージンは画面中央にボードマップを配置するための値です。プチコンの画面サイズは400x240ピクセルで、一つのスプライトの大きさは16x16ピクセルですので、ボードの左上の座標(MX,MY)は上記計算で求められます。変数名を短くすることがプチコンプログラミングのポイントです。長いと打ち込みが大変です。それにEDIT画面は横幅44文字分しかありません。44文字を超えても次の行に自動的に回り込み表示してくれますが、やはり1ステートメントを44文字に抑える方が読みやすいです。いうわけで、スプライト管理番号の配列はSPRITEBOARDMAPではなくSMAPとし、駒(Piece)の管理配列はPIECEBOARDMAPではなくPMAPとしています。次に、使用する3種類の駒のスプライト定義番号を配列にしておきます。'ダイヤモンドではボードマップを初期化して描画してみましょう。SNEXTNEXTRETURNまずはGOSUB文からです。@ラベルがついたステートメントに処理が飛ぶ、いわゆるGOTOジャンプですが、GOSUBの場合ジャンプ先でRETURN命令を使うと呼び出し元に戻ってこれます。これを上手に使ってサブルーチンっぽくモジュール化をしながらプログラミングができます。プチコンではDEF文で通常のサブルーチンや関数を作ることができます。こちらは次回登場する予定です。次にFOR文を見ていきます。TOの後の終了値を「含む値」までループしますので、特に配列のインデックスと組み合わせる場合は気を付けましょう。RND()は関数です。0〜引数-1の値まで、すなわちRND(3)なら0〜2のどれかが返ります。これを配列のインデックスに使ってランダムなスプライト定義番号を変数Pに設定しています。次にSPOFSの座標指定部分を確認しましょう。X座標は(X*16+MX)と計算しています。プチコンのスプライトは16x16ピクセルですので、スプライト一個分ずつ位置をずらして並べていることになります。2種類のボードデータ(SMAP,PMAP)にスプライト管理番号Sとスプライト定義番号Pを保存しておき、次回以降の駒消しのアルゴリズムに使っていきます。最後のINCは引数の変数を+1する命令です。それと今回、駒を選択するカーソルの初期化コードも追加したいのですが、記載は省略します。本コラムページの一番下のリンク(GitHub)からコードを見ることができますので、興味のある方は参照して下さい。ここまで書いたEDITモードの画面が参考画像2です。スタートボタンでDIRECTモード移行&実行してみます。参考画像3のように3種類の駒が格子状に並べばOKです。次回はカーソルの移動、隣接の駒の削除などを作成したいと思います。ちなみに日経ソフトウエア2015年4月号にプチコン3号の特集記事があります。1〜32015-03-01UbuntuさめがめプチコンEDITプチコンRUN第41回目:Haskellでテトリス(Part9)2015-02-08筆者:村田こんにちは。会社のレクリエーションでボウリングを4ゲームやって、右手のひら(?)が筋肉痛になっています。ところでボウリングの点数計算はプログラミングの練習問題に良さそうですね。それではテトリスのライン消しを実装していきます。揃ったラインを消してそれより上のブロックを1段落とすという処理です。ラインか揃ったかどうか判定する関数を書いてみましょう。linelineのどの部分も空(E)では無ければ消えるラインと判定します。この関数は左辺、右辺の最後の引数が同じなので省略できますね。Part6でカリー化というテクニックを紹介しました。このテクニックを使うとisDeleteLineは以下のように書けます。E)今度はこの逆の関数を作りたいと思います。ラインが揃ったか?ではなく、ラインが揃わず居残るラインか?という関数を作りましょう。isDeleteLineここで出てきた"."は関数を合成する演算子です。c>型の見方にも慣れてきましたか?この演算子は1つ目と2つ目の引数に関数を渡します。b)という型の関数です。見ると2つ目の関数の「戻り値の型(b)」が1つ目の関数の「引数の型(b)」となっており、ここで繋がるイメージです。isDeleteLineの戻り値をnot関数の引数に渡し、一つの関数に合成します。Bool>"."は()を付けずに使用すれば中置演算子ですので、普通はこう書きます。Bool>こうやって関数を"."で合成することで所望の関数を得ることができます。isDeleteLineを反転(not)するのではなく、初めからこう書く方が分かりやすいかもしれません。E)ラインのどこかに空マス(E)があれば居残るというわけです。それでは、ボード全体で居残るラインだけを抜き出し、下にストンと落下させるアルゴリズムを書こうと思います。filterを使ってボードから居残るラインを抜き出します。board次は空っぽのラインを作ります。ボード全体(23行)から居残るラインを除いた部分が空っぽのラインです。それと、分かりにくいのですが、ボードの最下端には描画されないガード領域を作っていたので、その部分を考慮して-1しておきます。[G,E,E,E,E,E,E,E,E,E,E,G]空っぽラインと居残りラインと最下端ガード領域を組み合わせて新しいボードを作ることができます。この新しいボードがライン消し後の状態を表しています。[[G,G,G,G,G,G,G,G,G,G,G,G]]これらをwhere句のローカル関数として定義し、dropLineという関数にまとめてみましょう。[[G,G,G,G,G,G,G,G,G,G,G,G]]次にどのタイミングでdropLineを使うかを考えます。テトリスでラインが消えるタイミングは、自分で操作しているテトリミノが固定化された時です。前回作成のautoDownの中でテトリミノが固定化されています。ここにdropLineを組み込みましょう。}これでテトリミノが固定化されたタイミングで、揃ったラインが削除されるようになりました。ラインが消える前後の状態を参考画像に載せます。ゲームオーバー判定、スコア、落下速度上昇など、まだまだ作りたい部分はありますが、今回のテトリス作成はここまでにしたいと思います。[おわりに]Haskellでテトリスはいかがでしたか?純粋関数型言語でもIOモナドを使ってGUIゲームが作れます。ゲームの中心となるアルゴリズムは純粋関数、外の世界との副作用はIOモナドとはっきり分けられるので、プログラムの整理には関数型言語が向いていると思いました。純粋関数はテストがしやすいです。記事の上ではあまり書きませんでしたが、実際には関数を一つ作る度にghciでテストしており、これがゲームのアルゴリズムを考える際に役に立ちました。それではPart9までお付合い頂きまして有難うございました。