新Swiftで行こう…第77回「ポーカー15解説」 田部井保
トランプの基本部分を別クラスに外出しします。
//
// ViewController.swift
// Poker
//
// Created by 保 Tabei on 2024/10/03.
//
import UIKit
class Card {
///マーク保持配列
let mark: [String] = ["♣️","♦️","❤️","♠️"]
///ナンバー保持配列
let number: [String] = ["A","2","3","4","5","6","7","8","9","T","J","Q","K"]
///カード枚数保持定数
enum EnumCard {
static let Count = 52
}
///既に出ているかチェックする、出ていたらtrue
///出ていなかったらfalse、とりあえず
///EnumCard.Count枚分falseで埋める
var check = [Bool](repeating: false, count: EnumCard.Count)
///残り枚数を保持する変数
var count = EnumCard.Count
///リセット
func reset() {
//全カード出ていない事に
for i in 0 ..< EnumCard.Count {
check[i] = false
}
//残り枚数52枚
count = EnumCard.Count
}
///カードを引いてカード表面文字列とカード番号を返す
func put() -> (String?, Int) {
//今回引いたカードを特定する変数
var card = 0
//乱数を0から残り枚数−1の範囲で発生させる
let randInt = Int.random(in: 0 ..< count)
//発生させた乱数分ループ
for j in 0 ... randInt {
//カードが既に出ていたらcard + 1
while check[card] {
card += 1
}
//jがrandIntに達していなければcard + 1
if j < randInt {
card += 1
}
}
//新たに出たカード既に出ているとマークする
check[card] = true
//残り枚数を1減算
count -= 1
//カード一時保持変数
var strCard: String
//ジョーカーの場合
if card == 52 {
strCard = "JK"
//ジョーカー以外の場合
} else {
//カード一時保持変数にマークとナンバーを保持
strCard = mark[card / 13] + number[card % 13]
}
//カード表面文字列とカード番号を返す
return (strCard, card)
}
//カード番号を指定するとそのカードの表面文字列を返す
func disp(i: Int) -> String {
//一時保持文字列
var strCard: String
//マークとナンバーをセットする
strCard = mark[i / 13] + number[i % 13]
//表面文字列を返す
return strCard
}
}
class ViewController: UIViewController {
///再配布ボタン
@IBOutlet weak var btnRe: UIButton!
///点数ラベル
@IBOutlet weak var lblPoint: UILabel!
///賭け点ラベル
@IBOutlet weak var lblBet: UILabel!
///配布時点役表示ラベル
@IBOutlet weak var lblBefore: UILabel!
///カードクラスを扱える様にする
var cardObj:Card = Card()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//ポイント10点
point = 10
//賭け点0点
betPoint = 0
//点数表示
display()
//5枚分のラベルを配置する
for i in 0 ..< 5 {
//ラベルを生成する
let lbl = UILabel(frame: CGRectMake(0, 0, 50, 21))
//ラベルの中心を指定する
lbl.center = CGPointMake(100 + 50 * CGFloat(i), 250)
//ラベルのテキスト配置を中央にする
lbl.textAlignment = NSTextAlignment.center
//ラベルの表示をカードが裏になっている様にする
lbl.text = "⬛️"
//ラベル配列に追加する
eachCard += [lbl]
//ラベルを画面上に配置する
self.view.addSubview(lbl)
//選択ボタンを生成する
let btn = UIButton(frame: CGRectMake(0, 0, 50, 21)) as UIButton
//ボタンの中心を指定する
btn.center = CGPointMake(100 + 50 * CGFloat(i), 300)
//ボタンのタイトルを「off」にする
btn.setTitle("off", for: .normal)
//ボタンのタイトルの色をグレーにする
btn.setTitleColor(UIColor.systemGray, for: .normal)
//ボタンが押された時に「btnTapped」関数が呼ばれるようにする
btn.addTarget(self, action: #selector(btnTapped), for: .touchUpInside)
//ボタンのタグに0から4の目印を付ける
btn.tag = i
//選択ボタン配列に追加する
eachButton += [btn]
//選択ボタンを画面上に配置する
self.view.addSubview(btn)
}
}
///選択ボタンが押された時に動作する関数
@objc func btnTapped(sender: UIButton) {
//もしタイトルが「off」なら
if sender.currentTitle == "off" {
//タイトルを「on」にして
sender.setTitle("on", for: .normal)
//選択ボタンの所のカードを裏にする
eachCard[sender.tag].text = "⬛️"
//もしタイトルが「on」なら
} else {
//タイトルを「off」にして
sender.setTitle("off", for: .normal)
//選択ボタンの所のカードを表にする
//カード番号はタグに入れてある
let card = eachCard[sender.tag].tag
//カード一時保持変数
var strCard: String
//ジョーカーの場合
if card == 52 {
strCard = "JK"
//ジョーカー以外の場合
} else {
//カード一時保持変数にマークとナンバーを保持
strCard = cardObj.disp(i: card)
}
//選択ボタンの所のカードを表示
eachCard[sender.tag].text = strCard
}
}
///ラベル配列
var eachCard: [UILabel] = []
///選択ボタン配列
var eachButton: [UIButton] = []
///役判定関数
func yaku() -> (Int, String) {
//チェック変数
var ck = 0
//基準カード
for i in 0 ..< 4 {
//比較カード
for j in i + 1 ..< 5 {
//ナンバーが同じなら
if eachCard[i].tag % 13 == eachCard[j].tag % 13 {
//チェック変数を+1する
ck += 1
}
}
}
//リターン倍率
var mul = 0
//役名
var msg:String = ""
//ペアなら
if ck > 0 {
//チェック変数の値によりペア名を指定する
switch ck {
case 1:
//ワンペア
mul = 1
msg = "ワンペア"
case 2:
//ツーペア
mul = 2
msg = "ツーペア"
case 3:
//スリーカード
mul = 3
msg = "スリーカード"
case 4:
//フルハウス
mul = 6
msg = "フルハウス"
case 6:
//フォーカード
mul = 8
msg = "フォーカード"
default:
mul = 0
}
//ペア以外
} else {
//フラッシュ変数とりあえずフラッシュ
var chk = true
//1番目(0の位置)のマークを取得(0〜3)
let mark: Int = eachCard[0].tag / 13
//2番目から5番目の位置のマークと比較
for i in 1 ..< 5 {
//1番目のマークと2〜5番目のマークを比較して違っていたら
if mark != eachCard[i].tag / 13 {
//フラッシュ変数をfalse(フラッシュではない)に
chk = false
//ループを終了
break
}
}
//エースがあるか
var isAce = false
//キングがあるか
var isKing = false
//5枚のカードについて調べる
for i in 0 ..< 5 {
//エースがあったら
if eachCard[i].tag % 13 == 0 {
isAce = true
}
//キングがあったら
if eachCard[i].tag % 13 == 12 {
isKing = true
}
}
//エースとキング両方あったらup変数をtrueに
let up = isAce && isKing
//最小値変数を宣言
var min = 0
//最大値変数を宣言
var max = 0
//1番目から5番目までループ
for i in 0 ..< 5 {
//変数にそのカードのナンバーを入れる
var val = eachCard[i].tag % 13
//エースとキング両方があった場合
if up {
//4以下のカードに
if val < 4 {
//13加える
val = val + 13
}
}
//1番目ならmin,maxにvalを入れる
if i == 0 {
min = val
max = val
//2番目以降
} else {
//変数が最小値より小さい時
if val < min {
//最小値を変数に
min = val
}
//変数が最大値より大きい時
if max < val {
//最大値を変数に
max = val
}
}
}
//ストレートなら
if max - min == 4 {
//ロイヤルなら
if max == 13 {
//フラッシュなら
if chk {
mul = 10
msg = "ロイヤルストレートフラッシュ"
//フラッシュでない
} else {
mul = 9
msg = "ロイヤルストレート"
}
//ロイヤルでない
} else {
//フラッシュなら
if chk {
mul = 7
msg = "ストレートフラッシュ"
//フラッシュでない
} else {
mul = 4
msg = "ストレート"
}
}
//ストレートでない
} else {
//フラッシュなら
if chk {
mul = 5
msg = "フラッシュ"
//フラッシュでない
} else {
//ブタ
mul = 0
msg = "ブタ"
}
}
}
//倍率を返す
return (mul, msg)
}
///点数計算関数
func flush() {
//リターン倍率
var mul = 0
//メッセージ保持変数
var msg: String
//リターン倍率
(mul, msg) = yaku()
//タイトル保持変数
var title: String
//メッセージをタイトルに入れる
title = msg
//実際の倍率(リターン倍率 ー 配った時の役)
var realMul = mul - beforeMul
//マイナスは無い
if realMul < 0 {
realMul = 0
}
//ポイントに賭け点×実際の倍率を足す
point = point + betPoint * realMul
//賭け点0
betPoint = 0
//ポイントが0ならゲームオーバー
if point == 0 {
title = "ゲームオーバー "
msg = "ゲームオーバー "
//最初からゲームを続けられる
point = 10
}
//ダイアログを表示
let alert = UIAlertController()
alert.title = title
alert.message = msg + "です"
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true, completion: nil)
//点数表示
display()
}
//配った時の役
var beforeMul = 0
///開始ボタン押下時処理
@IBAction func btnGoTouch(_ sender: Any) {
//リセットする
cardObj.reset()
//5枚引く
for i in 0 ..< 5 {
//カードを引いてiの位置に出力する
(eachCard[i].text, eachCard[i].tag) = cardObj.put()
}
//役判定
//flush()
//再配布ボタン有効
btnRe.isEnabled = true
//役名
var msg:String
//配った時の役を取得
(beforeMul, msg) = yaku()
//基準の役名、倍率を表示
lblBefore.text = "基準:" + msg + ":" + beforeMul.description
}
///再配布ボタン押下時処理
@IBAction func btnReTouch(_ sender: Any) {
//再配布ボタン無効
btnRe.isEnabled = false
//5枚分調べる
for i in 0 ..< 5 {
//選択ボタンのタイトルがonなら
if eachButton[i].currentTitle == "on" {
//カードを引いてiの位置に出力する
(eachCard[i].text, eachCard[i].tag) = cardObj.put()
//選択ボタンのタイトルをoffにする
eachButton[i].setTitle("off", for: .normal)
}
}
//役判定
flush()
}
///ポイント
var point = 0
///賭け点
var betPoint = 0
///点数表示
func display() {
//ポイント表示
lblPoint.text = point.description
//賭け点表示
lblBet.text = betPoint.description
}
///賭け点プラスボタン押下時処理
@IBAction func btnPlusTouch(_ sender: Any) {
//ポイント0点より大の時
if point > 0 {
//ポイント−1
point -= 1
//賭け点+1
betPoint += 1
}
//点数表示
display()
}
///賭け点マイナスボタン押下時処理
@IBAction func btnMinusTouch(_ sender: Any) {
//賭け点0点より大の時
if betPoint > 0 {
//賭け点−1
betPoint -= 1
//ポイント+1
point += 1
}
//点数表示
display()
}
}
Cardクラスを作って、そこにトランプの基本部分を移しました。
マーク、ナンバーの文字列、カード全枚数、既出チェック、残り枚数をCardクラスに移し、リセット関数(ゲーム開始時点の状態にする)、カードを引く関数、指定したカード番号の表示文字列を取得する関数を用意しました。
通常版の後半では、Cardクラスを別ファイルにしています。通常版を参考にして、Cardクラスを別ファイルにしてみて下さい。