AutoHotkeyのコードの書き方に慣れるために簡単なプログラムを作ってみよう第二弾。
前回はおみくじとじゃんけんを作ってみました。
今回はトランプのハイアンドローゲームを作ります。
ルールは単純で、山から取り出された1枚のカードに対して、次に取り出されるカードの数字がそれより高いか低いかを当てるものです。
一番高いのがA、一番低いのが2です。引き分け(同じ値)は勝ちにも負けにもなりません。
出来上がりはこうなります。ダイアログに従って入力したり答えたりしてゲームが進みます。
1か2で選択肢を入力
結果が表示されます
継続するか否かを選択
終えた時点の記録が表示されます
さて、まずトランプのデータを作ります。各カードのデータとして必要なものは、柄(クラブ、ダイヤ、ハート、スペード)とランク(2~A)、ランクの順番(1~13)です。(ハイアンドローはAが強く2が弱いので2~Aの順番で作ります。)
;配列で柄とランクのデータを用意する
suits := ["クラブ","ダイヤ","ハート","スペード"]
rank := ["2","3","4","5","6","7","8","9","10","J","Q","K","A"]
cards := [] ;各カードのデータを入れる配列を用意する
Loop {
;全ての柄について繰り返し処理
suit := suits[A_Index] ;A_Indexは現在のループが何回目かという数字が入った変数
Loop {
;各柄の中で全てのランクについて繰り返し処理
cards.Insert(Object("suit",suit,"rank",rank[A_Index],"num",A_Index)) ;各カードのデータを配列に追加する
If (A_Index >= 13)
Break ;13枚処理した時点で繰り返し終了
}
If (A_Index >= 4)
Break ;柄4種類処理した時点で繰り返し終了
}
処理を終えた時点で、配列cardsには、suitキーに柄、rankキーにランク、numキーに順番(=カードの強さ)がプロパティとして格納されたオブジェクトが入っています。例えば、52枚中の14番目に処理されている「ダイヤの2」なら、次のように値が入っているということになります。
cards[14].suit = ダイヤ
cards[14].rank = 2
cards[14].num = 1
このトランプを生成する一連を含めて関数を作ります。他のトランプゲームも作るかもしれないので、引数でゲーム名を指定してゲーム用の別の関数を呼び出せるようにします。(英語で「トランプをする」は「play cards」ですが、日本人的にひと目でわかるように関数名は「playTrump」とします。)
playTrump(game) {
suits := ["クラブ","ダイヤ","ハート","スペード"]
rank := ["2","3","4","5","6","7","8","9","10","J","Q","K","A"]
cards := []
Loop {
suit := suits[A_Index]
Loop {
cards.Insert(Object("suit",suit,"rank",rank[A_Index],"num",A_Index))
If (A_Index >= 13)
Break
}
If (A_Index >= 4)
Break
}
If(game="HighAndLow")
HighAndLow(cards) ;引数にトランプのデータを渡し、ハイアンドローゲームを呼び出す
}
vk1D & H::playTrump("HighAndLow") ;無変換キー+Hでハイアンドローゲームを発動
それではこれからハイアンドローゲーム本体を作ります。必要な処理の流れは以下の通り。
乱数を使い最初の1枚をランダムに選び、そのカードのデータは山から除く
ダイアログで予想が「ハイ」か「ロー」かを入力させる(有効な値になるまで入力を求める)
再び乱数を使いランダムに1枚選んで山から除き、先に選んだカードの強さと比較する
「ハイ」「ロー」の予想と合っていればwin回数を加算、引き分けならdraw回数を加算する
結果に応じたテキストをダイアログで表示する
まだ続けるかをダイアログで質問し、続けるなら2に戻り、続けないなら全体の結果を表示
こう書くと多いですね。それぞれの工程で処理は5行前後になります。
HighAndLow(cards){
title := "ハイアンドロー" ;ゲーム名を変数titleに入れておく(ダイアログのタイトルに設定する)
;1.乱数を使い最初の1枚をランダムに選び、そのカードのデータは山から除く
deck := cards.Clone() ;playTrump内で作ったトランプのデータを複製して使う(複製しなくてもいいと思うけど念の為)
Random, r,1,52 ;52枚のうち何枚目を選ぶかを乱数で決める
dealt := deck[r] ;配列deckのr番目のデータを変数dealtに入れる
deck.Remove(r) ;配列deckから今引いた1枚のデータを取り除く
win := 0 ;何回勝ったかを記録する変数winを用意しておく
draw := 0 ;何回引き分けだったかを記録する変数drawを用意しておく
Loop {
count := A_Index ;プレイ回数を変数countに入れておく
Loop {
;2.ダイアログで予想が「ハイ」か「ロー」かを入力させる(有効な値になるまで入力を求める)
InputBox, s,%title%,% "場のカード:" . dealt.suit . "の" . dealt.rank . "<code>n</code>n数字を入力してください。<code>n1:ハイ</code>n2:ロー"
If ErrorLevel <> 0
Return ;キャンセルの時はゲーム自体を終了する
If (s = 1 || s = 2)
Break ;有効な値が入力された時は質問の繰り返しから出る
}
;3.再び乱数を使いランダムに1枚選んで山から除き、先に選んだカードの強さと比較する
rest := 52 - A_Index ;残りの枚数を計算して変数restに入れる
Random, r,1,rest ;残りの枚数の中から1枚選ぶ
next := deck[r] ;今選んだデータを変数nextに入れる
deck.Remove(r) ;今引いたカードを山(配列deck)から取り除く
diff := dealt.num - next.num ;先に選んだカードと今選んだカードの差を計算して変数diffに入れる
;4.「ハイ」「ロー」の予想と合っていればwin回数を加算、引き分けならdraw回数を加算する
If ((s == 1 && diff < 0)||(s == 2 && diff > 0)) { ;予想が当たっている時
text := "Win!"
win := win + 1 ;win回数を加算する
} Else If (diff == 0) { ;引き分けの時
text := "Draw"
draw := draw + 1 ;draw回数を加算する
} Else { ;予想が外れている時
text := "Miss..."
}
;5.結果に応じたテキストをダイアログで表示する
MsgBox, 0,%title%,% "場のカード:" . dealt.suit . "の" . dealt.rank . "<code>n引いたカード:" . next.suit . "の" . next.rank . "</code>n`n" . text
dealt := next ;基準となるカードを新たに引いた方に変更する
If (A_Index >= 51)
Break ;山札がなくなるまでプレイしたら繰り返しから出る
;6.まだ続けるかをダイアログで質問し、続けるなら2に戻り、続けないなら全体の結果を表示
MsgBox, 4,%title%,% "現在" . A_Index . "回<code>n</code>nもう一度やりますか?`n(残り" . rest - 1 . "回)"
IfMsgBox, No
Break ;続けないなら繰り返しから出る(続ける場合はLoopの頭に戻って繰り返し)
}
;全体の結果を表示
MsgBox, 0,%title%,% count . "回の挑戦で" . win . "回勝ちました。(引き分け:" . draw . "回)`n(勝率:" . Round(win / (count - draw) * 100) . "%)"
}
もっとスマートになる余地があるかもしれませんが、現時点の私の力量ではこんな感じです。ひとまず動けばいいのです。
説明部分を省き、playTrump関数と合わせて整理すると次のようになります。以下の部分をahkファイルにコピペして無変換キー+Hでハイアンドローゲームをプレイすることができると思います。
vk1D & H::playTrump("HighAndLow") ;無変換キー+H
playTrump(game) {
suits := ["クラブ","ダイヤ","ハート","スペード"]
rank := ["2","3","4","5","6","7","8","9","10","J","Q","K","A"]
cards := []
Loop {
suit := suits[A_Index]
Loop {
cards.Insert(Object("suit",suit,"rank",rank[A_Index],"num",A_Index))
If (A_Index >= 13)
Break
}
If (A_Index >= 4)
Break
}
If(game="HighAndLow")
HighAndLow(cards)
}
HighAndLow(cards){
title := "ハイアンドロー"
deck := cards.Clone()
Random, r,1,52
dealt := deck[r]
deck.Remove(r)
win := 0
draw := 0
Loop {
count := A_Index
Loop {
InputBox, s,%title%,% "場のカード:" . dealt.suit . "の" . dealt.rank . "<code>n</code>n数字を入力してください。<code>n1:ハイ</code>n2:ロー"
If ErrorLevel <> 0
Return
If (s = 1 || s = 2)
Break
}
rest := 52 - A_Index
Random, r,1,rest
next := deck[r]
deck.Remove(r)
diff := dealt.num - next.num
If ((s == 1 && diff < 0)||(s == 2 && diff > 0)) {
text := "Win!"
win := win + 1
} Else If (diff == 0) {
text := "Draw"
draw := draw + 1
} Else {
text := "Miss..."
}
MsgBox, 0,%title%, % "場のカード:" . dealt.suit . "の" . dealt.rank . "<code>n引いたカード:" . next.suit . "の" . next.rank . "</code>n`n" . text
dealt := next
If (A_Index >= 51)
Break
MsgBox, 4,%title%,% "現在" . A_Index . "回<code>n</code>nもう一度やりますか?`n(残り" . rest - 1 . "回)"
IfMsgBox, No
Break
}
MsgBox, 0,%title%,% count . "回の挑戦で" . win . "回勝ちました。(引き分け:" . draw . "回)`n(勝率:" . Round(win / (count - draw) * 100) . "%)"
}
他にも作り方は様々あり得ると思います。自分ひとりのためのプログラムなら、自分が動かしたいように動けばそれでオッケーと思っています。