今日の内容
- ゲーム作り完結:「BlackJack」
はじめに
今回は、前回の続編で、BlackJackを作ります。本当なら昨日中に完成させる予定だったのですが、間に合いませんでした(´;ω;`)。
しかしながら、プログラム自体は粗悪品ではあるもの完成させることができたので、解説メインになります。
完成品
はじめに今回の完成品を紹介します。個人的に関数count_pointとprint_cardはもっと頑張れたなと思います..
次回からは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!");
}
}
実際に実行してみましょう。
You: ♤9, ♧A
CPU: ♧K
Which would you choose for 'A': 1 or 11?
>
配られたカードに「A」があったので、1点と11点のどちらを選ぶか聞かれています。
1点を選んでみます。
You: ♤9, ♧A
CPU: ♧K
Which would you choose for 'A': 1 or 11?
> 1
Current your point: 10
Hit or Stand
>
合計点の11点が表示されました。ここで、「Hit」と「Stand」の選択をします。21に近づきたいので、もちろん「Hit」を選択します。
You: ♤9, ♧A
CPU: ♧K
Which would you choose for 'A': 1 or 11?
> 1
Current your point: 10
Hit or Stand
> Hit
You: ♤9, ♧A, ♡4
CPU: ♧K
Current your point: 14
Hit or Stand
>
新しいカードが1枚配られ、この時点での合計点が表示されています。21を超えない限り、何回でも「Hit」できます。21を超えてしまうと、その時点で負けになります。
You: ♤9, ♧A
CPU: ♧K
Which would you choose for 'A': 1 or 11?
> 1
Current your point: 10
Hit or Stand
> Hit
You: ♤9, ♧A, ♡4
CPU: ♧K
Current your point: 14
Hit or Stand
> Hit
You: ♤9, ♧A, ♡4, ♤6
CPU: ♧K
Current your point: 20
Hit or Stand
>
さて、合計点が20点になりました。13分の1を引く自信がないのでここで「Stand」します。
You: ♤9, ♧A
CPU: ♧K
Which would you choose for 'A': 1 or 11?
> 1
Current your point: 10
Hit or Stand
> Hit
You: ♤9, ♧A, ♡4
CPU: ♧K
Current your point: 14
Hit or Stand
> Hit
You: ♤9, ♧A, ♡4, ♤6
CPU: ♧K
Current your point: 20
Hit or Stand
> Stand
You: ♤9, ♧A, ♡4, ♤6
CPU: ♧K, ♧6
Current cpu's point: 16
CPU: Hit
You: ♤9, ♧A, ♡4, ♤6
CPU: ♧K, ♧6, ♡6
Current cpu's point: 22
You win!
いきなり大量のログが表示されました!
最初にCPUのホールカードが表示され、その時点の合計点である16点が表示されます。
合計点が17以上でなければ、CPUは「Hit」を選び続けます。17以上かつ、21以下なら、CPUは「Stand」を選びます。もしCPUがbustしてしまうと、その時点でプレイヤーの勝ちとなります。
今回は、合計22点になってbustしてしまったので、「You win!」と表示されています。
遊び疲れたら、プログラムを見ていきましょう。
完成品を解読する
前回の続きから解読していきます。
前回は関数count_pointを追加して終わりました。これ以上追加する関数はないので安心してください。
//ナチュラルブラックジャックであれば、引き分けか勝ちか判定する。
if point.0 == 21 {
println!("BlackJack!");
print_card(&player, &cpu);
if point.1 == 21 {
println!("draw!");
} else {
println!("You win!");
}
return;
}
ポイントを表示したら、プレイヤーのポイントが21、つまりナチュラルブラックジャックかを判定します。
もしそうであれば「BlackJack!」と表示し、次にホールカードを表示して、CPUがナチュラルブラックかどうかを判定します。もしCPUもナチュラルブラックであれば、「draw!」つまり引き分けになります。でなければ、「You win!」プレイヤーの勝ちになります。「return」でmain関数を終わらせ、つまりプログラムを終了します。
ナチュラルブラックジャックでなければ次に進みます。
//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;
}
}
最初の「Current your point: 」はcount_point関数を呼び出した直後に置いたほうが良いかもしれません。
loop文の中身が重要です。ここでは、playerに「Hit」か「Stand」を選択させ、さらにbustしてないかも判定しています。もしbustしたら「You lose..」となり、しなければするかStandするまで「Hit」か「Stand」の選択ができます。
//プレイヤー行動終了。
//CPUがホールカードを公開する。
print_card(&player, &cpu);
println!("Current cpu's point: {}", point.1);
プレイヤーがbustせずに、Standしたらその時点でプレイヤーの行動は終了です。CPUはホールカードを表示します。
ここから、CPUのターンになります。
//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が17点以上になるまで引き続けています。もし17点以上であればStandします。17点未満ならHitをし、bustしたらその時点で「You win!」プレイヤーの勝ちになります。
//CPU行動終了。
//ポイントを比較し、高い方の勝ち。
if point.1 > point.0 {
println!("You lose..");
} else if point.1 < point.0 {
println!("You win!")
} else {
println!("draw!");
}
最後です。プレイヤーとCPUのどちらもがbustせずStandできたら実行されます。(もしbustしたらその時点でreturnして終わらせているため。)ここで、CPUも行動を終了し、互いのポイントを比較します。
これでRustをつかってBlackJackを作ることができました!
しかし、count_point関数とprint_card関数については、もっと簡単に書くことができると信じています。
おわりに
今日はここまでです。早かったですね。筆者はちゃんとプログラミングしましたよ!!
明日の内容は...明日をお楽しみに!
ご精読ありがとうございました。