はじめてのC++【26日目】

#はじめに
今日は、思ってたより時間取れなくてディーラーの動作を開発する。
それと、ABCコンテストがあったので参加した。
##Atcoder ABC122
今日は、文字列を扱う問題であった。A問題は解けたが、B問題で解き方がわかったがコードに移すことができなかった。
文字列を扱うのにまだなれていないということがわかった。
###A問題【Double Helix】
>**[問題文]**
AtCoder 星には四種類の塩基 A, C, G, T が存在し、A と T、C と G がそれぞれ対になります。
文字 bが入力されます。これは A, C, G, T のいずれかです。塩基 b と対になる塩基を表す文字を出力するプログラムを書いてください。
**[注記]**
bは文字 A, C, G, T のいずれかである。

```A.cpp
#include <iostream>

int main(){
  char a = 'a';//aで初期化
  std::cin >> a;
  if(a=='A'){
    std::cout << 'T' << std::endl;
  }else if(a=='T'){
    std::cout << 'A' << std::endl;
  }else if(a =='C'){
    std::cout << 'G' << std::endl;
  }else{
    std::cout << 'C' << std::endl;
  }
}
```
この問題は優しかった。
でも、`char a`に代入する文字を`ダブルクオーテーション`で記述するとエラーがでて、`シングルクオーテーション`だとエラーが出なかった。

**理由**
1文字を代入するときは`シングルクオーテーション`で代入する。
2文字以上の文字列を代入するときは,`ダブルクオーテーション`を使う。

公式の回答
4種類ほど回答があったが`map`という新しい機能(私にとって)を使っていたので、ここに載せておく。

```ans_A.cpp
#include<iostream>
#include<string>
#include<map>
using namespace std;
 
int main() {
	//入力
	string S;
	cin >> S;
 
	//処理
	// A <-> T C <-> G
 
	map<char, char> m;
	m['A'] = 'T';
	m['T'] = 'A';
	m['C'] = 'G';
	m['G'] = 'C';
 
	char ans = m[S[0]];
 
	//出力
	cout << ans << endl;
}
 
```
mapは連想配列というみたい。
野球みたいに、背番号を選手を登録しできるといった機能である。
どこかで使えそうなのでメモしておく。

###B問題 【ATCoder】
>**[問題文]**
英大文字からなる文字列 Sが与えられます。S の部分文字列 (注記を参照) であるような最も長い ACGT 文字列の長さを求めてください。
ここで、ACGT 文字列とは A, C, G, T 以外の文字を含まない文字列です。
**[注記]**
注記
文字列Tの部分文字列とは、Tの先頭と末尾から 0文字以上を取り去って得られる文字列です。
例えば、ATCODER の部分文字列には TCO, AT, CODER, ATCODER,  (空文字列) が含まれ、AC は含まれません。
**[制約]**
・Sは長さ 1以上 10以下の文字列である。
・Sの各文字は英大文字である。

```b(不正解).cpp
#include <iostream>
#include <array>
#include <vector>
#include <string>

int main(){
  std::string a ;
  std::cin >> a;
  int count = 0;
  int n = 0;
  std::array<int, 10> num;

  for(int i = 0;a == '0';i++){
    if(a[i]=='A' ||a[i]=='C'||a[i]=='G'||a[i]=='T'){
      count++;
    }else{
      num[n] = count;
      n++;
      count = 0;
    }
  }
  std::sort(num.begin(),num.end());
  std::cout << num.at(0) << std::endl;
}
```
B問題はできなかった。
考え方は、少しあっていたけど文字列の挿入とかにかなり手こずった。
`std::string`を使うことで、文字を代入することはできたが、そこからはできなかった。

公式回答

```ans_B.cpp
#include<iostream>
#include<string>
#include<map>
#include<algorithm>
using namespace std;
 
int main() {
	//入力
	string S;
	cin >> S;
 
	//処理
	string T = "ATCG";
 
	int ans = 0; //ATCG文字列の最大長
	int now = 0; //今見ている文字を末尾とするATCG文字列の最大長
	for (int i = 0; i < S.size(); i++)
	{
		bool isATCG = false;
		for (int j = 0; j < T.size(); j++)
		{
			if (S[i] == T[j]) isATCG = true;
		}
 
		if (!isATCG) {
			//違う場合は0にする
			now = 0;
		}
		else {
			//ATCG文字の場合は、前の文字列に1つ文字を足せる
			now++;
			//最大値を更新する
			ans = max(now, ans);
			// if(now > ans) ans = now; も同じ
		}
	}
 
	cout << ans << endl;
}
```
とてもきれいなコードと関心してしまいます。
`std::max`をうまく使いこなしているなと思った。
`std::string`にも`std::vector`みたいなメンバ関数もあるんですね。
文字列を扱うときは`std::string`で、数字を扱うときは`std::vector`や`std::array`を使うというふうに使い分けるといいのかな。

##BlackJack【4日目】
今日は思ったより時間が取れなくてクラス化はできなかった。
しかし、遊べるようにはした。
昨日に関数を作っていたので、ほとんど同じ感じでできた。関数って便利！

```BlackJack.cpp
#include <iostream>
#include <array>
#include <vector>
#include <numeric>
#include <random>

/******ランダムな値を生成********/
int Random_Number(int s,int f){
  std::uniform_int_distribution<int> dist(s,f);//s~fのランダム生成
  std::mt19937 mt{ std::random_device{}() };//ランダム作るときに必要
  return dist(mt);
}

/******カードを引く************/
void Draw_a_Card(std::vector<int> &Hand){

  Hand.emplace_back(Random_Number(1, 10));//カードを一枚引く
}

/******マークをランダムに生成(Show_a_Cardで使われている)*******/
void Choice_a_Mark(){
  constexpr const char* heart = "♥";
  constexpr const char* spade = "♠";
  constexpr const char* clover = "♣";
  constexpr const char* dia = "◆";
  switch (Random_Number(1, 4)) {
    case 1:
            std::cout << heart;
            break;
    case 2:
            std::cout << spade;
            break;
    case 3:
            std::cout << clover;
            break;
    case 4:
            std::cout << dia;
            break;
  }
}

/*********引いたカードをターミナルに表示**********/
void Show_a_Card(std::vector<int> &Hand){

  std::cout << "カードは";
  Choice_a_Mark();
  std::cout << ":" << Hand.at(Hand.size()-1) << "です" << std::endl;
}

/*******いままで引いたカードの合計値を表示*********/

void Show_a_Sum(std::vector<int> &Hand){
  std::cout << "合計値は" << std::accumulate(Hand.begin(), Hand.end(), 0) << "です" << std::endl;
}

/******自分の手札の合計値から、次の行動を選択している。*******/

bool my_self_Judge(std::vector<int> &Hand){//自分の手札を判断して継ぎの行動を選択
  char choose = 'd';//dで初期化
  if(std::accumulate(Hand.begin(), Hand.end(), 0)> 21){//合計値が21より大きくなると負け
    std::cout << "21より大きくなりました。あなたの負けです。" << std::endl;
    //break;
    return true;
  }
  else if(std::accumulate(Hand.begin(), Hand.end(), 0)== 21){
    //break;//合計値が21丁度になると強制的に終了
    return true;
  }
  else{
    std::cout << "引き続きカードを引きますか？(y/n)" << std::endl;//カードを引くかどうかの意思決定
    std::cin >> choose;//y/nをchoooseに代入させる
    if(choose == 'n') return true;//もしnならカードを引くことを終了
    return false;
  }
}

/********ディーラーの手札の合計値から次の行動を決定している*********/
bool dealer_Judge(std::vector<int> &Hand){
  if(std::accumulate(Hand.begin(), Hand.end(), 0)> 21){
    std::cout << "ディーラーが21を超えました。 あなたの勝利です！" <<std::endl;
    return true;//カードを引くことをやめる
  }else if(std::accumulate(Hand.begin(), Hand.end(), 0) >= 17){
    std::cout << "ディーラーがカードを引き終えました。" << std::endl
              << "勝負です！" << std::endl;
    return true;//カードを引くことを止める
  }else{
    std::cout << "もう一枚引きます" << std::endl;
    return false;//継続する
  }
}

/********自分の手札とディーラーの手札を比べて勝ち負け判定*********/
void Battle(std::vector<int> &myself, std::vector<int> &dealer){//自分とディーラーの手札が同じ場合ディーラーが勝つ。（三項間演算子 を使いたかった）
  std::accumulate(myself.begin(), myself.end(), 0) > std::accumulate(dealer.begin(), dealer.end(), 0)?
    std::cout << "あなたの勝ちです。" <<std::endl : std::cout << "あなたの負けです。" <<std::endl;
}

/********main関数*********/
int main(){
  std::vector<int> myself = {0};//自分の持ち札を入れる
  std::vector<int> dealer = {0};//ディーラーの持ち札
  bool battle = true;//ディーラーと勝負するかどうか
  std::cout << "BrackJackを始めます" << std::endl;
  while (1) {//自分がカードを引く
    Draw_a_Card(myself);
    Show_a_Card(myself);
    Show_a_Sum(myself);
    if(my_self_Judge(myself)){
      if(std::accumulate(myself.begin(), myself.end(), 0) >21){//手札がオーバしてたら勝負する必要ない
        battle = false;
      }
      break;
    }
  }
  if(battle){

  std::cout << "ディーラーのターン" << std::endl;
    while(1) {//ディーラーのカードを引く
      Draw_a_Card(dealer);
      Show_a_Card(dealer);
      Show_a_Sum(dealer);
      if(dealer_Judge(dealer))break;
    }
    Battle(myself, dealer);
  }

}
```
実行結果
<img width="494" alt="スクリーンショット 2019-03-25 0.51.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/298612/6c536fbe-d106-367e-4a8a-ef34a8b2484e.png">

おおー！
ブラックジャックができています！
文章では表せられない嬉しいものがありますね。

クラス化についてですが、コメントで「クラスの恩恵を受けれない」と言われていますが、一応やってみようと思います。
あと、もっと関数を細かくしたほうがいいとコメントで教えていただいたのでそれも明日やる。
##ロベールのC++
###昨日の訂正 スコープ解決演算子
昨日私が書いた記事に誤りがありました。
`::`が予想以上に複雑で頭が追いついていけてないです。
明日、じっくり調べようとおもいます。
今日は疲れたので、ロベールお休みします。
#終わりに
ブラックジャックが遊べるようになって嬉しい！
おやすみなさい