C#.NET Frameworkでソケット通信型トランプゲームを作った話

![5i6.gif](https://qiita-image-store.s3.amazonaws.com/0/360820/a15fa6ac-2a42-5eb2-d09c-ad67a4f142ed.gif)


#はじめに
かなーりコードが雑なのであまり詳しいコードは書きません。ご了承ください。
簡単なトランプゲームの4人用だけできます。カードを渡す系とかできません。
UIを非常に簡単にきれいに作れる**WPF**、おすすめですよ。(もうすでにクラシックデスクトップ？）
尚セキュリティは何も考えてなく技術力もありませんご了承ください。
#前提
非同期処理が少しだけでもわかる
トランプを知っている
C#が書ける
#動機
C#.NET大好きだから。トランプなら比較的簡単に作れると思ったから。
#方法
ソケット通信にはもうすーでに用意されてる**System.Net.Sockets**をつかうーよ。
１．クライアントはメッセージを作って一定時間ごとにサーバに送るよ。
２．サーバは受け取ったメッセージをまとめてクライアントに送るよ。
３．クライアントは受け取ったメッセージを解釈して相手の手札の枚数、チャット、出したカードの種類とか判別して画面に反映させるよ。
４．１～３までループしてゲームを進行するよ。
#こんなかーんじ
##まず接続先のサーバを作るよ

クライアントを承認&処理するためにTcpClient、MemoryStream、NetworkStreamを作っておくよ。


```C#:
 TcpClient[] tcpcli = new TcpClient[4];//4人用
 System.Net.Sockets.NetworkStream[] ns = new NetworkStream[4];
 System.IO.MemoryStream[] ms = new System.IO.MemoryStream[4];
```

TcpListenerでIPAddress.Any、ポート番号2001からくる接続を待機するよ。

```C#:
TcpListener tcplis = new TcpListener(IPAddress.Any, 2001);
```

4人あつまるまで待機するよ。

```C#:
            await Task.Run(async () =>
            {
                System.Text.Encoding enc = System.Text.Encoding.Unicode;
                int act, cnt = 0;
                while (cnt<4)
                {
                    //人が来たら承認する
                    tcpcli[cnt] = await tcplis.AcceptTcpClientAsync();
                    //承認した人のストリームを得る
                    ns[cnt] = tcpcli[cnt].GetStream();
                    //承認した人専用のメモリストリームを作る
                    ms[cnt] = new System.IO.MemoryStream();
　　　　　　　　　　　//クライアントはここで自分の名前を送ってくる
　　　　　　　　　　　//クライアントの名前をゲット
                    act = await ns[cnt].ReadAsync(bufbytes, 0, bufbytes.Length);
                    await ms[cnt].WriteAsync(bufbytes, 0, act);
                    //得たメッセージを文字列に変換
                    string mes = enc.GetString(ms[cnt].GetBuffer(), 0, (int)ms[cnt].Length);
                    ms[cnt].SetLength(0);
                    Console.WriteLine($"Name: {mes} 接続されました。あと{3 - cnt}人です");
                    names[cnt] = mes;
                    cnt++;
                    for (int i = 0; i < cnt; i++)
                    {
                        byte[] sendbyte = enc.GetBytes($"あと{4 - cnt}人です\n");
                        await ns[i].WriteAsync(sendbyte, 0, sendbyte.Length);
                    }
                    await Task.Delay(100);
                }
            });
```
##トランプを分けるよ
4人全員承認し終えたらトランプを分けるよ。
もっといい書き方あるとおもいます。

まずカードの文字列をつくるよ。（これでメッセージをやりとりするから）

```C#
　　　　　　　　 string[] card = new string[53];
                //カードを初期化
                int iInit = 0;
                for (int i = 0;i<53;i++)
                {
                    if (iInit == 0)
                    {
                        card[i] = "[C]{" + (i + 1).ToString() + "}";
                        if (i == 12) iInit++;
                    }
                    else if (iInit == 1)
                    {
                        card[i] = "[H]{" + (i + 1-13).ToString() + "}";
                        if (i == 25) iInit++;
                    }
                    else if (iInit == 2)
                    {
                        card[i] = "[D]{" + (i + 1-26).ToString() + "}";
                        if (i == 38) iInit++;
                    }
                    else if (iInit == 3)
                    {
                        card[i] = "[S]{" + (i + 1-39).ToString() + "}";
                        if (i == 51) iInit++;
                    }
                    else if(iInit==4)
                    {
                        card[i] = "[J]{0}";
                        iInit++;
                    }
                }
```

カードをシャッフルするよ

```C#
                //カードをシャッフル
                Random r = new Random();
                for(int i = 0; i <53;i++)
                {
                    int ran = r.Next(0, 52);
                    string tmp = card[i];
                    card[i] = card[ran];
                    card[ran] = tmp;
                }
```

カードを四分割するよ

```C#
                //カードを分ける
                int cCnt = 0;
                
                string _0 = _1 = _2 = _3 = "";
                for(int i = 0; i <53;i++)
                {
                    if (cCnt == 0)
                    {
                        _0+=card[i];
                        cCnt++;
                    }
                    else if (cCnt == 1)
                    {
                        _1 += card[i];
                        cCnt++;
                    }
                    else if (cCnt == 2)
                    {
                        _2 += card[i];
                        cCnt++;
                    }
                    else if (cCnt == 3)
                    {
                        _3 += card[i];
                        cCnt=0;
                    }
                }
```

indexの値に対応する人にカードを分けていく！

```C#
        async void sendcardinftoclient(int index)
        {
            byte[] sendbyte;
            if (index == 0)
            {
                sendbyte = enc.GetBytes("cardinfo"+ _0 + "\n");
            }
            else if (index == 1)
            {
                sendbyte = enc.GetBytes("cardinfo" + _1 + "\n");
            }
            else if (index == 2)
            {
                sendbyte = enc.GetBytes("cardinfo" + _2 + "\n");
            }
            else
            {
                sendbyte = enc.GetBytes("cardinfo" + _3 + "\n");
            }
            await ns[index].WriteAsync(sendbyte, 0, sendbyte.Length);
        }
```
##クライアントを作っていくよ
サーバに接続、認証は省かせてもらいます。カード情報を受け取るところから。

```C#
                     while (true)
                     {
                         string temp = "";
                         if (temp.IndexOf("cardinfo") > -1)
                         {
                             mycard = temp;
                             MatchCollection kmc = Regex.Matches(temp, @"\[(.+?)\]");//[]で囲まれた文字を取得
                             MatchCollection vmc = Regex.Matches(temp, @"\{(.+?)\}");//{}で囲まれた文字を取得
                             int mCnt = 0;
                             int mmCnt = 0;
                             foreach (Match m in kmc)
                             {
                                 foreach (Match mm in vmc)
                                 {
                                     if (mCnt == mmCnt)
                                     {
                                         string ms = m.ToString().Replace("[", "").Replace("]", "");
                                         string mms = mm.ToString().Replace("{", "").Replace("}", "");
                                         Console.WriteLine(ms + mms);
                                         //mycardKはクローバーの「C」やスペードの「S」を格納するstring配列
　　　　　　　　　　　　　　　　　　　　　　 mycardK[mCnt] = ms;
                                         //mycardVは値を-1~13の格納するint配列(-1はカードを出したことを示し、0はJokerを示す)
                                         mycardV[mCnt] = int.Parse(mms);
                                     }
                                     mmCnt++;
                                 }

                                 mmCnt = 0;
                                 mCnt++;
                             }
                             mycardR = mCnt;
                             break;
                         }
                         
                         await Task.Delay(500);
                         temp = await resGetAsync();//サーバからのメッセージを受け取る
                     }
```
ここまでできたらあとはもうメッセージに対応する処理をすればトランプゲームができてしまいます。TcpListener、TcpClient。非常に便利ですね。
#やってみて
始めてネットワークを使用したアプリ作りました。タイミングが非常に重要なんだなって思いました。同期させて処理をするのが難しいので結構妥協してるところがあります。
かなり適当な記事になって申し訳ないですが温かい目で見てくれると助かります。（**参考にしない方がいいです**）
#さいごに
？？？？？？？？
![5i7.gif](https://qiita-image-store.s3.amazonaws.com/0/360820/bf0d8fa4-f590-4ca8-600b-507d5c0c10b2.gif)