Unityでインディゲーム道!

プログラム、Unity初心者がインディゲーム制作を目指して日々思うことなどを書き綴ります。

"正規表現"が面白い!!

正規表現という字面を見ると、なんだかお堅い感じがして尻込みしていましたが、分かってみるとこんなに便利なモノもないなと感動しました。

 

このページが分かりやすいです。

サルにもわかる正規表現入門 

やっぱ、2000年前後の個人の方が真面目に作ったページは面白いですね。

 

 メタ文字と呼ばれる特殊な記号を使って文字列の形態を書き表すためのもので、パターン検索であったり、表記ブレにも対応できる、というものらしいです。(勉強中)

 例えば、ケータイの番号を検索したいなら、"\d{3}-\d{4}-"\d{4}という風に書き表すと、数字3桁ハイフン数字4桁ハイフン数字4桁で表記された、文字列を引っ張って来ることが出来ると。めっちゃくちゃ便利じゃん!!と驚嘆しました。

 

 ゲームなどで使うとなると、テキストの多いアドベンチャーゲームなのかなぁと初心者考えでは思うのですが、どうなんでしょうか。でも、単純に正規表現で出来ることは実務上でものすごい便利なので、プログラムを勉強し始めて一年越しではありますが、ここらでしっかり見につけようかなーと思います。

 

 一説によると、プログラムを勉強し始める前に正規表現を学んだほうがいい、という主張もあるそうです。ネットでチラチラと"正規表現"という単語を見てはいたんですが、プログラミング以外にも使えるようなのでもっと早くにやっとくべきだったなぁ、と少し反省・・・。

 というわけで、正規表現面白いなぁという日記でした。

 

 

プログラミングにおける様々な記号の読み方!プログラミング入門

意外とスルーされがちな、いろんな記号の読み方についてまとめていきたいと思います。

 

. , ; : [] {}  _ * | () <> ! " '  = + - % / \ ^

 

周知の事実?

 自分を含む、ズブの素人というのは、プログラマの人が初めから分かっているようなことすら分かっていないことが多いです。そして、記号というのは見たまんまな存在ではあるので、読み方はおざなりになってしまったりします。

 

 各々の記号は当然見分けは付きますが、読み方が分からないとモヤモヤとした認識になってしまいます。自分も最初は;(セミコロン):(コロン)の見分けがついてもどっちがどっちだか分からない状態からのスタートでした。名前を知ることによってこそ本質に近づけるのであり、ヴォルデモート卿もそれを恐れたわけです。

 

 はっきりいって、多くのプログラミング初心者向けの本は知ってて当然のことをスルーすることが多いです。でもそれはおかしいですよね、知ってて当然のことを知るために入門書を買うわけですから。ググレば済む?ほー、じゃあこのページでまとめちゃいますぜ。英語での読み方などを参考に編み出した我流になりますので、その点ご了承ください。

 

様々なカッコ

  一番ややこしいカッコについて先にまとめます。

( ) カッコ

 英語ではParenthesesと呼ばれたりもしますが、単純にカッコでいいでしょう。丸カッコでも良いかもしれません。

[ ]  角カッコ

 英語だとSquare bracketと呼ばれます。角カッコと読んでいます。

{ }  ひげカッコ

 Curly brace, Curly bracketと呼ばれます。クネクネしてる、カールしているから、ですね。日本語ならひげカッコで良いと思います。

<>  矢カッコ

 英語だとダイヤモンドらしいです。仰々しすぎるし長いので矢カッコでいいかなーと。genericにおけるタイプ宣言、比較演算子として活躍。

 

少しややこしいやつ

&  アンド(アンパサンド)

 ラテン語でandを意味するetを併せて作った文字なので、アンド読みで。

%  mod(モッド)

 確立を表すパーセンテージではなく、剰余算を表すオペレータです。mod(モッド)と読んでいます。

"  ダブルクォテーション

 ややこしい、というより長い。二重引用符ですが、これも長い。ダブルかなぁ?

'  シングルクォテーション

 シングルで良いのかなぁ。(適当)引用を意味するquoteの頭文字をとってシングルキューとかがいいんですかね?

アスタリスク

 C言語系では様々な役目を任され、混乱の元凶だったりする。(掛け算、ポインタの宣言、間接参照・・・)

|  タテボー、パイプ

 馴染みがない記号代表!比較演算子においてORの意味で使われます。 縦のラインのなので、Vertical Line(バーティカル・ライン)でもありますが、タテボーか海外にならってパイプで良いと思います。

^  カレット

 本来は校正用の記号らしい。C#だと論理排他的 OR 演算子となる。

 

御馴染みの記号

.  ドット

 C#だとクラスのメンバーにアクセスするためのアクセス演算子ですね。

,  コンマ

 表記を区切ったり、引数を区切ったり。

;  セミコロン

 C#だと文の最後に必ずつける大事な記号です。

:  コロン

 継承したクラスを示したり、switch文などのラベルを示したり。

_  アンダーバー

 下にある線だから、アンダーバー。安直。

=  イコール

 プログラミングだとイコールであってイコールじゃない!アサイナー(asigner)とかで呼び分ける必要あるのでは? ==(ダブルイコール)で本来のイコールの意味になる。

+  プラス

 足し算

-  マイナス、ハイフン

 引き算

/  スラッシュ

 割り算

\  バックスラッシュ、あるいはエン

 同じ働きをするが、キーボードや環境などの事情で入れ替わる、ややこしい奴ら。ハテナだとエン・マークで表示されています。

!  ノット(ビックリ)

 否定の意味で使われるからややこしい。もう意味に合わせてNOTと読んでます。

?  ハテナ

 三項演算子などに使われる。ハテナでいいんじゃないでしょうか。

 

まとめ!

 というわけで、一通りまとめてみました。やっぱ名前をきちんと認識していることが大事です。それを踏まえた上で本を読めば、理解の深さが違ってきます。といっても、自分もまだまだ曖昧なところがあるので、精進していきますよ!!

 

 

 

"Cuphead" Unity製の注目インディゲームの初感レビュー!!

いよいよ期待のインディゲーム、Cupheadが発売されました!

 ぶっ続けで3時間ほどプレイし、第一、第二エリアをクリアするかしないかの所までいったので、ひとまずその初感をザックリと書いておきたいと思います。

f:id:miur-us:20171003211414g:plain

以下全ての画像は実際のゲーム画面のスクショです。

いやー、おもしろい!!あと良い意味で難しい! 

 やる前に想定していたゲーム性とは少し違ったデザインでしたが、なぜそうしたかを考察すると、インディだからこその割りきりがあり、そこがプレイ面にも良い影響をあたえていると思いました。そこらへんの考察は後ほど。では順にまとめていきます!

※ゲーム製作者的観点の考察を多く含みます! 

 

どんなゲーム?

ガンシューティング2Dアクションです。主人公であるカップヘッドは悪魔の経営するカジノで魂を賭けたギャンブルに負け、見逃してもらう代わりにカジノへの借金滞納者の魂を回収するように命じられます。(ヘビーだ・・・)

 

 自分はPVを見た感じ、メタルスラッグのようなゲームなのかなー?と思っていました。つまり、ステージを進んでいって、ボスを倒して・・・という構成です。また同じく2Dガンアクションの名作、魂斗羅を思い浮かべた人もいるようです。(実はやったこと無いです、恥ずかしながら。ミニスーファミでやりますぜ!)

f:id:miur-us:20171003211529j:plain

 しかし、実際には通常ステージはあるにせよ、それは決して本筋ではなく、マップ各地に偏在する、ボス(つまり滞納者)を訪ねて直接対決を繰り返していく、という言わば、ボスラッシュ的な構成だったのです。(各ステージに一体なので直後に連続ではありません。が実質的にボス戦が続きます。)

 

 ある意味、予想を裏切られた形ではありますが、しかし、やってみるとその絶妙な難易度で死んで、覚える、そしてボスを打ち負かすという明確かつシンプルな目標と素晴らしい映像、アップテンポなスイングジャズが融合した、かなりの高揚感を味わえるゲームになっています。

  

個性的なゲーム

 まずヴィジュアル、グラフィックから唯一無二の個性を放っていますよね。PVの時点でスゴいな!という印象でしたが、実際やるとさらに良いです。というか、ゲームとしても気が抜けないゲームなので、その映像に気を取られてしまうとクリアできないです。アニメーションに見とれている暇はありません(笑)

 

 1930年代のカトゥーンアニメ調のグラは群を抜いたクオリティです。そこは説明する必要は無いでしょう!問題は、じゃあゲームとしてはどうなのか?ということになります。見た目も大事だけど中身も大事ですから!

 

 インディゲームの場合、どうしても『~っぽい』とか『~の影響うけてるなー』という印象を持つことが良くも悪くも多くあります。繰り返しますが、自分はメタルスラッグっぽい感じなのかな?と想像してました。

 

 しかし、実際のゲームとしてのデザインは、スーパー・マリオブラザーズを初めとした往年の2Dゲームからの影響をもちろん感じさせながらも、Cupheadとしての独自性をきちんと打ち出せている、と感じました。つまりボスを倒すということに重きをおいたゲーム性が、CupHeadらしさを生んでいるのではないか?ということです。

f:id:miur-us:20171003211610j:plain

ドラゴン、そして宙に浮かぶ足場。これはまさしくロックマンのメカドラゴン!

今のところ、一番好きなボスかもしれません。

 

ゲームデザインについて

  いわゆる伝統的な2Dアクションというのは、マリオしかりロックマンしかり、障害物レースのように様々な仕掛け、ザコ敵を避けながら(あるいは倒して)ステージを進み、中ボスを倒していき、各ステージを走破して、最終的にラスボスを倒す、というような構成になっています。つまり、アスレチックを攻略していくようなゲームです。

 

f:id:miur-us:20171003211715j:plain

 しかし、CupHeadというゲームは明らかにボス戦そのものをゲーム体験の主軸においています。Cupheadにはコインがありますが、その数は限られており、通常ステージの中に置かれています。しかし、通常ステージは少なく、1つのエリアにそれぞれ2ステージずつしかありませんでした。(この後のエリアでは増えるかもしれません。)ボス戦ステージの方が数が多いのです。

f:id:miur-us:20171003211854j:plain

  コインは主人公キャラの新たな技を買うために存在しており、それが必要ないのであれば、別に通常ステージをクリアする必要もない、という扱いです。初見プレイでは間違いなく、買っていかないとツライです。チャージショットがお気に入り。(一方、マリオでのコインの扱いは100枚集めれば一機増えるという救済措置的な存在)

 

f:id:miur-us:20171003212357j:plain

  これってなかなか画期的ではないでしょうか?ボスは与えたダメージによって、基本第三段階まで変化します。(上写真はスライムの第二段階)これはよくあるパターンですよね。親切なことにプレイヤーが負けると、どこまで行ったかをリトライ画面で示してくれます。 難しいけど何度も死んで覚えていき、馴れると遅くとも三分以内に倒せる、という短い時間に密度の高いゲーム体験が出来るというデザインになっています。

f:id:miur-us:20171003211805j:plain


 思ったのは、ダークソウルシリーズのような良い意味での死にゲーです。しかし、このゲームには、ダクソにもあった探検要素はほとんどなく、ボス戦の死んで覚えて打開する、という部分を凝縮した、割り切ったゲームであると言えます。

 

 つまり、このゲームは伝統的な2Dアクションではなく、各地に潜む個性豊かなキャラクター達に殴り込みをかけて、その命を奪うという非常にシンプルなゲームです。もちろんボスを倒すのには一筋縄ではいきませんので物足りなさは一切無いです。

 

 あとやりこみ要素がきちんと用意されています。各ステージにはスコアが設定されており、プレイによってFからA(A以上もあるらしいです。)までの成績がもらえます。ただクリアするだけでも大変なのですが、よりやりこみたい人への準備も怠っておりません! 繰り返し遊べる良いゲームだと思います。

 

インディならではの割り切り

  インディゲーム開発というのは、資金面においても、作業人数においても、そう工数を多く取れるものではありません。アニメに関しては、手描きということで手は掛かっているとはいえ、ゲーム全体における一貫したレベルデザインというのは、インディにとって困難な作業です。

 

 なので、ボス戦という通常のゲームであれば、合間の要所にあるモノをゲームのど真ん中に据えることで、レベルデザインの負担を押さえているのではないのかと思います。(おそらくステージ構成よりもボスの攻撃パターンを考えるほうが負担は少ない?)とは言っても、アニメ的演出は各ボスごとに相当凝っているので、手を抜いているというわけではありません。そちらの方を優先した、ということだと思います。

 

  これはリソースの限られたインディならではの割り切りだと思います。しかし、大事なことは、その割り切りがゲーム性そのものにおいても、良い影響を与え、個性を獲得しているということです。

  

Unityやるじゃん!

 間違いなく、Unity製のゲームの中での現時点でのトップクラスの作品に入ると思います。世界観の大切さだけでなく、ゲーム性をも両立させる、という言わばインディにおける最大の課題というものをクリアしているように思えるからです。

 

 もはや、Unityは単に手軽にゲームを作るための汎用ゲームエンジンではなくなりつつあるのではないでしょうか?もちろん、そういう用途でも今後使われるとは思いますが、これだけのゲームが作れるのですから!!

  

 間違いなく多くのUnityユーザーの指針となるゲームだと思います!

 

 

神ゲー"じゃんけん"でプログラミングを考える!!

神ゲーの1つである、ジャンケンをプログラミングで再現することで、プログラミングの最初から終わりまでの一連の流れをステップごとに追って説明していきます。それによりプログラミングとはなんだろうということを考察する内容です。

 ジャンケンは分かりやすいゲームではありますが、それをちゃんとプログラミングするとなると、初心者にとってそれほど簡単ではありません。 

f:id:miur-us:20171001211150g:plain

 完成したプログラムはこんな感じです。コンソールアプリになります。コンソールはとっつきにくいかもしれませんが、馴れてしまえば純粋にコードにだけ集中できるので練習にはふさわしいフォーマットだと思います。

 文字だけとは言え、最低限のジャンケンとしての体裁は整っています。こんなんでも、ちゃんとするにはそれなりに考えないとだめですからね。逆に人間はコレだけのことを『なんとなく』できるわけで人間もなかなかやるじゃん!という気になります。

 

 以前に書いた"プログラミングにおける5つの手順"に沿って、順を追って各工程ごとに書いていくことで、プログラミングという作業の流れというものも考えていきたいと思います。 それも本稿における重要な狙いです。

 

どんな構造なのか?

その前に、まず最初に概略について。

 たかがジャンケンですが、コンソールアプリとはいえ150行ほどになりました。コードは一応載せますが、分かりやすさのためにも先にプログラムの全体図を示したいと思います。 一枚の図にやるべきことをまとめています。 

f:id:miur-us:20171001223107p:plain 

 この図では、おおよその流れと各々の動作とそのための解決策をざっくりと書き表しています。ある意味では、リアルなジャンケンもこの図のような処理をしていることになります。いろんな機能を加えようとすれば、いくらでもできるとは思いますが(例えば、アスキーアートで手の形を表す、など)とりあえず最低限の機能のみのプログラムにしました。では5つの手順に沿って、考えていきたいと思います。

 

要件定義

まずはどんな設計、ルールにするかを考えます。(第一工程)

  • プログラム上のNPCとジャンケン勝負。
  • ベーシックに先に三回勝った方が勝ち。

 シンプルにコレだけになります。せめてwindowsアプリにしてグラフィカルにやった方がいいとは思いますが、プログラミングすることが目的だし、めんどくさいのでコンソール・アプリでの開発としました。

 

ファクタリング

次の第二工程は、"じゃんけん"というものの分析になります。

 ジャンケンは一対一、あるいは複数人が同時に『手の形』を出し合い、その優劣によって勝敗を決めるゲームです。手の形はそれぞれグー、チョキ、パーでいわゆる三すくみの関係(絶対的な強者がいない)になっています。

同じ手が出た場合は、『あいこ』となり、勝敗つかずのやり直しになります。

 

道具の選定 其の一

 分析も終わったので、その結果に基づき、ジャンケンをプログラム上で再現するために必要な道具を考えます。

 

 まずは互いの勝敗数を数えるための変数が必要です。単純な数を数えるだけなのでintタイプが二個あれば十分です。

int playerScore = enemyScore = 0;

 次は重要な『手の形』をどう表現するか?そして、二つの手をどう比較するかの問題です。プログラミングですので、つまりは数字に置き換わるわけですが、数字自体に意味はなく、自動的に振り分けられるような番号であり、プログラム中は変化することは無いので、(定数)つまりenum(列挙体)がちょうど良いということになります。

enum HandForm {

Goo = 1,

Choki = 2,

Pah = 3

}

 という風にかけます。数字を直接的に割り振ることもできますが、そうすると人間側がいちいち「1はなんだっけ?」という風に、その都度思い出さないといけなくなる可読性の低いコードになってしまいます。なので、列挙体を使って言葉に置き換えた方がより分かりやすいコードになります。

 

そして、勝敗の判定方法です。

まずはプレイヤーとNPCの手の形を記憶するオブジェクトが必要です。

HandForm playerHand, enemyHand

このように列挙体変数を作り、そこにそれぞれデータを格納します。

 

 この変数をintにキャスト変換し、その数字を使ってswitch文で勝負の判定を行います。それについては、後ほど説明します。

  

道具の選定 其の二

次は実際の勝負を何回行うか?という問題です。つまりループ処理になります。

 今回のゲームはどちらかが勝つまで続きます。ここで問題なのが、じゃんけんはあいこがあるので、何回勝負が続くかわからないということです。

つまり、どちらかが三回勝つまで勝負は延々と続くことになります。

 

 回数が決まってない、条件がtrueな限り処理が実行される、ということでwhile文がこの場合に適しています。条件は・・・

"どちらの勝ち星も共に2以下の限り"

とします。

while( playScore < 3 && enemyScore < 3 ){

//ジャンケン勝負の処理

}

 上記のように、プレイヤーの勝ちが3より小さい、"かつ" 敵の勝ち星も3より少ない、という風に書き表せます。これはつまり、どちらが3回勝った時点"false"偽となるのでループは終了するということになります。

 

道具の選定 其の三

 ところで、敵の手はどうやって決める?という問題がありました。普通に考えればランダムに出すのがゲームとしては親切でしょう。

 

 C#のライブラリにはRandomクラスがあるので、それをつかって1から3までの乱数を出します。それによって、列挙体から手を選ぶという方式になります。 

Random rnd = new Random();

 

 rndはインスタンスです。手の形に振り分けられた数字は1から3なので、rnd.Next(1, 4)とします。これにより、1から3までの整数がランダムに生成されます。

で、今回入力された整数にしたがって、手の形を返す関数を作りました。それに乱数を入力します。

enemyHand = DecisionHand(rnd.Next(1, 4));

これでランダムにNPCの手を決めたことになります。

 

ロジックを組み上げる

 概略図で示したようなロジックを考える作業です。コードを見ながらの方が説明しやすいので、コードは随時示していきながら、どういう風にロジックを組み上げていくかを考えていきます。コードの冒頭はコチラです。enumを最初に書いてます。

f:id:miur-us:20171001220332p:plain

 

まずは、敵の手を決めます。それについては具体的な内容は先に書いたとおりです。

 

 次にプレイヤーの手を決めます。数字キーを押してもらい、そこから整数を取得して、手を決定するための関数に入力します。

f:id:miur-us:20171001213837p:plain

 それがその関数です。HandForm列挙体を返します。(行数からも分かるとおり、一番下に記述しています。)switch文で入力された数字に基づいて対応した手の形を返す、という構造です。

 ちなみに数字のキーボードに振り分けられた数字は1のキーで49、2のキーで50ということになっているので、数値化する際は48を引かないとダメです。

f:id:miur-us:20171001214237p:plain 

 その後は、実際に手の形を互いが見せ合って勝負するのですが、何もせずに画面上に表示されると、入力した瞬間結果が見えるのでジャンケンらしくなりません。

 『じゃん!けん!ぽん!』というコールとリズムがなによりもジャンケンらしさを生みます。なのでThread.Sleepを使って、リズムを表現しています。コール部を再現するためのものが、上部写真のしたの方のコードになります。

Thread.Sleepを使うにはusing System.Threadが必要です。)

 

f:id:miur-us:20171001214919p:plain

 その後、瞬時に互いの手を表示させますが、色が着いていた方が見やすいので、Console.ForegoudundColor = ConsoleColor.Redという風に文字の色を変えます。

 見やすいインターフェイスというのは、実際のUnityでゲームを作る上でも大事なわけで、こういう細かな気配りの重要性を改めて感じます。

 

そして、いよいよ勝負の判定です。

 二人の場合、その手の組み合わせは9通りになります。なので見易さからいってもswitich文を二つ使って羅列するのがいいのかなと判断しました。

f:id:miur-us:20171001215840p:plain

 ここはもう愚直に書いているという感じで、本当ならもう少し短く書ける工夫があるのかなと思いますが、ひとまず。結果の判定文とスコアの処理をしています。アイコの場合は、スコアは動かさず無効試合となります。

 

 ここまでがいわゆる1ゲームの流れで、while文の中身になります。前述したようにどちらかが三回勝つまではこの流れが延々と繰り返されます。最初に貼ったGIFは自分が上手いことストレート勝ちしていますが、普通はけっこう続きます。

 

ループを抜けたら、最終的な勝者を判断して、勝者を称えます。

f:id:miur-us:20171001220813p:plain

  ループを抜けた時点では、どちらかのスコアが3を超えたという判断しか行っていません。なのでプレイヤーが勝ったかどうかを判断し、勝者の宣言をします。

 

コードを書く!

 実際のプログラミングの5つの手順の最後は、実際にコードを書く!ということになりますが、前項にてコードがあった方が分かりやすいのでそっちで出しちゃってますので、ここは省略します。

 

 要はコードを書くということは、コレまで書いてきたような作業を経て、初めて取り掛かれることであり、つまりはプログラミングという作業全体のうちの一部に過ぎないと言うことです。

 

 これは本ブログでは度々書いていることですが、今回考察したプログラミングはどういう作業で、どんなことを考えないといけないのか?ということは市販の本にはほぼ書かれていないことです。なんでなんですかね?やっぱちゃんと説明するのが大変だからでしょうか?自分を含めた初心者が望む説明ってこういうことだと思うんですが。

 

拡張性について

 というわけで、じゃんけんプログラムを作ってみました。がやろうと思えば、いろんな拡張機能が追加できそうです。

 例えば、今回のNPCは完全ランダムですが、プレイヤーが入力した後に自分の手を決めるという仕組みにすれば絶対勝てないゲームに出来ます。(ずるい!)

 

 それは現実的ではないですが、ほぼ完全ランダムゆえそれに気づくと確立論的な戦術をプレイヤーが立てやすくなってしまうので、場合によっては手の出し方を少し偏らせることでプレイヤーを惑わす、そんなAIも作れるかもしれません。

 

 まずそもそも文字だけじゃつまらん!という場合にはGUIアプリを作った方がいいですし、じゃんけんと言えども試せることはいろいろある!ということになります。

 

まとめ!

 以上でジャンケンを題材にプログラミングの一連の作業、流れを考察してみる!ということをやってみました。やっぱ丁寧にやるとなると、ジャンケンですらかなりの文量になりますね。でも、実際こうした記事を書いてみると、プログラミングをするにはどういう風に考えないといけないかを改めて考えることが出来てよかったです。

 

 

なぜプログラミングが分からなかったのかを振り返る!<入門への道 >

そこそこプログラミング出来るようになった現在から、右も左もわからなかった頃、入門者時代を振り返り、なぜプログラミングは分からないのか?ということについての根本的原因を明らかにするのが本エントリの狙いです。一般的な観点(非プログラマ的観点)からの考察になります。特に具体的なコードは載せませんが、前提とする言語はC#としています。

※原稿用紙15枚分ほどで長いッ!!。

 

暗号だから読めない

 英語を使った人工言語だから、やはり英語力はそれなりに必要なのではないか?というエントリを以前書いたことがあります。 

 単純な単語力以上に、英語をリアルなスピードで使える力(ちから)、タイプしていく力がより大事なのではないか?という仮説です。もちろん英語が出来なくても、プログラミングは出来るんですが、日本人にとっての大きな壁はそこにある気がします。つまり多くの日本人にとって英語は暗号みたいなもんだし、"二重の暗号"になってしまうのではないか?ということです。

 

 で暗号というのはつまり"コード"です、プログラム・コードのコードと同じ"code"
暗号における"コード"というのは、つまり言葉の意味を入れ替える、というタイプの暗号を指します。普段使っている言葉が、違う場所では違う意味で使われているとしたら混乱しますよね?白が黒という意味だったら大変なことになってします。

 しかしプログラミングでは、そんなことがたくさんあるわけです。

 

現実社会(リアル)とは違う意味

 最も代表的な例が、" = "(イコール)でしょう。本来ならば、左辺と右辺は"等しい"という意味で、数学的事実を述べるための記号です。

a = b ;  //現実社会においてはaとbは等しい、という意味だが・・・

 しかし、プログラミングにおいては左辺にある"変数"に対して、左辺の"変数"の値を割り当てる、アサイン(asign)する処理を表す記号と成ります。つまり、ここではbの値をaに割り当てていることになり、aとbは等しい、という意味ではないのです。(処理の結果、等しくはなるわけですが)

 

 もう、この時点でワケわかんないですよね?しかし、そういう決まりなんです。だから納得してください!と言われます。でも、そういう決まりなんですって言われて直ぐ納得できる人は少ないわけで。ここがプログラミング入門者にとってキツイ。


 そしてこの"変数"自体も違う意味ですからね。メモリの一部分に名前をつけて、プログラム内でデータの保存に使えるように確保したものであり、数学における変数とは厳密には違います。プログラミングにおいて"変数"は何かを入れる箱のように例えられますが、そのような性質から単に"変数"という言葉を割り振られただけです。"関数"なども同じです。(コンピュータ上のモノを数学用語を使って抽象化した結果?)

 

 何かに例えることによって、分かりにくいコンピュータ上の機構を人間に分かりやすいようにしよう!ということは多くありますが、そのたとえ話を本気にしすぎてしまうとかえって分かりにくくなってしまうのではないかと思います。

 

 このような普段使われている言葉、記号がプログラミングにおいては違う意味、概念として使われている、というのが混乱の元です。

 自然言語プログラミング言語は近づけようとはしてますが違うものです。(言語によって、どれくらい近いかは変わる。)なので、最初はその意味を理解して覚えないといけないわけですが、ダブル・シンキングが必要になります。2 + 2 = 5なのです。

 

テキストだからこそ想像力が必要

 なので最初はその暗号の読み方、つまり文法を学んでいくことになります。しかし、ただ暗号の意味を分かるようになってもコードは読めません。なぜか?小説を読むように想像力を働かせないといけないからです。

 

 要は表層的な記述を読んだだけではコードを読んだとはいえない、ということです。そのコードは何がしたいのか?それを実現するためにどんな手段を採用しているか?(ソリューション、アルゴリズムを読み取らなければいけません。で他人のコードを読むことが大事とは言え、もちろん最初は文法も理解し切れてないし、そういう背後にある考えや論理を読む、ということも初心者には難しいわけです。

 

 視覚的にプログラミングしていくソフトもあるでしょうが、基本的にはプログラム・コードはテキスト主体で、文字の集合体です。小説は文字だけですが、私達はそれを読むことで、様々な世界や物語を頭の中に思い浮かべることが出来ます。

 

 コードも同様に単なる文字の集まりであっても、確かにコンピュータ上には何らかの世界が展開されており、なんらかの仕事がされています。それは画面に文字を映す、という具体的なものかもしれませんし、大きな仕事を達成するための細かい、人間にはよくわからない仕事かもしれません。それらを想像する力が、最初はないから読めません。

  

 目には見えなくても、確かに存在し、直接的には人間には分かりづらいことであっても、具体的な何らかの処理をしているわけです。そのことを感じ取れるように、想像できるように知識を付けていくことが求められます。プログラミングの文法というのはあくまで書式上のルールでしかなく、逸れも大事ですが、物事の仕組みや論理的な思考の方が重要だと思います。

 

コンピュータと人間のギャップ

 プログラミングというのは、人間が手作業でやろうとすると面倒くさい、時間がかかる、あるいは事実上不可能な仕事をやってもらうための手段です。ザックリ言ってしまえ、コンピュータの計算能力を利用して、現実世界における問題を解決するための機械への命令文を書くことがプログラミングです。

 

 iPhoneSiriみたく話してやってもらうのが理想ですが、現時点では不可能です。しかしプログラミングを始めたばかり、あるいはやったことがない人は、このことが理解できません。なぜならプログラミングという作業と普段コンピュータを使ってやっていることが、結びつかないからです。コンピュータと人間がいろんな意味でどれほどかけ離れた存在か?ということがよく分かってないから、ということになります。

 

 昨今はディープラーニング・ブームで人工知能への熱が高まり、また先述のSiriなどもあり、どんどん人工知能が日常のものになってきつつあります。人口知能によって仕事が奪われる!のもいよいよ現実になるか?みたい話題もよく見聞きするようになりました。

 


 そうした流れの中で見て取れるように、世間的にコンピュータは人間に相対する存在であり、超える存在だと捉えられている、と考えることが出来ます。これは、ある意味では正しいし、しかし過剰評価の部分もあります。

 

 人間の友達、あるいは敵としての存在するコンピュータ、AIは古いSFから現代のアニメまで、よくモチーフにされます。しかし、そのようなレベルの人工知能を実現するには、まだまだ十数年の時間がかかるようです。オバマ元大統領が任期末期にGoogleの中の人からそのようなテーマについて聞いたようなので、実際そうなのでしょう。

 

 人間は何にしても、擬人化して捉えるような部分があります。特に日本人はそこらへんの気質は強いのかもしれません。人工知能を過剰に擬人化して、人間のようにとらえてしまうのは、少なくとも現時点では、コンピュータというものへの理解を妨げます。

 

ではコンピュータとはどんなもので、人間とどう違うのか?

そしてコンピュータを利用するとはどういうことか?

 

 利用に関して、たとえ話を石油による火力発電で考えてみたいと思います。火力発電は石油そのもの自体をエネルギー化するのではなく、石油を燃やして湯を沸かしてタービンを回して発電しています。実はコンピュータによる仕事もこのような転用に近いです。コンピュータの莫大な計算を計算以外(つまり電卓以外)の仕事で使う、ということで、コンピュータが主体的に何かやっているわけではないんです。

 

使う言葉も考え方も違う

 コンピュータの言葉は01であり、考え方は全て計算です。これが全てであり、だとすると、やはり人間とは明らかに異なった存在であると分かります。しかし、最初はこの事実を受け入れるのは以外と難しいです。

 なぜならワタシ達現代人は日々の生活の中で、当たり前にコンピュータを使いこなしている(ように思わされている)からです。あらゆることをスマフォでやっています。なので、プログラミングを学び始めて直面する様々な課題とそうした普段使っているコンピュータとのギャップがとてつもなく大きいものなっています。 

 Hello World!プログラムは普段使っているようなソフトを自分で作ってみたい!という人にとっては退屈すぎますが、データ(この場合は文字列)を"入力"し、ライブラリにある標準関数を使い、"出力"するというプログラミングにおける基本的なことを短いながらに網羅しているプログラムであり、実は凄いプログラムなんですが、どうしても「なんだ!この詰まらないプログラムは!」となってしまいます。

 

 そして、どんなコンピュータであれ全て0と1に構成され、計算によってあらゆるソフトは動いています。あんだけいろいろ出来てんじゃん!?0と1だけであんないろいろ出来るのはおかしくない?みたいに思うのが普通です。それもやはり障壁となるギャップのひとつです。プログラミングを学ぶ上では、まずそうした手軽にコンピュータを使うということと自分でプログラミングするということの落差を呑み込めるようになるのが大事なのかもしれません。

 

 数字で表せるモノであれば、最終的にそれらは0と1だけで表すことができ、コンピュータ上で取り扱うことが出来るわけです。(10進法から2進法への変換)なにより、現代のコンピュータはアホみたいに莫大な計算力があります。一秒間に何十億回という計算が出来ます。コンピュータに暗算勝負を仕掛ける人はいないでしょう。

 

 数字で表すことが出来て、かつ計算することが出来る問題であれば基本的にはコンピュータは解決することが出来るということになります。しかし、そうでなければ、そもそもコンピュータがそうした問題、仕事を理解することすら出来ないのです。

 つまり計算によって答えが出せる問題ならば、0と1で構成された計算式による命令文をコンピュータに実行してもらうことで解決できる、ということになります。

 

プログラミングの目的

 さて、実際のプログラミングにおいて、問題なのは数字で表す、つまりデータ化や、そうしたデータをどう組み合わせて、どう計算するか?を考えるのは人間ということです。"上手いやり方"を人間がコンピュータに教えないといけません。

(そうしなくて済むようになるかも?というのがディープ・ラーニングらしい。)

 

 プログラミングは単にコードを書くだけでなく、そうした領域の問題解決も含むわけです。と同時に、既存のコードにはそうした部分が含まれているのであり、コードを読むということは、コードの背後にある概念を理解する、ということになります。

 つまり、目の前にあるコードというのは結果の産物であり、それに至るまでには様々な工程を経ているということです。だから、コードを読むというのは難しいのです。

 

絵が書けます。音楽が聴けます、作れます。映像を見れます、作れます。

夢のようなことを実現するための処理をコンピュータはしてくれています。

 

 しかし、どう処理するかの命令は人間が書いています。人間のやって欲しいことというのは、コンピュータには基本的に理解しがたいことであり、コンピュータに分かるような形で命令を書かなければいけません。

 

 つまり、人間とコンピュータのギャップを埋める、というのがプログラミングの本質だということになります。なので、そこをまず理解することがプログラミング入門の一歩の気がしますし、それなりの時間が掛かるのかなと思います。

  

じゃあ、どうするべきか?

注意点と対策をまとめます。

 

 プログラム・コードは一種の暗号であり、人間側の都合とコンピュータ側の都合が交じり合っているので、惑わされない。分かりにくくて当然と考える。

 コンピュータ上でことを人間が分かりやすいように何かに例えることが多いが、あくまで例え話なので本気にしない

 コードを読むというのは、表層的に読んでも意味がなく、何がしたいか?どう解決しているか?を理解する必要があり、その分野に文法は直接的には関係ない

 むしろ、理想論的にはコードを書き始めた時点で、コンピュータにどう指示するか?という問題解決の手段は見つかっており、その解決法を文法に沿って翻訳したものがコードと呼ばれるものに過ぎない。(現実的には試行錯誤が必須。)

 hello world!は詰まらないプログラムだけど、そこで諦めない。

 やはり、それなりに時間が必要なので焦らず自分で考える力を付ける。

 文法を覚えきるより、簡単なプログラムを自分で書いていく方がいい。

 コンピュータと人間の違いを理解し、その差を埋めるように"お願い"するということを意識する。

 

あくまで個人的な見解も入ってますが、こんな感じでしょうか。

 

最後に

 という感じで未熟者がいろいろ講釈たれたわけですが、とにかく言いたいのは、

プログラミングおもしろいから、諦めずに頑張ろうぜ!ということ。

まぁ仕事としてやってないからってこともあるのかもしれませんが、でも馴れるまでが大変だったとはいえ、単純にいろいろできるようになるとやっぱ楽しいんですよね。

 

 もちろんプログラミングは手段であり、それを使って何をするかが大事なんですが、それ自体がおもしろいなぁって。

 

 Unityなんかは特にプログラミングできなくてもゲーム作れるよ!という触れ込みではあるんですけど、逆にプログラミングできると何でもできますからね。なんかプログラミングをネガティブなものとして捉えるのはもったいないと思います。

いや、実際まともな動作にするには大変ではあるんですけどね。

 

  

プログラミングってこういうことだよね?というチャート図を描いてみた。

結局、プログラミングとはどういう作業なんだろう?ということを一覧できるような図を描いてみよう、というのは以前から思っていました。

 

プログラミングを一概に説明するのは無理でしょう。でも初心者の頃にそういうのが欲しかったんですよね。大体でいいので、プログラミングという作業の概要をなんとなく掴めるような図です。

 

これをチャート図といっていいのか分かりませんが、一応上から下へと順に工程を説明するような形にはなっているので、いいのかなー?

 

f:id:miur-us:20170913013402p:plain

かなりでかくなっちゃいました。

こんな感じで改善の余地アリアリですが、とりあえず自分が今理解している範囲でのプログラミングという作業の俯瞰図として描けたと思います。

まぁごちゃごちゃしてます。あとオブジェクト指向を念頭に置いた図になっています。

 

これを具体的にゲーム制作に当てはめた図も描くと、より分かりやすくなりますかね。

それも近々。

 

この図を描いて改めて思ったことをまとめると、人間とコンピュータのギャップを如何に埋めるか?という一言に尽きるのかもしれません。

そして、やはりコードを書くというのはプログラミングの中でも一部分に過ぎないということです。もっと物事の本質を掴むチカラが大事なんだなーと。

 

話は変わりますが、コードを書けるの当たり前で、おもしろいゲームを作れるかは別問題である!ということを最近痛感してきています。

コードによる処理も大事なんだけど、やっぱグラフィック面が大事なんですよね。音とか曲はまだ自分で何とか出来るんですけども。 

まぁ、そのためのアセットストアですけども!^_^;

 

最後、少し話がズレましたが、こんな感じで楽しくやってます。

 

 

C#で"FizzBuzz問題"に挑む!Main内で五行達成。

FizzBuzzとは英語圏における言葉遊びで、複数人で数字を順に言っていくわけですが、3の倍数の時は"fizz"5の倍数の時は"buzz"3と5の公倍数の時には"fizzbuzz"と言うのがルールになっています。

FizzBuzz問題とは、この遊びをプログラミングで再現するというものです。その人が本当にプログラミングが出来るかどうかを試すのにちょうどいい"簡単な問題"とされるもので、面接などで出題されるそうです。あるいはいろんな条件を課して、力試しとして取り組むこともあるようです。

 

きっかけはこちらの記事です。"fizzbuzz"という遊びはなんとなく聞いたことがあった気がしますが、プログラミングでそういうものがあるのは初めて知ったので、興味が出ました。ググってみた所、会社内でコレを使ってコンテスト開いた!というブログ記事なんてのもあったので、それを軽く読んでみたり。

 

どちらの記事もPythonでのコードです。ほぼ経験ナシのPythonとは言え、答えのコードを軽くは見てしまってはいるんですが、せっかくなのである条件を付け加えて、C#でこのfizzbuzz問題のコードを書いてみることにしました。

以下はどのような試行錯誤を経て、最終的なコードへと至ったかの思案の推移をまとめたドキュメントであり、なるべくどのように思考したかを順を追って逐一説明する形で文章化してあります。

 

条件とは?

目標としては可読性や見た目を損なわず、できる限り行の少ないコードにする!ことに設定しました。

1から100までの数字をコンソール画面に表示し、ルール通りに特定の数字の時にはfizzやらbuzzを変わりに表示する、というコードです。

いわゆるウィキペディアなどにあるような模範的なコードではなく、C#として使えるものは全部使って出来るかぎり短いコードにするということです。

 

試験ではないので制限時間もないし、コード実行して結果を見つつ改善していく、ということも特に禁止してません。あくまで自分で考えて、コードを書くという試みです。

 

問題の分割!

さて、この問題は大きく分けて2つの個々の問題に分割することが出来ます。

  • 繰り返し処理
  • 条件分岐

まずは1から100までを繰り返し画面に映す、という問題です。

これはループ文、イテレーションによって実現できます。whileかforのどちらかを選ぶか、ということについては、自分はwhile文の中でincrement(インクリメント)すれば短くできるかも?ということでwhile文を選びました。しかし、それは誤りで普通にfor文の方を使った方が簡単に短く出来るので最終版ではforを選択しました。

 

カウント変数、イテレータをそのままConsole.WriteLineを使って繰り返し画面に表示すれば、ひとまず1から100まで画面に映す、という問題は解決できます。

for(int i =1; i <= 100; i++){

Console.WriteLine( i );

}

 

次に解決すべき問題は、数字を判断して、3の倍数の時はfizzを、5の時はbuzzという風に数字の代わりに文字列を映す、という問題です。

  • 3の倍数            Fizz
  • 5の倍数            Buzz
  • 3と5の公倍数   FizzBuzz
  • それ以外の数   カウント変数を出力 

これは素直にif文でそれぞれ条件判断すればいいわけですが、自分の課題はなるべく行数を少なくすることです。なので三項演算子を使うことにしました。

 

三項演算子とは

(条件式) ? trueの時の値 : falseの時の値

というもの場合によってはif文よりシンプルに(あるいは読みづらく)条件判定が書き表わせられるというものです。

 

これを使えばConsole.WriteLine()のカッコ内、引数として三項演算子の式を入れることが出来ます。これによって行数を少なく出来ます。後で説明しますが、これは一回挫折します。しかし、最終的には三項演算子を使うというアイデアを実現することが出来ました。見当違いではなかったということになります。

Console.WriteLine( a > b ? a : b  );

//aの方が大きいならaがそうでないならbが表示される。

というわけで、この問題を解決するための準備は整いました。

 

解決に向けて その1

自分はwhile文を選んでしまったので、まずwhileの条件式の中でincrement出来るかの確認から始めました。一回試したかったというのもあります。

カウント変数は文字数を減らす意味でもnumberの頭文字のnとしました。

int n = 0;

while( n++ < 100){

Console.WriteLine(n);

}

です。

インクリメントは前置き後置きがあって、演算子や比較子を実行する前に増やすか後に増やすか選ぶことが出来ます。

どちらでも問題はないようです。後置きだと100との比較後にincrementが実行され、その後に、内部の処理が実行される、という流れになります。

ちなみにVisual StudioはConsole.ReadLine()などで受付待ちさせないと、処理終了後すぐ画面が閉じてしまうので付け足していますが、除外して行を数えています。

f:id:miur-us:20170822233900p:plain

f:id:miur-us:20170822233815p:plain

というわけでひとまずは最初の問題は解決です。しかし先ほど書いたように、最終的にはwhile文は捨てて、for文を使います。

 

解決に向けて その2

三項演算子です。それぞれの倍数は%(剰余算)を使って表現出来ます。倍数はつまり、その数で割り切れるということであり、%の答えが0になります。

6 % 3 == 0 (6を3で割った余りはゼロ)

 

で最初に思いついたアイディアを形にしたのが、この二行になります。

f:id:miur-us:20170822234305p:plain

キーポイントはConsole.Writeです。これは改行されません。つまり、3,5の倍数の時は続けてbuzzが表示されることで、Console.WriteLine("fizzbuzz")を省略出来ます。これは我ながら良いアイデアだと思いました。つまり3,5の公倍数の時には"FizzBuzz"を出力する!という個別の処理文を書く必要がなくなるので省スペースになります。 

f:id:miur-us:20170822234238p:plain

これを実行すると、Fizzの後ろに3が出力されてしまっていますが、それなりに上手くいっているように思えます。しかし、どうしても3の倍数を出力しない方法が、この時点では思い浮かびませんでした。

 


なので、諦めて素直にif文を使ったコードにすることにしました。

continue文で、その時点でループを強制終了して次のループへ移行します。

f:id:miur-us:20170822234441p:plain

3と5の公倍数を表すのに素直な表現は

if( n % 3== 0 && n % 5 == 0 ){}

になりますが、これは圧縮出来ます。

if( n%3 + n%5 == 0 ){}

どちらも0であるならば、その和も0になるのだから、これでも条件としてはオッケーなはずです。この後は素直にそれぞれの条件を判断し、相応しい数字、文字列を移すようにします。というわけで、完成かと思いましたが、最後の文が三項演算子で省略できると閃きました。上の写真の最後の文は元々は

if( n % 5 == 0 ) Console.WriteLine( "Buzz" );

else Console.WriteLine( n );

でした。

つまり、ここをConsole.WriteLine( n % 5 == 0 ? "buzz" : n.ToString() );としました。

三項演算子の場合は、nをToStringを使って文字列に変換することが求められます。

まさしく最初に思いついたアイデアをそれなりに活かすことが出来て満足でした。

f:id:miur-us:20170822234836p:plain

 

まだ終わってない!!

というわけで、できたー!と満足していたんですが、やはり三項演算子を活用した更に短いコードを諦めきれず、そしてやはりfor文の方が短く済むということで、再度挑戦しました。改めてというか、出来た後一時間後くらいなんですけども。

for ( int i = 1; i <= 100; i++ ){

//判断し、画面に映す処理

}

とりあえず、最初の改善案はfor文に変更することです。こちらの方が分かりやすい!

そして、最初のアイディアの大きな問題はfizzが打ち出された後、そのまま流れでカウント変数がプリントされてしまうことでした。

 

しかし、continueを使って次のループに飛ばす、という技を第二案で使っていたので、これを使えばいけるだろうと考えました。

 

何かもう一行、条件が必要です。fizzの条件を再確認しましょう。3の倍数であり、5の倍数出ない時です。つまり、複数の条件ですから、三項演算子は使えません。そうだ!別にif文使えばいいだけじゃん!と思いつきました。

f:id:miur-us:20170823001506p:plain

三項演算子の間にif文を書いてはいけない、なんていうルールはないです。自分自身の思い込みによって、視野が狭くなっていました。

if( i % 3 == 0 && i % 5 != 0){ Console.WriteLine(); continue;}

fizzは改行されませんが、Console.WriteLine()を実行し改行処理。continue;でイテレーションを飛ばします。これでcontinueも一個に削減。

以上で、FizzBuzz問題のコードをMain内で五行にすることが出来ました。 

 

まとめ!

今回の試みを経験したことで、やはりアイディアは大事だし、一回ダメでもいろいろ試してみると道は開ける、ということを強く実感しました。結果的に最初の三項演算子を使ってコードを圧縮できるかも!?という算段は外れてはいなかったので。

いろいろ勉強になりました! 

応用編として、%を使わない、とかいろんな縛りプレイがあるみたいですね。

そこらへんもいつか挑戦してみたいと思います。

 


と思ったら、記事を書いている間に案を思いつきました。

割り切れる、というのは余りがない、あるいは割った商の小数点以下がないということなので、%(剰余算)を使う代わりに次のように書き表せます。

もし i / 3.0f - i / 3 == 0 なら

3.0fつまりfloatにしないと、商が切り捨てられた整数になってしまいます。逆にi / 3とすることで、小数点以上のみに出来るのでこれを引けば、少数点以下を表せます。

もしiが1の場合、1.0 ÷ 3.0 = 0.33333...で1 ÷ 3は少数点以下切捨てで0でよって、少数点以下が存在するので(0.333... - 0 = 0.333...)1は3の倍数ではない、という風に判断することができます。

iが6の時は、(6.0 ÷ 3.0 = 2) - ( 6÷ 3 ) = 0.0よって、3の倍数である!となります。

あとは先ほどの三項演算子の条件式の中身と取り替えるだけです。

f:id:miur-us:20170822235029p:plain

というわけでFizzBuzz問題の研究を終わります。やっぱ自分で考えて解くと爽快感がありますね。