ひさしぶりだよね!プロプロ人ことプロレタリアプログラマー人間だよ!オッスオッス!元気してた!?🤗

…うん…そっか…ゴメンね😥

アタイ?アタイは元気してたよ!ふへへ!

◆はじめに

こんな記事開いちゃったってことは、キミはプログラミングしてみたいって思ってるのかね?

あはは。なんで?わけわかんない😟

…まぁいいや。じゃあプログラミングの世界に入門してみよっか!ごく一般的な方法で!

(以下、書くの疲れるのでプロプロ人は登場しません。)

◆この入門を完遂したときに何が出来るようになるか

なにも。なにもできるようにはならない。

「お前が何をできるようになるか」は俺の知るところではねぇぜ。お前さん次第です。

楽しいかどうかも、知らん。俺は書いていて楽しいけど。

いま世間で仕事してるプログラマが以下の七面倒くさい作業をしているってことでもない。大抵のプログラマはもっと楽してて、無知で、自分が書いたプログラムがどういう経緯で動いているのかって事を詳しくは知らずに仕事してる。だからガチれば半年でまくれる。

うん。じゃあやっていく。

◆Linux環境を手に入れろ

21世紀を手に入れろ。

プログラミングに入門するんであればWindowsで十分やれる。やれるんだけど、よりミニマムかつ根源的に入門させたいのでLinuxでやっていく。「まったくの未経験に対して好きなようにプログラミング教えてみろ」ってなったらLinuxでやらせると思う。UIなんか使わせない。
WSLを使う理由のひとつは、WSLの廃棄が簡単だから。いらなくなったら環境をポイ捨てして再構築すりゃいい。

最初からLinux使ってるんならそのまま進め。WindowsならWSLってのをインストールしろ。Macは知らん。

WSLのインストールの仕方は以下参照。多少面倒だけど頑張れ。「Ubuntu 20.04 LTS」的なものを実行できれば勝ち。Ubuntuを動かせ。

Windows 10 用 Windows Subsystem for Linux のインストール ガイド

よくわからんなら、この記事のコメントに書いていけ。
パスワードは、とりあえずユーザー名にしとくのが無難。クソほど忘れるし、WSLでプログラミングの勉強するだけであればセキュリティを考える必要はない。

◆ソースをつくる

ソースって言うのは、お料理にかけておいしいやつのこと。それを作ると美味しいのでパソコンが動いてくれる。

▼ターミナルさん

「ターミナル」というなにがしにコマンドを打ち込むことによってPC
の操作をしていく。WindowsのWSLであれば、起動したそれがターミナル。もとからLinuxとか使っててターミナルの開き方がわからないならググれ。

Windows Terminalってのもあってカッコいいから、それを使ってUbuntuやら操作するのもおかし。

Windows Terminal

▼ファイルつくる

Linuxのターミナルを開くことに成功したならば、まずフォルダ作って、そこにプログラムのソースとなるファイルを作りたい気持ちが腹の底から湧いてくるはずだ。

ターミナルでは、マウスを一切使わずにPCを操作していく。フォルダを作ったりファイルを作ったりフォルダを開いたりってのも、マウスなし。全部コマンドを打ち込むことにより達成せねばならない。また、フォルダのことをLinuxではディレクトリって呼ぶ。

「mkdir」コマンドで「tinpo」っていう名前のディレクトリをつくる。mkdirはすなわちmake directoryの略。tinpoはちんぽ。

今後「$」が先頭にきてるアレがあったら、コマンドラインでの操作だと思ってくれ。「$ 」は入力せんでいい。下の例でいうと「mkdir」から入力しる。入力し終わったらEnter押せ。

$ mkdir tinpo

「cd」コマンドにより「tinpo」フォルダ内に移動する。change directory。

$ cd tinpo

「touch」コマンドで「prac1.c」ってファイル作成。touchがどういう意味なのかは知らん。

$ touch prac1.c

「tinpo」っていうフォルダ名とか「prac1.c」ってファイル名は別に何でも構わんよ。prac1ってのはpracticeのprac。

「ls」ってコマンドを叩けば、フォルダの中身を一覧できます。一個上のフォルダにあがりたいなら「cd ..」ってコマンドを叩け。

▼viとかいうエディタ

作ったファイルにソースコードを書き込もう。

ターミナルには入力補完機能がありまして、いちいちファイル名とか全部入力することもない。下記コマンドを例にとると、viって入力して半角スペースいれたら、Tabキーを押せば入力補完してくれる。Tabをポチポチと押し続けてお目当てのファイルを出せ。あと、「p」って入力した後にTabキー押せばファイルの先頭が「p」から始まるファイルを出してくれるよ。

$ vi prac1.c

「vi」コマンドはvimっていうテキストエディタを起動してくれる。なかなか操作がむずいので気を付ける事。慣れればいいものだらしいんだけど研鑽が必要。

Ubuntuにデフォで入っていると思われる「nano」っていうエディタがあって、そっちのほうが実際のところ操作が簡単ではある。「nano prac1.c」で起動できる。でも、vim使って苦しもう。

vimの起動に成功しただろうか。成功したっぽいなら「i」キーでINSERTモードに入るがよいぞ。vimではINSERTモードに入らねば書き込みはできない。あと「i」って入力しようとして全角で「い」とか入れてもINSERTモードには入れないんで、入力しちゃった文字をBackspaceで全部消してから半角モードにしてね。

INSERTモードに入ったら、以下を書きこめ。Ctrlキーを押すと迷宮入りする羽目になるかもしれないので気を付けろ。全部手で打ち込め。今はとりあえずコピペに頼らないで頑張れ。

#include <stdio.h>

int main(int argc, char *args[])
{
    printf("Hello, world!\n");
    return 0;
}

入力し終えたら「Ctrl + c」でINSERTモードを離れる。そいで「:wq」と入力し、Enterキーで保存して終了する。

ドザであれば「Ctrl + c」でコピーを連想するだろうけども、ターミナルの操作では「終了」を連想した方が良い。vimではCtrl + cでINSERTモードを終了だし、ターミナルから実行したコマンドを終わらせたい時もCtrl + c。困ったらCtrl + c。

vimの操作むずいかもしれないけど、vimを使わずにターミナルでテキストファイル編集するのはもっとつらたん。catやらsedのコマンド使わねばならん。vimに感謝しよう。

◆の~みそこねこね

コンパイラ。

人間に読めるプログラムを「prac1.c」として保存したわけだが、それをパソコンの読めるプログラムに変換したらねば動かすことは叶わない。

「ソース」「ソースコード」って言ってたのは、つまり「パソコンを動かすことのできるプログラム」がプログラムであって、その元ネタになるからソースコードと言う。

どうやってソースコードをプログラムに変換するのか?そりゃ変換用のプログラムを書いてやらねばならない。ソースコードを変換する用のプログラムのソースコードを書いて、それを変換する用のプログラムを変換する用の…みたいな話になってくるから、もうあるやつを使おう。

余談:セルフホスティング – Wikipedia

変換する用のプログラムは、大まかに「コンパイラ」と呼称される。

コンパイラはインターネットに落ちている。それを取得するには「apt-get」というプログラムを使う。まずapt-getのバージョンアップをしよう。

$ sudo apt-get update

sudo(super do)っていうのを、実行したいコマンドの前に入れている。このsudoによって「管理者権限で実行」的なアレになる。パスワードを聞かれるかもしれないので、入力してEnterキーを押せ。

あと「Yes/No」を聞かれたら「yes」あるいは「y」って入力してEnterしておけ。あるいは[Y/n]とか書いてあったら、Enter押すだけでYesを選んでくれる。大文字があればそっちがデフォになるんだな。

暫く待ってアプデが終わったらば、以下のコマンドを叩く。

$ sudo apt install gcc

この「gcc」ってやつがGNUコンパイラコレクションっつって、色々なコンパイラ入ってるプログラムです。ありがとう、GCCを作ってくれたどこかの誰かさん達。

◆コンパイる

コンパイラ使ってソースコードをコンパイルする。「コンパイルする」ことを「コンパイる」っていう。嘘。言わない。

以下のコマンドを叩く。

$ gcc -o prac1 prac1.c

「-o prac1」ってのは、「『prac1』っていうファイル名でコンパイル結果を保存してね」っていう意味。べつにprac1じゃのうて「hoge」でも「fuga」でもなんでもいい。好きにしろ。この「-o prac1」を省略した場合、「a.out」っていうファイル名として自動的にコンパイル結果が保存される。

とくに何も表示されなかったらコンパイルに成功している。「ls」コマンドを叩けば「prac1」というファイルが生成されていることが確認できることと思う。なんらかの真っ赤なエラーが表示されたんであれば、prac1.cに書いてある内容が壊れているはず。見比べろ。あるいはファイル名が間違ってるとか。

prac1.cを捨てたくなったら「rm prac1.c」ってコマンド打てば削除される。rmはremoveって意味。

◆動かす

やっと動かせるぜ。イェイイェイ。

$ ./prac1

これでコンパイル結果を実行できる。「./」の「.」は、いまいるディレクトリ。「./」が無いとPCにインストールされているプログラムを探しに行ってしまって動かないので「./prac1」って指定してあげてね。

動かしたら、ターミナルに「Hello, world!」って表示されたことと思う。よかったね。

◆prac1.cの解説

なんのこっちゃわからんだろうから、解説を試みる。

▼#include <stdio.h>

実行したら「Hello, world!」って文字列がターミナルに出てきたじゃん?その「ターミナルに出す」っていう動作ですら、本来は自分でコーディングせねばならんのですね実は。

でもそれって辛いから、C言語の標準ライブラリ集から拝借してきたわけです。他のプログラムを、このコードにincludeしたわけです。stdio.hってのが標準入出力、すなわち「Standard I/O」を意味している。えっちだ…w

この「stdio.h」ってのが無いと、prac1.cにあった「printf」っていう機能が動かなくなっちゃう。printfはstdio.hにいる。printfのコードが気になるんならこの辺を見てみてね。

▼int main(int argc, char *args[])

ソースコードをファイルに書いてプログラムにして実行しようと思ったとき、「どっからプログラムを開始すればいいのか問題」が発生する。

だから「mainという名前の関数から開始しよう」っていう話になった。

関数って言うのは、処理のまとまりのこと。あとで説明する。

関数であるところのmainの右隣にある丸カッコと、その内側に書かれているもろもろは、引数とよばれる奴ら。とりあえず考えなくていい。あとで使う。

細かく説明しちゃうと、例えばgccを動かすときに「gcc -o prac1 prac1.c」とか書いてたじゃん?その「-o prac1」やら「prac1.c」っていう情報を受け取るための口がプログラム上に必要となるとは思わんかね。mainという関数は特別で、argcとかargsって形でそれら「コマンドライン引数」を受け取ることができますです。argcはコマンドライン引数の総数。argsは「配列」なるデータ構造であり、なんかかんかすればコマンドライン引数の内容を読める。

このmainはエントリーポイントとも呼ばれたりする。実行開始の目印みたいな意味合い。

▼printf(“Hello, world!\n”)

printfの後ろに丸カッコがあるでしょう。つまりprintfも「関数」と呼ばれるアレであるような気がする。

でもコレは…mainとは何やら違うな。関数を作っているっていうか、関数を使っているように見える。うん。

そして、丸カッコの中に書かれている「”Hello, world!\n”」は「引数」だったな確か。

以上を整理するに、printfという関数に引数を渡すと、その渡した引数の内容がターミナルに表示されるような気がする。

…ん。いや、「”」と「\n」は無視されておるな。「”」で囲まれた内側を表示しているんだろうけど、「\n」って何だろ。文字列の終端を意味しているんだろうか?vimで「\n」を削除してコンパイルして実行してみるか。

…なるほど。この「\n」ってやつは「改行」を表しているらしいな。なるほどなるほど…。

~完~

▼return 0

「ここで関数がおわるんだよ」っていうマーク。今回のmainでは「0」をreturnしている。

0ってのは、mainという関数が「int」を返却せねばならないから0。

おまえ「intってなに?」

俺「さぁ…」

▼;

セミコロンを行末に置くと、そこまでで1文あつかいになる。セミコロンが無いと、コンパイラくんが「1文」を解釈できなくなって死んじゃう。

◆prac2 ~俺に挨拶しろ~

お辞儀をしろこのポッター野郎。

▼前回までのたますじ

prac1で Hello, world! 処女を散らしたお前であるが、そのHello, world!を「Hello, 警報!」とか「Hello, project!」に書き換えてたりして遊んでたら3年が経過していた。

※「vimでファイルを開く」「INSERTモードにする」「書き換える」「Ctrl + cでINSERTモードから離脱する」「:wqからのEnterで保存」「gccでコンパイる」「実行する」っていうルーチンを何回か練習してもいいかもね。

▼scanf

printfは「標準出力」。かたやstdio.hは「標準入出力」。つまり「標準入力」が存在しているような気がする。いや存在しているに決まってる。

というので「scanf」と言うものを使えばいい。

▼コピー、編集

prac1.cからprac2.cを生み出し、そのprac2.cを編集しよう。

tinpoディレクトリを開いているだろうか。そうでもないなら「cd」コマンドで移動してこい。tinpoディレクトリを開いたら以下のコマンドでprac1.cをprac2.cという名前でコピーする。

$ cp prac1.c prac2.c

そいで、「vi prac2.c」で編集しよう。以下を写経。

#include <stdio.h>

int main(int argc, char *args[])
{
    char name[255];
    scanf("%s", name);
    printf("Hello, %s!\n", name);
    return 0;
}

何だこのコードは。何が起きていやがる。理解できん。もう嫌だ。とにかく「gcc prac2.c」でコンパイルし「./a.out」で実行してみよう。

…なんだ?何が起きた。どういう状況だ。とりあえず「サイババ」って入力してEnterキーを押してみるか。

サイババ
Hello, サイババ!

何だコイツ。俺は…俺はサイババじゃない!違う!俺は違う!ああああああああ!!!!!もう嫌だあああああああああ!!!!!!!!!!!!!!11111

▼prac2.cの解説

・char name[255]

「変数」なるものを作っている。変数には値を記憶させておくことが出来る。値を記憶させておいて後で使うためのホニャララが変数。

例えばchar name[255]という書き方によって宣言できる変数は

  • nameという名前
  • charを255個格納できる

って感じ。

charが意味するものは「文字」。まとめると「文字を255個格納できるnameっていう名前の変数を宣言した」ことになる。良く分からんけど。

ちなみに、1文字のcharが格納できればいいってんなら

$ char name;

でいい。

・scanf(“%s”, name);

でたわね標準入力。

さっき宣言したnameっていう変数に対して、この書き方でなにやら読み込める。「%s」ってのが「string」を意味していて、stringは「文字列」を意味する。変数nameは文字を255個格納できるんで、つまり255文字までの文字列を格納できるんじゃあるまいか?

このscanfという関数は、入力を待ち受けてくれる機能を持ってる。お前がターミナルに何かを入力してEnterキーを押すまで待ってくれる。

このscanfって関数には、実用上色々と問題があったりする。取り敢えず触れないけど。

・printf(“Hello, %s!\n”, name);

また「%s」がおるな。

それよりまず、関数printfが2つの引数を受け取っているようだな。さっきのscanfもだけど。みたところ、関数には複数の値を渡すことが出来るらしい。カンマで区切れば。

引数を何個うけとれるのかってのは関数によってそれぞれ定められているし、引数の順番を守らないとちゃんと動いてくれない。printfは1~2個の引数を受け取ることができるように観察される。

納得しなくていい。いずれ腑に落ちる。

ほいでな?さっき「サイババ」って入力したじゃん?それが「Hello, サイババ!」と言う風にprintfされたわな。

printfの「%s」に変数nameの中身が設定されつつ出力されているっぽいね。楽しいね。(説明放棄)

printfもscanfもだけど、末尾の「f」は「format」を意味している。scanfでは「%s」に値を受け取ってnameに横流ししているし、printfでは%sに変数の値をあてがっている。それがformat。理解できなくてもいい。

▼改善しる

prac2って何か実感わかないし面白くないってのもわかるんだが、段階踏まないとお前みたいなポンコツブレインクソビッチポケモンはすぐに置いて行かれてメソメソ泣きながら失禁しちゃうし仕方ない。もうちょいお付き合い願いたい。

prac2というプログラムを、ユーザーにとって分かりやすいものにしてみようよ折角だし。

ターミナルにカーソルがある状態で「↑」を入力すると、コマンドの履歴を呼び出せる。prac2.cを編集してコンパイラ通して実行してっていうのは既に一度叩いたコマンドだから、そっから引き出して動かすのが楽ちんちん。

どうやったら分かりやすくなるのかって、printfで補強してやりゃいいんだよ。以下を写経しろ。

#include <stdio.h>

int main(int argc, char *args[])
{
    char name[255];
    printf("名前を入力してください:");
    scanf("%s", name);
    printf("へぇ、あんたも%sって言うんだ。\n", name);
    return 0;
}

なんでコピペでなく写経させるのかって、「動かない経験」を積ませるためです。動かないコードを動くようにできると理解が少し深まる。

どや?

ちょっとした事だけど、それっぽくなるもんでしょう?なにかワクワクしたものになるよな。

◆prac3 ~おさかな~

prac2を作ったお前はプログラムマスターになった。プログラムマスターになったので、以下のプログラムを開発した。

※prac3.c とゆう ふぁいるめいで しゃきょう して 、こんぱいらに くわせたのち、ケツをぶったたいて うごかして みよう!

#include <stdio.h>

int main(int argc, char *args[])
{
        printf("さかな博士「あなたの好きな魚はなんですか?」\n");

        char fish[255];
        scanf("%s", fish);

        printf("さかな博士「%s…ですか。私はサバのほうが美味しいと思いますがね。では。」\n", fish);

        return 0;
}

動かしてみればわかるんだが、なんとも素敵なプログラムである。

だが、重大な欠陥がある。それすなわち「サバ」と入力されたときだ。サバが好きだって言ってんのに、さかな博士は頓珍漢なことを言い始める。

これはよくない。

▼if else

もしもAであればBという処理をする。AでなければCという処理をする。

的な場合分けできればいいんじゃあるまいか。

それが「if文」というやつです。

#include <stdio.h>
#include <string.h>

int main(int argc, char *args[])
{
        printf("さかな博士「あなたの好きな魚はなんですか?」\n");

        char fish[255];
        scanf("%s", fish);

        if (strcmp(fish, "サバ") == 0) {
                printf("さかな博士「結婚してください。」\n");
        } else if (strcmp(fish, "うなぎ") == 0) {
                printf("うなぎ将軍「よくも俺の仲間を!!」\n");
        } else {
                printf("さかな博士「%s…ですか。私はサバのほうが美味しいと思いますがね。では。」\n", fish);
        }

        return 0;
}

おまえ「いや、これ何が起きてるかわからん。」

おれ「うん。じゃあ説明するか。」

おまえ「いや、やだこれ。むずかしい。」

おれ「は。ん?うん。そうか。でもこれ複雑なわけではなくて、簡単な部品の集まりなん

おまえ「やだ」

おれ「あのっ」

おまえ「やだ!!」

おれ「よしじゃあケツだせ!!!!」

◆C#

※この段落は別に読まんでよいし、理解できなくていい。

じゃあオラオラ来いよオラァ!

というので、C言語だと今後もなかなか大変。コマンドラインでやるのも大変。カスタマイズすればもうちょい楽になるが。

だから、昔の人はターミナルでの開発をやめた。C言語も具体的過ぎて大変っちゃ大変なので別のプログラミング言語を使うようになった。

世の中にプログラミング言語はいろいろある。あるんだけど今日のところはC#でやってみる。RustかTypeScriptかで迷ったけど。

今後、WSLじゃない場所で開発をする。WSLgってのでGUIを使った開発が可能なんだけど、まだプレビュー過ぎて使えない。一般的に使えるようなアレになったらこのブログは書き換えとく。

WSLという仮想環境上での開発じゃなくなるわけだから、環境を捨てたり作り直したりってのはやりづらくはなる。あんま気にせんでもよいけど。

◆導入

環境構築なんか全部コマンドラインでやるのがいいんです。

▼Windows

wingetってものが便利なのでそれを利用しつつインストールを進める。でもwingetがこれまたプレビュー版。まいっちゃうよね。

winget-cli | github

上のページで「appxbundle」って拡張子のファイルを見つけてダウンロードして実行しやがれ。ほいだら「winget」コマンドが使えるようになる。

  • Windowsキーを押して
  • 「powershell」って入力してPowerShell起動

PowerShellを起動出来たら以下のやつを入力していけ。

・.NET SDK取得

プログラム言語によっては、それ用の開発キットが提供されていることがある。それ使えば開発が楽ちんぽ。プログラム開発キットは、SDK(Software Development Kit)って呼ばれている。

winget install dotnet

・VS Codeっていう開発ツール取得

VS Codeはエディタである。さっきはvimを使ってたけどVS Codeを使う。

winget install vscode

※インストール完了したら勝手に起動しちゃうかもしれんけど、気にすんな。

・VS Codeをアレする

拡張機能を入れようぞ。VS Codeをインストールしたことによって「code」っていうコマンドを使えるようになりました。「–install-extension」で拡張機能をインストールできる。

だから「日本語化パック」「C#の拡張機能パック」入れる。

code --install-extension ms-ceintl.vscode-language-pack-ja
code --install-extension ms-dotnettools.csharp

終わったら日本語を有効化するためにウィンドウを閉じてもっかいVS Codeを起動しろ。

▼MacとLinux

Visual Studio Codeをインストールし、.NET 6 PreviewのSDKをインストールしろ。難しいこっちゃない。がんばれ。

◆プロジェクト作成

Homeに行く

cd ~

フォルダ作って移動

mkdir programs
cd programs

「cd p」って入れた後にTabキーで入力補完使えば「cd .\programs\prac1\」的に移動できる。

programsフォルダに移動出来たら、さっきのSDKで手に入れた「dotnet」コマンドでプロジェクトの下地を作る。

dotnet new console -o Prac4
  • new:新規プロジェクトを作る
  • console:コンソールアプリを作る
  • -o Prac4:「Prac4」って名前で作る

今作ったプロジェクトをVS Codeで開く。

code Prac4

これで準備完了。

◆準備完了

あーっはっはっはっは!!!生きてるぅ^~!帰ってこれた^~!

とはいえ、もうここまで付き合ったやつもおらんだろう。俺でも無理だと思う。だけど構わない。もう戻れない。いけるとこまで行くしかない。死のうかな。

そして、VS Codeで「Ctrl + @」と入力すれば、同じウィンドウでターミナルが起動してくれます。

Ctrl + @でターミナル開いたら以下のコマンドでプログラムを動かすことができる。

dotnet run

runによりHello World!と表示されたことと思う。

◆prac4

VS Codeを開いた状態で「Ctrl + p」と入力し「program」と入力し、Enterして「Program.cs」を開く。

そしたらもう編集しようよ。VS Codeに以下を写経。

using System;

namespace Prac4
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("さかな博士「あなたの好きな魚はなんですか?」");
            string fish = Console.ReadLine();
            
            if (fish == "サバ")
            {
                Console.WriteLine("さかな博士「結婚してください。」");
            } 
            else if (fish == "うなぎ") 
            {
                Console.WriteLine("うなぎ将軍「よくも俺の仲間を!!」");
            }
            else
            {
                Console.WriteLine(@$"さかな博士「{fish}…ですか。私はサバのほうが美味しいと思いますがね。では。」");
            }
        }
    }
}

本当はコードの入力補完とかエラー検出が効くはずなんだけど、Omnishapっていうやつがどうもうまく動かん。前は動いていたと思ったんだが。

「Ctrl + s」で保存をしたら、Ctrl + @でターミナル開いて「dotnet run」しろ。

どういうコードなのかって?説明してやろうじゃないか。

▼Console

Console.WriteLineで標準出力に一行文字列を表示することができる。Cの時にあった末尾の「\n」が無くなってんね。それはWriteLineが初めから「行」を出力してくれるから。Console.Writeっていう、Lineがついてないやつを使えばprintfと同じ動きをさせることもできる。

▼代入

string fish = Console.ReadLine();

これは何なんだろうか。

動きを見るに、標準入力から受け取った文字をfishという変数に格納しているらしい。

というので、ここでは「代入」という操作をしている。代入はさっき使ってたC言語にもある操作。C#だけの機能ではなく、大抵のプログラミング言語に用意されているもの。代入。

代入は、変数に「なんかの値」を記録させることができる。単に右辺(=マークの右側)を左辺に保存しているってだけ。今回の例では「=」の記号により、「Console.ReadLine()」を変数fishに格納している。

Console.ReadLineは、右側にカッコがついているので関数だな。

関数ってものについてまだ俺は説明をちゃんとしていないわけだが、この記事では最後まで説明してないです。ノリでやれ。

Console.ReadLine()により標準入力から文字を受け取りつつ「fish」という変数に格納しておるんだな。

変数fishはつまり文字列を代入できる変数なんだろう。C言語では文字を意味する「char」の集まりが文字列だったんだけど、C#ではstringっていうので文字列を扱えるみたい。

▼if

大抵のプログラミング言語では、if文というものにより条件分岐を記述することができる。

ifに続く丸かっこに条件を書くことにより、それに適合したときだけブロック内の処理を動かすことができる。

ブロックってのは「{}」で囲まれた範囲のこと。「fish」という変数が「サバ」だった場合は結婚を申し込むコードになっている。

「else」ってのはifの条件にあてはまらなかったときに通る処理を書く場所。「else if」はそれまでの条件に当てはまらず、かつ右側の丸カッコ内に適合したやつ。else ifは何個でもつなげることができる。

理解しろ。

あと、ifブロックの中にさらにifを入れ子で書くこともできるよ。

▼returnなくね?

省略した。

書いてもいいけど「return 0;」だとダメ。Mainが「void」ってのを返すように指定されているから。voidであるばあいは「return;」みたいに無を返却せねばならない。

▼文字列にfish埋め込んでね?

よく気付くな。えらいぞ。ちんちんよしよししてあげよう。

ダブルコーテーション「””」の前に「$」ってついてるじゃん?それを使えば文字列の中に「{fish}」的に変数を埋め込むことができる。便利ね。$はC#固有。C言語には無い機能だよ。

◆prac5 ~ループ~

相手に魚を選ばせるっていうか「サバ」と答えるまで粘りたい。サバを食わせたい。新しいプロジェクト作ろう。

Ctrl + @でターミナル開いて以下コマンドを叩く。

cd ..
dotnet new console -o Prac5
code Prac5 -r

「code Prac5 -r」の「-r」は、ウィンドウをreuseすることを意味している。「-r」がなければ新しいウィンドウとして開く。

using System;

namespace Prac5
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("さかな博士「あなたの好きな魚はなんですか?」");

            string fish = Console.ReadLine();
            while (fish != "サバ")
            {
                Console.WriteLine("さかな博士「ゑ?すみません。もう一度お願いします。」");
                fish = Console.ReadLine();
            }

            Console.WriteLine("さかな博士「やはり魚と言ったらサバに限りますよね。」");
        }
    }
}

写経が済んだらCtrl + @でターミナル開いて「dotnet run」しろ。

▼ループ処理

お前が「サバ」と答えるまで無限に聞き返してくるプログラムになったはずだ。

なんでかって?

それはね?お前を食べるためさ!!!!!🐺🐺🐺🐺

大抵のプログラム言語にはループ(反復処理)という仕組みが用意されていて、繰り返すことができる。どんなプログラミング言語であっても「条件分岐(ifのこと)」と「反復処理」が九分九厘存在している。

C#のループ処理はまずwhile、そしてdo-whileforforeach。いっぱいあるんだけど、とりあえずどんなループであってもwhileで書けるからそれで攻めてみる。

dotnet runで走らせたプログラムを強制的に終わらせたい場合は、ターミナルで「Ctrl + c」を押すよろし。

▼while

while (fish != "サバ")

「fishがサバでなければもう一度聞きなおす」って動作をさせたかった気がしていて、動きをみるに上記while文がその役目を果たせているらしい。

fishがサバと等しい時はループの外に出てしまうわけだから、つまり「!=」ってのは「等しくない」を表現しているっぽい。

であれば、whileの右カッコの内側に書かれているのは、ifでもあったように「条件」みたいですね。条件に合致している間じゅうループし続ける能力がwhileであるはずだ。

そいで、whileのブロックの中でfishを書き換えているわけで、fishの中身が「サバ」になった瞬間にこのループから脱出することができるのかな。

神「いいえ。違います。whileのブロックに書かれた処理は上から下まで一通り流れ、最後の行を処理した後に条件判定が走ります。fishがサバになった瞬間ではありません。」

そうなんだ…ありがとう!神!

◆prac5.1 ~別解~

同じ処理を違う書き方してみる。

using System;

namespace Prac5
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("さかな博士「あなたの好きな魚はなんですか?」");

            while (true)
            {
                string fish = Console.ReadLine();
                if (fish == "サバ")
                {
                    break;
                }
                
                Console.WriteLine("さかな博士「ゑ?すみません。もう一度お願いします。」");
            }

            Console.WriteLine("さかな博士「やはり魚と言ったらサバに限りますよね。」");
        }
    }
}

▼true / false

trueはC#の予約語であって、「真」を意味している。真の逆、「偽」を意味するのが「false」。

ifやらwhileに書いていた条件式は、その式を評価した結果がtrueになるかfalseになるかを見ていたんです。

if (true)と書けばそのifブロックの中に必ず入るし、while (true)と書けば、そのブロックは無限ループになる。

無限ループがあるプログラムは即ち終わらないプログラムだ。困る。

でもこの例ではbreakしているから安心なんですね。

▼break

自分がいまいるループを強制終了してブロックの外に脱出できる。それだけ。ifでfishの判定をし、サバであったらbreakしているな。

一行で書いてもいいよ。

if (fish == "サバ") break;

◆prac5.2 ~別解~

do-whileループは以下の感じでかける。

using System;

namespace Prac5
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("さかな博士「あなたの好きな魚はなんですか?」");

            string fish = "";
            do
            {
                fish = Console.ReadLine();
                if (fish != "サバ")
                {
                    Console.WriteLine("さかな博士「ゑ?すみません。もう一度お願いします。」");
                }
            }
            while(fish != "サバ");

            Console.WriteLine("さかな博士「やはり魚と言ったらサバに限りますよね。」");
        }
    }
}

whileの条件を下に書くことができるんですね。

今見たように、同じ動作をするプログラムであっても別解がなんだかんだある。このdo-whileの例はなんか前のふたつより汚いコードに思えるな。ループの書き方を変えたほうがいい。あるいは、セリフの出し方を変えてあげたりすればdo-whileのほうが適していたりもするのだ。

◆prac6 ~ゲーム開発~

飽きたからゲームを作ろうゲームを。

とはいえ、使える材料がほぼないので簡素なゲームになってしまう。でも何でか割と遊べるゲームだから作ってみろ。

  1. コンピュータくんが 0 ~ 100 の間で好きな数字を決める。
  2. プレイヤーが適当な数値を標準入力に食わせる。
  3. 間違っていた場合、その入力した数値が上か下かを教えてくれる。
  4. 正解するまでループ。

んん…作れる気がしない…

材料足りてないよね?

▼ランダム

コンピュータくんに適当な数値を決めてほしい。その願いをRandomがかなえてくれます。

とりあえずPrac6を作れ。Ctrl + @でターミナル開いて以下コマンドを叩く。

cd ..
dotnet new console -o Prac6
code Prac6 -r

そしたら以下を書いてみる。

using System;

namespace Prac6
{
    class Program
    {
        static void Main(string[] args)
        {
            var r = new Random();
            while (true)
            {
                Console.Write(r.Next(0, 101));
                Console.ReadLine();
            }
        }
    }
}

このコードにより、Enterを押すたびに0~100の値が表示されるプログラムを記述できる。

終わりたくなったらCtrl + c。

・var r = new Random();

C#には、ランダムな値を生成してくれる奴がはじめから用意されている。それがRandomさ。

そのRandomは、なんていうか概念の状態でありますから、この世界に召喚してあげなければ使うことできません。「new Random()」っていう操作によって実体化させてあげる必要がある。

うつしよに顕現したRandomを「r」という名前の変数に格納して使えるようにしたのが先ほどのコードです。

rという変数を宣言しつつ、召喚したRandomで初期化している。

・varってなに?

理解できなくていい。

varを使わないのが本来の書き方だったりする。以下の記述でRondomの実体(今後はインスタンスと呼ぶ)を変数rに格納できる。

Random r = new Random();

Rondomをnewによって実体化させたとき、そのインスタンスを格納できる変数もRondomという形になっていなければならない。

うん。わからんだろう。

変数には種類があって、ある変数にはあるインスタンスしか格納できない。「string fish」にはstringを、「Random r」にはRandomを、「int n」にはintしか格納することができない。

stringは文字列で、intは整数値。Randomはランダム。いやランダムってなんだよ。

とにかく、以下の書き方により「rという名前の変数を作りながら」「インスタンス化したRandomを代入」している。代入ってなんだよ。

Random r = new Random();

そいで、「=」の右側、すなわち右辺がRandomのインスタンスであるってことはコンパイラくんが理解できるので、左側に書かれている「Random r」のRandomは「var」っていう風に省略できる。

「var fish = “”」であれば、右辺が文字列だってことをコンパイラくんが推論できるんでfishの型はおのずとstringって決定される。いちいち変数の型を書かなくていいので便利。

・r.Next(0, 101)

Randomのインスタンスには「Next」っていうメソッドがある。

おまえ「メソッドってなに。もうわからん。いっそ殺して。」

いっぺん寝てこい。

メソッドってのは、命令みたいなものです。この世界に召喚したRandomに対して「0から100までの整数をおくれやす」って命令できなければRandomを召喚した意味なかろう?命令できなければただの置物だ。

Randomのインスタンスは、Nextという命令を聞くことができる。言い換えればNextというメソッドを持っていて、俺たちはそのメソッドを呼ぶことができる。

Nextを呼ぶときには、一緒に色々と値を渡すことができる。今回は「0から100のランダムな整数」が欲しいんで、0と101を渡しておけばいい。

その他の用法は「Randomクラスのページ」を参照

おまえ「クラス」ってなに?

なんかの概念って思ってればいいよ。概念であるところのクラスをインスタンス化したら、そのインスタンスを使って遊べる。

そいで、さっきも言ったけどRandom.Nextというメソッドはランダムな値を返却してくれる。それを受け取るにはintの変数を作ればよいよ。

var r = new Random();
var n = 0;

n = r.Next(0, 101);
Console.WriteLine(n);

n(numberのn)っていう変数にランダムな値を記憶させておくことができる。

Random.Nextって呼ぶたびに新しい値を返してくれちゃうわけだから、どっかに保存しておかないと再利用できなくて困るよね。

▼比較

数字とか文字列とか、わりと簡単に比較することできます。

比較演算子

C#に於ける「比較」はIComparable<T>インターフェースを…

いや。そうじゃないな。

とにかく、「==」とか「!=」で一致不一致を比較できるし、数値なら「<」とか「>」とか「<=」とか「>=」で大小の比較もできる。

たとえば「<」は「右辺が左辺よりも大きいかどうか」だし、イコールがついた「<=」なら「右辺が左辺以上であるかどうか」を判定することできます。

using System;

namespace Prac6
{
    class Program
    {
        static void Main(string[] args)
        {
            int n1 = -1;
            int n2 = 2;

            Console.WriteLine(n1 == n2);
            Console.WriteLine(n1 != n2);
            Console.WriteLine(n1 < n2);
            Console.WriteLine(n1 > n2);
            Console.WriteLine(n1 <= n2);
            Console.WriteLine(n1 >= n2);
        }
    }
}

TrueやらFalseがターミナルに表示されたことと思う。

これをif文とかwhile文で使えますよね。

もう作れるよね?よぉし。作れ。考えろ。

・おまえ「作れねぇよ」

それなー。

さっきから「文字列」とか「数値」とか言ってたんだけど、それらって本質的に違うから比較とかできないんですね。

数値の「0」と文字列の「”0″」を比較することは当然ながらできない。数値の「2」と「100」は当然 2 のほうが小さいけど、文字列の「”2″」と文字列の「”100″」であれば、最初の一文字が「1」である「100」のほうが小さい扱いです。

文字列を比較するためには「string.Compare(“100”, “2”)」みたいにする。「<」とかは通常使えない。そいで100のほうが小さいんで、このメソッドは「-1」を返却してくる。

標準入力からは文字列で受け取る以外ねぇし、以上の情報から「文字列を数値に変換する」ことができないと困るような気がしてきませんかね。しない?しろ。

int32.TryParseっていうメソッドを使います。覚えること多いねぇ。

using System;

namespace Prac6
{
    class Program
    {
        static void Main(string[] args)
        {
            var moji = "1";
            var r = int.TryParse(moji, out var kazu);
            if (r) Console.WriteLine(100 > kazu);
        }
    }
}

Q:intって実体化されてないけど、なんでTryParseって命令を呼べるんですか。

A:そういう日もある。概念が持ってるメソッドを使うことができたりする。超能力。

Q:「var r」には何が入ってるの。

A:変換に成功したかどうか。mojiの中に「”うんこ”」とか入ってたら、数値に変換できないよね。変換できなかったらfalseが入りますわよ。

Q:「out var kazu」ってどゆこと。

A:メソッドには「out」っていう「結果を出す場所」を受け取るものがある。「var kazu」を出力場所に指定してあげることによって、Parseの結果をkazuに受け取ることができる。

TryParseの結果がtrueだったら変換に成功したってことだから、if文を書いて使ってあげればいい。falseだったら「変換に失敗したよ」って扱いにして差し上げろ。

よーーしお疲れ様。じゃあ作れ。それそれ。

  1. コンピュータくんが 0 ~ 100 の間で好きな数字を決める。
  2. プレイヤーが適当な数値を標準入力に食わせる。
  3. 間違っていた場合、その入力した数値が上か下かを教えてくれる。
  4. 正解するまでループ。

書いてみて、俺の書いたコードと見比べてみような。

▼回答例

タァーーーーーーイムアーーーーーーップ!(サルヂエ

こんなんでました。

using System;

namespace Prac6
{
    class Program
    {
        static void Main(string[] args)
        {
            var random = new Random();
            var target = random.Next(0, 101);

            Console.WriteLine("◆かずあてゲーム");
            Console.WriteLine("・俺のいま考えてる数値を当てろ。");

            while (true)
            {
                var input = Console.ReadLine();
                var result = int.TryParse(input, out var n);
                if (result)
                {
                    if (target == n) break;

                    if (target < n)
                    {
                        Console.WriteLine("・おっきぃよぉ///");
                    }
                    else
                    {
                        Console.WriteLine("・ちいさい…");
                    }

                }
                else
                {
                    Console.WriteLine("・数値を入れろ数値を");
                }
            }

            Console.WriteLine("・正解だ。よくやった。");
        }
    }
}

回答のひとつがこんな感じ。お前の書いたコードはどうだっただろうか?

人の書いたコードと見比べてみるのも楽しかろう。

▼別解:notとcontinue

true/falseの前に「!」を入れると、true/falseを反転できる。

あとcontinueってのを使うと

  • ループを打ち切る。
  • ループの先頭からもっかい始める。

ができる。breakはループを打ち切ってループの外に出ちゃうけど、continueはループの先頭に戻れる。

つまり?つまり、うまくやればブロックの入れ子を減らすことができる。ifとかwhileとか、階層が深すぎるとコードが読みづらくなるんすね。同じ処理を書いたとしても読みづらいコードになる。読みやすいコードにもできる。

using System;

namespace Prac6
{
    class Program
    {
        static void Main(string[] args)
        {
            var random = new Random();
            var target = random.Next(0, 101);

            Console.WriteLine("◆かずあてゲーム");
            Console.WriteLine("・俺のいま考えてる数値を当てろ。");

            while (true)
            {
                var input = Console.ReadLine();
                var result = int.TryParse(input, out var n);

                if (!result)
                {
                    Console.WriteLine("・数値を入れろ数値を");
                    continue;
                }

                if (target == n) break;

                if (target < n)
                {
                    Console.WriteLine("・おっきぃよぉ///");
                }
                else
                {
                    Console.WriteLine("・ちいさい…");
                }
            }

            Console.WriteLine("・正解だ。よくやった。");
        }
    }
}

「変換に失敗した場合」って、もう次のループに行っちゃえばいいじゃん。だからcontinueしちまえばいいんだよ。そうすれば入れ子の深さを減らせる。上から読んで理解できるコードにできる。

continueやらbreakやらでループを操れ。うまいことやればコードを簡潔にできる。一番初めに書いたコードは、たぶん読みづらいコードだ。書き直したりしろ。

▼スリーチャンス

ゲーム性がないわな。「当てられなかったら負け」じゃないとゲームとは言えねぇし、一発で当てられるような奇跡は祝ってほしい。ルールを変更してみる。

  • 3回で当てられないと負け。
  • 0 ~ 100 だと広すぎるんで、0 ~ 9 にする。
  • 一撃で当てたら祝う。

・四則演算

ヒントとして「変数の内容を1ふやす」って操作が必要になる。けど、四則演算の方法を教えてなかったな。数値を単純に足したり引いたりすればいい。

int a = 0;
int b = 1;
int c = a + b;

ね。「a + b」の結果をcに代入している。

加算は「+」減算は「-」乗算は「*」除算は「/」。プログラム言語によるけども。乗除算が加減算より優先されるのも四則演算と同じだし、カッコで優先度を変更できる。

int a = (1 + 2) * 3 + 4 * 5;

aの中身は29ですよね。

あと、以下の書き方もできまする。

int a = 0;
a = a + 1;

右辺がまず処理されてそれが代入されるんだとしたら、aには1が入るよな。

でな?以下の結果は何だと思うね。

int a = 3 / 2;

Console.WriteLineすればわかるけど、「1.5」にはなってくれんのですね。なんでか「1」になる。

それはintが整数値だから。小数値は切り捨てされちゃう。

小数値を扱いたいのであれば、intじゃのうてdoubleを使えばいい。

double a = (double)3 / 2;

double型であったらaが1.5になってくれる。ミソなのが「(double)3」って書き方。3をdouble扱いすることができる。3がint扱いのままだと、小数点はやっぱり切り捨てられてしまう。

以下の書き方でもいいよ。「3.0」って書けば「小数やね」って理解してくれる。

double a = 3.0 / 2;

足し算を知っていれば問題は解ける。解けるんだけど、便利な書き方をまた教えてあげるからそれを使っても良いよ。

・return

returnでその関数を終わらせることができる。breakはループを終わらせることができたけど、returnは関数を終わらせることができる。

using System;

namespace Prac6
{
    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                var input = Console.ReadLine();

                if (input == "さば")
                {
                    Console.WriteLine("「「サバ!!!」」");
                    return;
                }
            }
        }
    }
}

このreturnは「Main」ってブロックから脱出できる。

正解したらもうそこでメッセージ出して処理を打ち切ろうや。

・const

定数。「変数」は後から再代入して中身を変えることができるけど、定数には再代入できない。「もう値を変えたくない」って明示することによりコードの意図を明確化できる。

const int a = 1;

このaは変数みたいに値の読み直しができるけど、再代入はできない。

・インクリメント/デクリメント

代々、「++」とか「–」みたいなもんで数値を1増やしたり減らしたりできる。できないプログラミング言語もあるけどC#はできる。

int a = 1;
a++;
a--;

a++を通ると、aに1がプラスされる。a–を通ると、aから1がマイナスされる。インクリメントとかデクリメントとか言う。

何回目の挑戦であるかを数えるために、カウンターとなる変数を作ってインクリメントしていこう。

よーしじゃあ作れ。

▼新Prac6

できたかね。俺が書いたのが下記の通り。見比べてみろ。

using System;

namespace Prac6
{
    class Program
    {
        static void Main(string[] args)
        {
            const int LIMIT = 3;
            const int MAX_NUM = 9;

            var random = new Random();
            var target = random.Next(0, MAX_NUM + 1);

            Console.WriteLine("◆かずあてゲーム");
            Console.WriteLine($"・俺のいま考えてる数値(0 ~ {MAX_NUM})を当てろ。 {LIMIT} 回以内に。");

            var tryCount = 0;
            while (tryCount < LIMIT)
            {
                tryCount++;
                Console.WriteLine($"『{tryCount} 回目の挑戦』");

                var input = Console.ReadLine();
                var result = int.TryParse(input, out var n);

                if (!result)
                {
                    Console.WriteLine("・数値を入れろ数値を");
                    continue;
                }

                if (target == n)
                {
                    if (tryCount == 1)
                    {
                        Console.WriteLine("・ホールインワン!ワンワンワンワンワンワン!");
                        return;
                    }
                    Console.WriteLine("・正解だ。よくやった。");
                    return;
                }

                if (target < n)
                {
                    Console.WriteLine("・おっきぃよぉ///");
                }
                else
                {
                    Console.WriteLine("・ちいさい…");
                }
            }

            Console.WriteLine($"・お前の負けだ。正解は {target} でした。");
        }
    }
}

こんな感じ。わかるかね。わからんかね。

ちょい難しかったかもしれんな。でも大体これで遊べるだろう。

もうちょい簡単な例に戻る。

◆Prac7 ~FizzBazz~

代々受け継がれしプログラミングの課題。それがFizzBazz。

  • 1から100までの数値をループさせる。
  • 3で割り切れる場合は標準出力に「Fizz」と表示。
  • 5で割り切れる場合は標準出力に「Bazz」と表示。
  • 3でも5でも割り切れる場合は標準出力に「FizzBazz」と表示。
  • それ以外は元々の数値を表示。

▼割り切れるってなに?

あまりがないこと。剰余がゼロであること。

つまり、

  • 数値Aを数値Bで割った結果を整数値Cに代入
  • BとCを掛け合わせてAと比較
  • 一致しなければ割り切れない

だよね。

int使えば小数値を無視できるから、以下の書き方で判別可能。

int x = 9;
int y = 3;
int z = x / y;
if (y * z == x)
{
    Console.WriteLine($"{x} は {y} で割り切れる");
}
else
{
    Console.WriteLine($"{x} は {y} で割り切れない");
}

ね。

でもまぁまぁ面倒だから、余りを求める演算子「%」を使えばよろしい。

int x = 10;
int y = 3;
if (x % y == 0)
{
    Console.WriteLine($"{x} は {y} で割り切れる");
}
else
{
    Console.WriteLine($"{x} は {y} で割り切れない");
}

うわぁべんりぃ。ありがとーーーー。

今までの例を理解できたんなら、問題なくコードに落とし込めるだろう。書け。

この後に回答を載せるけど、また教えてない書き方をする。あはん。

▼回答例

こんなかんじ。

using System;

namespace Prac7
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i <= 100; i++)
            {
                if (i % 3 == 0 && i % 5 == 0)
                {
                    Console.WriteLine("FizzBazz");
                }
                else if (i % 3 == 0)
                {
                    Console.WriteLine("Fizz");
                }
                else if (i % 5 == 0)
                {
                    Console.WriteLine("Bazz");
                }
                else
                {
                    Console.WriteLine(i);
                }
            }
        }
    }
}

▼forってなに

  • iって変数を0で宣言しつつ
  • ループが終わるごとに「i++」しつつ
  • 「i <= 100」を満たす限りループ

みたいなのを、上記のノリで書けます。whileでカウンター作ってってのでも問題ないが。

▼and / or

真偽値「true」「false」の変数がふたつあったときに、その条件を混ぜ合わせたい。「条件1と条件2の両方がtrueであるとき」をandで、「条件1と条件2のどちらか片方がtrueであるとき」をorで引っ掛けられる。

andが「&&」でorが「||」です。

  • if (true && true):とおる
  • if (true && false):とおらない
  • if (true || false):とおる

▼ナベアツ

3で割り切れる数値と3を含む数値でアホになるプログラム。

「3を含む数値」ってのの判定が、intのままだと難しい。だからintを文字列に変換したい。

こうする。

int a = 1;
string b = a.ToString();

intのインスタンスには「ToString」ってメソッドがある。戻り値はstring。すなわち文字列。

そいで更に言えば、stringのインスタンスは「Contains」ってメソッドを持っていて、ある文字列が任意の文字列を持っているかどうかを判定し true / false で返してくれる。

じゃあこんな感じで書けるだろう。

using System;

namespace Prac7
{
    class Program
    {
        static void Main(string[] args)
        {          
            for (int i = 0; i <= 100; i++)
            {
                if (i % 3 == 0 || i.ToString().Contains("3"))
                {
                    Console.WriteLine($"( ゜∀ 。)「{i}!!!!!!」");
                }
                else
                {
                    Console.WriteLine($"( ・∀・)「{i}」");
                }
            }
        }
    }
}

◆Prac8 ~じゃんけんマシン~

これで最後。じゃんけんマシン作ろう。

ほんとはコンソールじゃなくて画面が出てくるあれを作ろうと思ったんだけど、説明しきれん。一応「dotnet new wpf」で、画面が表示されるWPFアプリを作れる。

Prac8を作る。

cd ..
dotnet new console -o Prac8
code -r Prac8

とりあえず、以下の感じでつくれるだろう。標準入力にグーチョキパーを入力したらじゃんけんできる。

using System;

namespace Prac8
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("◆じゃんけんしようぜ!");

            // 相手の手
            var r = new Random();
            var n = r.Next(0, 3);
            var enemy = n switch
            {
                0 => "グー",
                1 => "チョキ",
                _ => "パー",
            };

            // 自分の手
            var me = Console.ReadLine();

            if (me == enemy)
            {
                Console.WriteLine("◆あいこでしょ?");
                return;
            }

            if ((me == "グー" && enemy == "チョキ")
                || (me == "チョキ" && enemy == "パー")
                || (me == "パー" && enemy == "グー"))
            {
                Console.WriteLine("◆俺の負けだよ…");
                return;
            }

            Console.WriteLine("◆てめぇの負けだ。でなおしてまいれ。");
        }
    }
}

▼switch

ランダムの結果を、じゃんけんの手に変換してるな。

var enemy = n switch
{
    0 => "グー",
    1 => "チョキ",
    _ => "パー",
};

「0、1、2」をRandomに生成して、それをグーチョキパーに変換して「enemy」という変数に格納している。最後の「_ => “パー”」ってのは、それまでの条件に一致しなかったパティーンを補足してるやつ。

この書き方では0でも1でもないやつを全てパーとして扱ってる。

▼野球拳

じゃあ、体力制にしてみよう。

自分のライフ、相手のライフを変数に入れて、whileで回せばいいだろ。

using System;

namespace Prac8
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("◆じゃんけんしようぜ!");

            const int MAX_LIFE = 5;

            var life = MAX_LIFE;
            var enemysLife = MAX_LIFE;

            while (life != 0 && enemysLife != 0)
            {
                // 相手の手
                var r = new Random();
                var n = r.Next(0, 3);
                var enemy = n switch
                {
                    0 => "グー",
                    1 => "チョキ",
                    _ => "パー",
                };

                // 自分の手
                var me = Console.ReadLine();

                if (me == enemy)
                {
                    Console.WriteLine("◆あいこでしょ?");
                    continue;
                }

                if ((me == "グー" && enemy == "チョキ")
                    || (me == "チョキ" && enemy == "パー")
                    || (me == "パー" && enemy == "グー"))
                {
                    Console.WriteLine("◆ぐはあぁ!!!");
                    enemysLife--;
                }
                else
                {
                    Console.WriteLine("◆オラアアア!!!");
                    life--;
                }
                
                Console.WriteLine($"[自分の体力:{life} / {MAX_LIFE}] [敵の体力:{enemysLife} / {MAX_LIFE}]");
            }

            if (life == 0) Console.WriteLine("◆てめぇの負けだ。でなおしてまいれ。");
            if (enemysLife == 0) Console.WriteLine("◆俺の負けだよ…死にます。");
        }
    }
}

うん。ええやん。ゲームやんこれ。

▼拡張:処理の切り出しとEnum

読まんでよし。

・関数(メソッド)

ある処理のまとまりを切り出すことができる。

using System;

namespace Prac8
{
    class Program
    {
        static double CalcPriceIncludingTax(int p)
        {
            return p * 1.08;
        }

        static void Main(string[] args)
        {
            var price = 100;
            var taxed = CalcPriceIncludingTax(price);
            Console.WriteLine($"税抜き{price}円は税込み{taxed}円です。");
        }
    }
}

Mainと同じ階層に「CalcPriceIncludingTax」って関数を定義している。intの変数を受け取り、doubleを返却している。その「CalcPriceIncludingTax」は、とりあえずMainの中で使うことができる。Mainの中でpriceを渡したら、doubleとして結果が返ってきてますわね。

「static」ってのは、インスタンス不要なメソッドであるという意味だよ。理解できなくていいけど。

・Enum

Enumってのが用意されているプログラム言語が多い。Enumは列挙って言うか…数値に意味を持たせるための機構というか…なんというか…

「じゃんけんの手」とか「じゃんけんの勝敗」をEnumにすることができる。

enum Command
{
    Gu = 0,
    Choki = 1,
    Pa = 2
}

enum Result
{
    Win,
    Lose,
    Draw
}

また、このenumにもTryParseがある。ターミナルからReadLineで「0」とか受け取りつつenumに変換するのは以下の通り。

var r = Enum.TryParse(Console.ReadLine(), out Command me);

rには変換できたかどうかが入って、meにはCommandが入る。

例を書いてみる。もう多くは説明せんぞ。読み解け。標準入力に「0」「1」「2」を入力すればそれがグーチョキパーに対応する。「Gu」「Choki」「Pa」って入力しても動いてくれるよ。

using System;

namespace Prac8
{
    enum Command
    {
        Gu = 0,
        Choki = 1,
        Pa = 2
    }

    enum Result
    {
        Win,
        Lose,
        Draw
    }

    class Program
    {
        static Result Janken(Command me, Command enemy)
        {
            if (me == enemy) return Result.Draw;

            if ((me == Command.Gu && enemy == Command.Choki)
                || (me == Command.Choki && enemy == Command.Pa)
                || (me == Command.Pa && enemy == Command.Gu))
            {
                return Result.Win;
            }

            return Result.Lose;
        }

        static void Main(string[] args)
        {
            Console.WriteLine("◆じゃんけんしようぜ!");

            const int MAX_LIFE = 5;

            var life = MAX_LIFE;
            var enemysLife = MAX_LIFE;

            while (life != 0 && enemysLife != 0)
            {
                // 相手の手
                var random = new Random();
                var enemy = (Command)random.Next(0, 3);

                // 自分の手
                var parseResult = Enum.TryParse(Console.ReadLine(), out Command me);
                if (!parseResult)
                {
                    Console.WriteLine("◆そんな手はねぇ!");
                    continue;
                }

                var result = Janken(me, enemy);
                switch (result)
                {
                    case Result.Win:
                        Console.WriteLine("◆ぐはあぁ!!!");
                        enemysLife--;
                        break;
                    case Result.Lose:
                        Console.WriteLine("◆オラアアア!!!");
                        life--;
                        break;
                    default:
                        Console.WriteLine("◆あいこでしょ?");
                        break;
                }
                Console.WriteLine($"[自分の体力:{life} / {MAX_LIFE}] [敵のライフ:{enemysLife} / {MAX_LIFE}]");
            }

            if (life == 0) Console.WriteLine("◆てめぇの負けだ。でなおしてまいれ。");
            if (enemysLife == 0) Console.WriteLine("◆俺の負けだよ…死にます。");
        }
    }
}

▼拡張:画面のリライト

読まんでよし。

もっとゲームっぽくするには、画面のリライトをすればいい。まいど画面を書き直せばいい。

「Console.Clear()」ってのを書けばいいよ。

以下の感じになる。やったね(説明放棄)。説明しない。何とかググったりして読み解け。

using System;

namespace Prac8
{
    enum Command
    {
        Gu = 0,
        Choki = 1,
        Pa = 2
    }

    enum Result
    {
        Win,
        Lose,
        Draw
    }

    class Program
    {
        static Result Janken(Command me, Command enemy)
        {
            if (me == enemy) return Result.Draw;

            if ((me == Command.Gu && enemy == Command.Choki)
                || (me == Command.Choki && enemy == Command.Pa)
                || (me == Command.Pa && enemy == Command.Gu))
            {
                return Result.Win;
            }

            return Result.Lose;
        }

        static void Main(string[] args)
        {
            const int MAX_LIFE = 5;

            var life = MAX_LIFE;
            var enemysLife = MAX_LIFE;

            while (true)
            {
                Console.Clear();
                Console.WriteLine("◆じゃんけんしようぜ!");
                Console.WriteLine($"→自分の体力:{new string('●', life)}{new string('◯', MAX_LIFE - life)}");
                Console.WriteLine($"→相手の体力:{new string('●', enemysLife)}{new string('◯', MAX_LIFE - enemysLife)}");
                Console.WriteLine();
                Console.WriteLine("◆じゃーんけーん…");
                Console.WriteLine("[グー:0] [チョキ:1] [パー:2]");

                // 相手の手
                var random = new Random();
                var enemy = (Command)random.Next(0, 3);

                // 自分の手
                var parseResult = Enum.TryParse(Console.ReadLine(), out Command me);
                if (!parseResult)
                {
                    Console.WriteLine("◆そんな手はねぇ!");
                    Console.WriteLine();
                    Console.WriteLine("[Press Enter...]");
                    Console.ReadLine();
                    continue;
                }

                // 両者の手を表示
                Func<Command, string> toHand = (Command c) => c switch
                {
                    Command.Gu => "グー",
                    Command.Choki => "チョキ",
                    _ => "パー",
                };
                Console.WriteLine($"自分の手:{toHand(me)}");
                Console.WriteLine($"敵の手:{toHand(enemy)}");
                Console.WriteLine();

                // じゃんけん
                var result = Janken(me, enemy);
                switch (result)
                {
                    case Result.Win:
                        Console.WriteLine("◆ぐはあぁ!!!");
                        enemysLife--;
                        break;
                    case Result.Lose:
                        Console.WriteLine("◆オラアアア!!!");
                        life--;
                        break;
                    default:
                        Console.WriteLine("◆あいこでしょ?");
                        break;
                }

                // 体力が空になったらループ脱出
                if (life == 0 || enemysLife == 0) break;

                Console.WriteLine();
                Console.Write("[Press Enter...]");
                Console.ReadLine();
            }

            if (life == 0) Console.WriteLine("◆てめぇの負けだ。でなおしてまいれ。");
            if (enemysLife == 0) Console.WriteLine("◆俺の負けだよ…死にます。");
        }
    }
}

◆おわり

もう書くの疲れた。ふざけんな。だれが真面目にこなすんだこんなもん馬鹿野郎。

というので、こんな感じにプログラムを書くのよ。もっときれいに書くこともできるけど、またそれは別のお話…

第一コンソールアプリしか作ってないし。先は長いよね。でもまぁ入門できたと言えるんじゃないの?

…できてないか。せめて配列とファイル操作くらいはやったほうがいいわな。でも俺はもう疲れた。いずれパート2でやろうぜ。

そのあとはWebアプリのつくりでもやろうかね。ここでの学びは役に立つはずだぜ。基礎をすっ飛ばして入門することもできたけど、今回は基礎をやったのだよ。お疲れ様だったな。

そんじゃな。あばよ。またどこかで。