(DAY15) 俺と学ぶRust言語~BlackJack（1）~

# 今日の内容
- ゲーム作り前編：「BlackJack」

# はじめに
[前回](https://qiita.com/FUKAch/items/acf8b1761b3c0e755091)は構造体について学びました。さて、ここまでを通して様々なことを学んできましたが、今日はその知識を生かして簡単な「BlackJack」ゲームを作成したいと思います。今日中に終わらなかったので、明日には完成させます。

# BlackJackとは
BlackJack(ブラックジャック)はカードゲームで、ディーラーとプレイヤーの対戦型ゲームです。割と簡単なルールなので、プログラミング初心者でも簡単に作ることができます。
- ルール
    -  カードはトランプカード（♡、♢、♧、♤の四種類がそれぞれ1~13まである）
    -  プレイヤーはディーラーよりカードの合計が「21」に近ければ勝ち
    -  ただし、「21」を超えたらその時点で負け
    -  点数は、2~10はそのまま、「J・Q・K」は全て10点と数える
    -  「A」は1点か11点のどちらか、都合の良い方で数える

- 進行
    - カードを2枚ずつディーラーとプレイヤーに配る
    - ディーラーは2枚のうち1枚を表向き（アップカードと呼ぶ）にする
    - プレイヤーのカード2枚が「A」と「10・J・Q・K」のどれか1つの組み合わせなら、「ナチュラルブラックジャック」となる -> もしディーラーも「ナチュラルブラックジャック」であれば引き分け、でなければプレイヤーの勝ち
    - 「ナチュラルブラックジャック」でないなら、プレイヤーは「Hit」と「Stand」の二択を選択する
    - 「Hit」->カードをもう1枚ひく
    - 「Stand」->その時点での合計点で勝負する
    - 「Hit」をしても21を超えなければ、何回でも「Hit」できる
    - 「Hit」をして21を超えたら、「bust」となり、その時点でプレイヤーの負けとなる
    - プレイヤーが行動を終えると、ディーラーは伏せられたカード「ホールカード」を表向きにする
    - ディーラーは合計が17点以上になるまで引き続ける（17点以上では引くことはできない）
    - ディーラーがbustしたら、bustしてないプレイヤーの勝ち

プログラムで書こうとしても、そんなに難しくないルールです。ディーラー(CPU)の選択肢もランダムで選んで支障ないので、さっそく書いていきます。

# BlackJack!
ブラックジャックを作るために、新しいプロジェクトを作成します。
```:cmd
cargo new blackjack
cd blackjack
```
Cargo.toml内にrandクレートの説明を追加します。
```toml:Cargo.toml
[package]
name = "blackjack"
version = "0.1.0"
edition = "2021"

[dependencies]
rand = "0.8.5"
```
`cargo build`して以下のようになれば導入完了です。
```:cmd
cargo build
   Compiling cfg-if v1.0.0
   Compiling byteorder v1.5.0
   Compiling getrandom v0.2.15
   Compiling rand_core v0.6.4
   Compiling zerocopy v0.7.35
   Compiling ppv-lite86 v0.2.20
   Compiling rand_chacha v0.3.1
   Compiling rand v0.8.5
   Compiling blackjack v0.1.0 (C:\Users\username\blackjack)
   Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.79s
```
ゲームの進行に沿ってプログラムを書いていきます。

まずは、カードを2枚ずつプレイヤーとディーラー（CPU）に配ります。
カードですが、今回は可読性重視のタプル型ベクタを用意しました。コピペしてください。
また、今回は2デッキ分使います。
```rust
fn main() {
   let mut cards: Vec<(char, u32)> = vec![
        ('♡',1),('♡',2),('♡',3),('♡',4),('♡',5),('♡',6),('♡',7),('♡',8),('♡',9),('♡',10),('♡',11),('♡',12),('♡',13),
        ('♢',1),('♢',2),('♢',3),('♢',4),('♢',5),('♢',6),('♢',7),('♢',8),('♢',9),('♢',10),('♢',11),('♢',12),('♢',13),
        ('♧',1),('♧',2),('♧',3),('♧',4),('♧',5),('♧',6),('♧',7),('♧',8),('♧',9),('♧',10),('♧',11),('♧',12),('♧',13),
        ('♤',1),('♤',2),('♤',3),('♤',4),('♤',5),('♤',6),('♤',7),('♤',8),('♤',9),('♤',10),('♤',11),('♤',12),('♤',13),

        ('♡',1),('♡',2),('♡',3),('♡',4),('♡',5),('♡',6),('♡',7),('♡',8),('♡',9),('♡',10),('♡',11),('♡',12),('♡',13),
        ('♢',1),('♢',2),('♢',3),('♢',4),('♢',5),('♢',6),('♢',7),('♢',8),('♢',9),('♢',10),('♢',11),('♢',12),('♢',13),
        ('♧',1),('♧',2),('♧',3),('♧',4),('♧',5),('♧',6),('♧',7),('♧',8),('♧',9),('♧',10),('♧',11),('♧',12),('♧',13),
        ('♤',1),('♤',2),('♤',3),('♤',4),('♤',5),('♤',6),('♤',7),('♤',8),('♤',9),('♤',10),('♤',11),('♤',12),('♤',13),
    ];
}
```
なぜベクタにしたかというと、要素数を動的に変更できるからです。当然ですが、現実世界でデッキを用意してカードを1枚引いたとき、デッキからは1枚減りますよね？それを忠実に再現することで、プログラムの動作を理解しやすいものにしました。
次に、randクレートを利用してベクタから1枚取る関数「rnd_card」を作成します。デッキからカードを取りたいとき、必要な枚数分この関数を呼び出せば良くなるわけです。
```rust
use rand::prelude::*;

fn rnd_card(cards: &mut Vec<(char, u32)>) -> (char, u32){
    //ベクタcardsから要素を1つ取得し、ベクタからそれを削除してから返す。
    let x = thread_rng().gen_range(0..cards.len());
    let card = cards[x];
    cards.remove(x);
    card
}
```
タプル(char, u32)型ベクタを引数とし、タプル(char, u32)を返す関数です。つまり、main関数内で、引数に「&mut cards」を渡して呼び出すと、その要素を返す関数にします。
中身では「0 < x < ベクタの長さ-1」の範囲で乱数を生成し、xに入れています。
変数cardにベクタのx番目を入れ、ベクタのx番目を消し、変数cardを返します。

main関数内で「player」という変数にcardsから1枚入れたければ、以下のようにすると良いわけです。
```rust
fn main() {
    let mut cards: Vec<(char, u32)> = vec![
        ('♡',1),('♡',2),('♡',3),('♡',4),('♡',5),('♡',6),('♡',7),('♡',8),('♡',9),('♡',10),('♡',11),('♡',12),('♡',13),
        ('♢',1),('♢',2),('♢',3),('♢',4),('♢',5),('♢',6),('♢',7),('♢',8),('♢',9),('♢',10),('♢',11),('♢',12),('♢',13),
        ('♧',1),('♧',2),('♧',3),('♧',4),('♧',5),('♧',6),('♧',7),('♧',8),('♧',9),('♧',10),('♧',11),('♧',12),('♧',13),
        ('♤',1),('♤',2),('♤',3),('♤',4),('♤',5),('♤',6),('♤',7),('♤',8),('♤',9),('♤',10),('♤',11),('♤',12),('♤',13),

        ('♡',1),('♡',2),('♡',3),('♡',4),('♡',5),('♡',6),('♡',7),('♡',8),('♡',9),('♡',10),('♡',11),('♡',12),('♡',13),
        ('♢',1),('♢',2),('♢',3),('♢',4),('♢',5),('♢',6),('♢',7),('♢',8),('♢',9),('♢',10),('♢',11),('♢',12),('♢',13),
        ('♧',1),('♧',2),('♧',3),('♧',4),('♧',5),('♧',6),('♧',7),('♧',8),('♧',9),('♧',10),('♧',11),('♧',12),('♧',13),
        ('♤',1),('♤',2),('♤',3),('♤',4),('♤',5),('♤',6),('♤',7),('♤',8),('♤',9),('♤',10),('♤',11),('♤',12),('♤',13),
    ];
    let player = rnd_card(&mut cards);
    println!("{:?}", player);
}
```
しかしこれでは、プレイヤーの手札が2枚、3枚も増えるたびに変数を用意しなくてはなりません。そこで、またベクタを使います。ディーラーの分もベクタを用意し、「プレイヤーのベクタの0番目＝(♡のA)」というように実装します。
```rust
fn main() {
    let mut cards: Vec<(char, u32)> = vec![
        ('♡',1),('♡',2),('♡',3),('♡',4),('♡',5),('♡',6),('♡',7),('♡',8),('♡',9),('♡',10),('♡',11),('♡',12),('♡',13),
        ('♢',1),('♢',2),('♢',3),('♢',4),('♢',5),('♢',6),('♢',7),('♢',8),('♢',9),('♢',10),('♢',11),('♢',12),('♢',13),
        ('♧',1),('♧',2),('♧',3),('♧',4),('♧',5),('♧',6),('♧',7),('♧',8),('♧',9),('♧',10),('♧',11),('♧',12),('♧',13),
        ('♤',1),('♤',2),('♤',3),('♤',4),('♤',5),('♤',6),('♤',7),('♤',8),('♤',9),('♤',10),('♤',11),('♤',12),('♤',13),

        ('♡',1),('♡',2),('♡',3),('♡',4),('♡',5),('♡',6),('♡',7),('♡',8),('♡',9),('♡',10),('♡',11),('♡',12),('♡',13),
        ('♢',1),('♢',2),('♢',3),('♢',4),('♢',5),('♢',6),('♢',7),('♢',8),('♢',9),('♢',10),('♢',11),('♢',12),('♢',13),
        ('♧',1),('♧',2),('♧',3),('♧',4),('♧',5),('♧',6),('♧',7),('♧',8),('♧',9),('♧',10),('♧',11),('♧',12),('♧',13),
        ('♤',1),('♤',2),('♤',3),('♤',4),('♤',5),('♤',6),('♤',7),('♤',8),('♤',9),('♤',10),('♤',11),('♤',12),('♤',13),
    ];
    //playerとcpuの手札
    let mut player: Vec<(char, u32)> = Vec::new();
    let mut cpu: Vec<(char, u32)> = Vec::new();
}
```
そして、playerベクタとcpuベクタにrnd_cardの結果をそれぞれ2回追加します。
```rust
fn main() {
    ...
    //playerとcpuの手札
    let mut player: Vec<(char, u32)> = Vec::new();
    let mut cpu: Vec<(char, u32)> = Vec::new();
    //playerとcpuの初期のカードを手札に加える
    player.push(rnd_card(&mut cards));
    player.push(rnd_card(&mut cards));
    cpu.push(rnd_card(&mut cards));
    cpu.push(rnd_card(&mut cards));
}
```
次にこれを表示する関数を作ります。playerとcpuを渡すことで全部表示させたり、一部を渡すことでそこだけ表示させたりできるようにしました。
```rust
fn print_card(player: &Vec<(char, u32)>, cpu: &Vec<(char, u32)>) {
    print!("You: ");
    for i in 0..player.len() {
        let a =(player[i].1).to_string();
        print!("{}{}", player[i].0, match player[i].1 { 1=>"A", 11=>"J", 12=>"Q", 13=>"K", _=> a.as_str()});
        if i != player.len()-1 { print!(", ") }
    }
    print!("\nCPU: ");
    for i in 0..cpu.len() {
        let a =(cpu[i].1).to_string();
        print!("{}{}", cpu[i].0, match cpu[i].1 { 1=>"A", 11=>"J", 12=>"Q", 13=>"K", _=> a.as_str()});
        if i != cpu.len()-1 { print!(", ") }
    }
    println!("");
}
```
例えば、player全部を表示させたければplayerベクタの参照を、cpuの1つ目だけを表示させたければ引数に`&vec![cpu[0]]`を与えればよいです。
最初はプレイヤーのカード2枚全部と、CPUのカード1枚を表示させたいので、main関数で以下のように呼びます。
```rust
fn main() {
    ...
    print_card(&player, &vec![cpu[0]]);
}
```
さて、ここまででカードを2枚ずつプレイヤーとディーラー（CPU）に配ることができました。

次にプレイヤーのカードの合計点を計算し、もしナチュラルブラックジャックであればディーラーのホールカードを表にして比べるプログラムを実装します。
```rust
...
fn count_point(mut point: u32, vec: &Vec<(char, u32)>, player: bool) -> u32 {
    for i in vec {
        if player == true {
            if i.1 == 1 {
                println!("Which would you choose for 'A': 1 or 11?");
                loop{
                    let mut a = String::new();
                    print!("> ");
                    stdout().flush().unwrap();
                    stdin().read_line(&mut a).expect("Can't read it!");
                    let a:u32 = a.trim().parse().expect("Type a number!");
                    if a==1 || a==11 {
                        if a == 11 { point+=10; }
                        break;
                    }
                }
            }
        } else if player == false {
            if i.1 == 1 && point+11<=21{ point+=10;}
        }
        point += match i.1 { 11=>10, 12=>10, 13=>10, _=> i.1 };
    }
    point
}

fn main() {
    let point:(u32, u32) = (0, 0);
    ...
    //現在の合計点を表示
    point.0 = count_point(point.0, &player, true);
    point.1 = count_point(point.1, &cpu, false);
    println!("Current your point: {}", point.0);
}
```
変数pointは、「(プレイヤーのポイント, CPUのポイント)」になります。count_point関数は引数に応じてプレイヤーとCPUのどちらのポイントも計算することができます。
さらにプレイヤーの場合、3つめの引数はtrueとし、もし「A」であれば1点か11点かを選ぶことができます。もしCPUであれば、「A」は21を超えそうなら1点で、でなければ11点にするとしています。

# おわりに
すみませんが、今日中に終わらなかったので、区切りの良いここまでとします。明日には完成させたいです..💦
次回もBlackJackを作っていきます。最近忙しいので毎日投稿大変ですが、これからも頑張っていきたいと思います。
ご清聴ありがとうございました。

# 追記
一応完成したものがこちらです。
```rust
use std::io::*;
use rand::prelude::*;

fn rnd_card(cards: &mut Vec<(char, u32)>) -> (char, u32){
    //ベクタcardsから要素を1つ取得し、ベクタからそれを削除してから返す。
    let x = thread_rng().gen_range(0..cards.len());
    let card = cards[x];
    cards.remove(x);
    card
}

fn print_card(player: &Vec<(char, u32)>, cpu: &Vec<(char, u32)>) {
    print!("You: ");
    for i in 0..player.len() {
        let a =(player[i].1).to_string();
        print!("{}{}", player[i].0, match player[i].1 { 1=>"A", 11=>"J", 12=>"Q", 13=>"K", _=> a.as_str()});
        if i != player.len()-1 { print!(", ") }
    }
    print!("\nCPU: ");
    for i in 0..cpu.len() {
        let a =(cpu[i].1).to_string();
        print!("{}{}", cpu[i].0, match cpu[i].1 { 1=>"A", 11=>"J", 12=>"Q", 13=>"K", _=> a.as_str()});
        if i != cpu.len()-1 { print!(", ") }
    }
    println!("");
}

fn count_point(mut point: u32, vec: &Vec<(char, u32)>, player: bool) -> u32 {
    for i in vec {
        if player == true {
            if i.1 == 1 {
                println!("Which would you choose for 'A': 1 or 11?");
                loop{
                    let mut a = String::new();
                    print!("> ");
                    stdout().flush().unwrap();
                    stdin().read_line(&mut a).expect("Failed!");
                    let a:u32 = a.trim().parse().expect("Type a number!");
                    if a==1 || a==11 {
                        if a == 11 { point+=10; }
                        break;
                    }
                }
            }
        } else if player == false {
            if i.1 == 1 && point+11<=21{ point+=10;}
        }
        point += match i.1 { 11=>10, 12=>10, 13=>10, _=> i.1 };
    }
    point
}

fn main() {
    //初期化
    let mut point: (u32, u32) = (0, 0);
    let mut cards: Vec<(char, u32)> = vec![
        ('♡',1),('♡',2),('♡',3),('♡',4),('♡',5),('♡',6),('♡',7),('♡',8),('♡',9),('♡',10),('♡',11),('♡',12),('♡',13),
        ('♢',1),('♢',2),('♢',3),('♢',4),('♢',5),('♢',6),('♢',7),('♢',8),('♢',9),('♢',10),('♢',11),('♢',12),('♢',13),
        ('♧',1),('♧',2),('♧',3),('♧',4),('♧',5),('♧',6),('♧',7),('♧',8),('♧',9),('♧',10),('♧',11),('♧',12),('♧',13),
        ('♤',1),('♤',2),('♤',3),('♤',4),('♤',5),('♤',6),('♤',7),('♤',8),('♤',9),('♤',10),('♤',11),('♤',12),('♤',13),

        ('♡',1),('♡',2),('♡',3),('♡',4),('♡',5),('♡',6),('♡',7),('♡',8),('♡',9),('♡',10),('♡',11),('♡',12),('♡',13),
        ('♢',1),('♢',2),('♢',3),('♢',4),('♢',5),('♢',6),('♢',7),('♢',8),('♢',9),('♢',10),('♢',11),('♢',12),('♢',13),
        ('♧',1),('♧',2),('♧',3),('♧',4),('♧',5),('♧',6),('♧',7),('♧',8),('♧',9),('♧',10),('♧',11),('♧',12),('♧',13),
        ('♤',1),('♤',2),('♤',3),('♤',4),('♤',5),('♤',6),('♤',7),('♤',8),('♤',9),('♤',10),('♤',11),('♤',12),('♤',13),
    ];
    //playerとcpuの手札
    let mut player: Vec<(char, u32)> = Vec::new();
    let mut cpu: Vec<(char, u32)> = Vec::new();
    //playerとcpuの初期のカードを手札に加える
    player.push(rnd_card(&mut cards));
    player.push(rnd_card(&mut cards));
    cpu.push(rnd_card(&mut cards));
    cpu.push(rnd_card(&mut cards));
    
    print_card(&player, &vec![cpu[0]]);
    //現在の合計点を表示
    point.0 = count_point(point.0, &player, true);
    point.1 = count_point(point.1, &cpu, false);
    //ナチュラルブラックジャックであれば、引き分けか勝ちか判定する。
    if point.0 == 21 {
        println!("BlackJack!");
        print_card(&player, &cpu);
        if point.1  == 21 {
            println!("draw!");
        } else {
            println!("You win!");
        }
        return;
    }
    //HitかStandかを繰り返す
    println!("Current your point: {}", point.0);
    loop {
        let mut a = String::new();
        println!("Hit or Stand");
        print!("> ");
        stdout().flush().unwrap();
        stdin().read_line(&mut a).expect("Failed");
        if a.trim() == "Hit" || a.trim() == "Stand" {
            if a.trim() == "Hit" {
                player.push(rnd_card(&mut cards));
                print_card(&player, &vec![cpu[0]]);
            } else {
                break;
            }
        } else {
            continue;
        }
        point.0 = count_point(point.0, &vec![player[player.len()-1]], true);
        println!("Current your point: {}", point.0);
        if point.0 > 21 {
            println!("You lose..");
            return;
        }
    }
    //プレイヤー行動終了。
    //CPUがホールカードを公開する。
    print_card(&player, &cpu);
    println!("Current cpu's point: {}", point.1);
    //CPUが17点以上になるまで引き続ける。
    loop {
        print!("CPU: ");
        if point.1 < 17 {
            println!("Hit");
            cpu.push(rnd_card(&mut cards));
            point.1 = count_point(point.1, &vec![cpu[cpu.len()-1]], true);
            print_card(&player, &cpu);
            println!("Current cpu's point: {}", point.1);
            if point.1 >21 {
                println!("You win!");
                return;
            }
        } else {
            println!("Stand");
            break;
        }
    }
    //CPU行動終了。
    //ポイントを比較し、高い方の勝ち。
    if point.1 > point.0 {
        println!("You lose..");
    } else if point.1 < point.0 {
        println!("You win!")
    } else {
        println!("draw!");
    }
}
```