新Swiftで行こう…第14回「トランプ4」 田部井保

目次 解説編

 前回の問題は、乱数を発生させる時に、何度も繰り返さなくてはいけないのを止める方法はという事でした。

 前回の答えの一例です。

//
//  ViewController.swift
//  Card
//
//  Created by 保 Tabei on 2024/09/01.
//

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var lblCard: UILabel!
    @IBOutlet weak var lblCount: UILabel!
    
    @IBOutlet weak var lblKA: UILabel!
    @IBOutlet weak var lblK2: UILabel!
    @IBOutlet weak var lblK3: UILabel!
    @IBOutlet weak var lblK4: UILabel!
    @IBOutlet weak var lblK5: UILabel!
    @IBOutlet weak var lblK6: UILabel!
    @IBOutlet weak var lblK7: UILabel!
    @IBOutlet weak var lblK8: UILabel!
    @IBOutlet weak var lblK9: UILabel!
    @IBOutlet weak var lblKT: UILabel!
    @IBOutlet weak var lblKJ: UILabel!
    @IBOutlet weak var lblKQ: UILabel!
    @IBOutlet weak var lblKK: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    let mark: [String] = ["♣️","♦️","❤️","♠️"]
    let number: [String] = ["A","2","3","4","5","6","7","8","9","T","J","Q","K"]

    enum EnumCard {
        static let Count = 13
    }
    
    var check: [Bool] = [Bool](repeating: false, count: EnumCard.Count)
    
    var count = EnumCard.Count
    
    @IBAction func btnGoTouch(_ sender: Any) {
        if count == 0 {
            for i in 0 ..< EnumCard.Count
            {
                check[i] = false
            }
            
            lblKA.text = ""
            lblK2.text = ""
            lblK3.text = ""
            lblK4.text = ""
            lblK5.text = ""
            lblK6.text = ""
            lblK7.text = ""
            lblK8.text = ""
            lblK9.text = ""
            lblKT.text = ""
            lblKJ.text = ""
            lblKQ.text = ""
            lblKK.text = ""

            let alert = UIAlertController()
            alert.title = "初期化"
            alert.message = "カードを配り直します"
            alert.addAction(UIAlertAction(title: "OK", style: .default))
            present(alert, animated: true, completion: nil)
            count = EnumCard.Count
        }
        else
        {
            var card = 0
            let randInt = Int.random(in: 0 ..< count)
            var i = 0
            repeat {
                while check[card] {
                    card += 1
                }
                if i == randInt {
                    break
                } else {
                    i += 1
                    card += 1
                }
            } while true
            
            check[card] = true
            
            count -= 1
            lblCount.text = count.description
            
            if card == 52 {
                lblCard.text = "JK"
            } else {
                lblCard.text = mark[card / 13] + number[card % 13]
            }
            
            switch card {
            case 0: lblKA.text = lblCard.text
            case 1: lblK2.text = lblCard.text
            case 2: lblK3.text = lblCard.text
            case 3: lblK4.text = lblCard.text
            case 4: lblK5.text = lblCard.text
            case 5: lblK6.text = lblCard.text
            case 6: lblK7.text = lblCard.text
            case 7: lblK8.text = lblCard.text
            case 8: lblK9.text = lblCard.text
            case 9: lblKT.text = lblCard.text
            case 10: lblKJ.text = lblCard.text
            case 11: lblKQ.text = lblCard.text
            default: lblKK.text = lblCard.text
            }
        }
    }
}

 ここは、非常に難しいので、どういうアルゴリズムかと言うと、乱数を発生させるのは残り枚数を最大として発生させ、既に出ているカードは飛ばして、残っているカードだけを対象にして、発生した乱数分数えて、そのカードを引いたカードとするものです。これだけだと難しいので、仮に♣️A、♣️2、♣️5、♣️7、♣️T、♣️Jが既に開かれているとして、6枚出ているので残りの7枚が最大となるように乱数を発生させます(0〜6を出す)。ここで例えば3が出たとすると、♣️A、♣️2は飛ばして、♣️3が0、♣️4が1、♣️5は飛ばして、♣️6が2、♣️7は飛ばして、♣️8が3で、今回開かれるのは♣️8となります。

            var card = 0

 今いるカードの位置を表します。0〜12の値を取ります。

            let randInt = Int.random(in: 0 ..< count)

 残り枚数を最大として、乱数を発生させる部分です。

            var i = 0

 発生した乱数分数える変数です。0〜残り枚数ー1の値を取ります。

            repeat {

            } while true

 ループさせています。これだけだとループを抜けられないので、途中のbreakでループを抜けています。

                while check[card] {
                    card += 1
                }

 既に開かれているカードを飛ばす部分です。cardだけを進めています。

                if i == randInt {
                    break
                } else {
                    i += 1
                    card += 1
                }

 もしカウント(i)が発生させた乱数になったらループを抜け、そうでないならカウントを一つ進め、カードも一つ進めます。

 中々難しい処理だと思います。もっと簡単に出来たら良いのですが。肝の部分はこうも書けます。

            var card = 0
            var randInt = Int.random(in: 0 ..< count)
            while randInt > 0 {
                while check[card] {
                    card += 1
                }
                randInt -= 1
                card += 1
            }
            while check[card] {
                card += 1
            }

 今回は、i を使わずに発生させた乱数の randInt を減らしていって、0になったら止めると言う処理にしています。ただ0になった瞬間の card のカードが既に表示されている場合があるので、最初のループを抜けた後、card が既に開かれているカードか調べて、まだ開かれていないカードが出て来るまでループを続けます。

 他の書き方です。

            var card = 0
            var randInt = Int.random(in: 0 ..< count)
            while randInt >= 0 {
                while check[card] {
                    card += 1
                }
                randInt -= 1
                if randInt >= 0 {
                    card += 1
                }
            }

 これは一つ前と近いのですが、生成した乱数を減らしていって、0になるまで減らします(先程のは1になるまで)。ループの最後で card を+1しているのですが、0になった時にも+1してしまうと目的の1つ次になってしまいます。

 ここは難しいので、先程の例で考えます。仮に♣️A、♣️2、♣️5、♣️7、♣️T、♣️Jが既に開かれているとして、6枚出ているので残りの7枚が最大となるように乱数を発生させます(0〜6を出す)。ここで例えば3が出たとすると、3>=0なのでループが続きます。card =0は♣️Aなので check[card]=trueでループが続きます。card=1となり、card=1は♣️2なので check[card]=trueでループが続きます。card=2となり、card=2は♣️3なので check[card]=falseとなり、ループを抜けます。ここで randIntが−1されて2となります。2>=0なので card=3となります。

2>=0なのでループが続きます。card=3は♣️4なので check[card]=falseとなりループは抜けます。randIntが−1されて1となります。1>=0なので card=4となります。

1>=0なのでループが続きます。card=4は♣️5なので check[card]=trueでループが続きます。card=5となり、card=5は♣️6なので check[card]=falseでループを抜けます。ここでrandIntが−1されて0となります。0>=0なので card=6となります。

0>=0なのでループが続きます。card=6は♣️7なので check[card]=trueでループが続きます。card=7となり、card=7は♣️8なので check[card]=falseでループを抜けます。ここでrandIntが−1されて−1となります。−1>=0ではないので card はそのままです。

−1>=0でないのでループを終了します。こうして card=7となり、♣️8が引かれました。

 この様にも書けます。

            var card = 0
            let randInt = Int.random(in: 0 ..< count)
            for i in 0 ... randInt {
                while check[card] {
                    card += 1
                }
                if i < randInt {
                    card += 1
                }
            }

 同じ事を実現させるのに書き方は沢山あります。

 さて、♣️だけではなくて、他のマークも出したい所ですが、ラベルを並べるのだけでも大変です。そこでラベルをコードから生成させます。⬛️は「しかく」で出て来ます。

 コードから生成させる例です。とりあえず♣️だけです。

//
//  ViewController.swift
//  Card
//
//  Created by 保 Tabei on 2024/09/01.
//

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var lblCard: UILabel!
    @IBOutlet weak var lblCount: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        for i in 0 ..< EnumCard.Count {
            let lbl = UILabel(frame: CGRectMake(0, 0, 200, 21))
            lbl.center = CGPointMake(160, 100 + CGFloat(i) * 30)
            lbl.textAlignment = NSTextAlignment.center
            lbl.text = "⬛️"
            eachCard += [lbl]
            self.view.addSubview(lbl)
        }
   }

    let mark: [String] = ["♣️","♦️","❤️","♠️"]
    let number: [String] = ["A","2","3","4","5","6","7","8","9","T","J","Q","K"]

    enum EnumCard {
        static let Count = 13
    }
    
    var check: [Bool] = [Bool](repeating: false, count: EnumCard.Count)
    
    var count = EnumCard.Count
    
    var eachCard: [UILabel] = []
    
    @IBAction func btnGoTouch(_ sender: Any) {
        if count == 0 {
            for i in 0 ..< EnumCard.Count
            {
                check[i] = false
                eachCard[i].text = "⬛️"
            }

            let alert = UIAlertController()
            alert.title = "初期化"
            alert.message = "カードを配り直します"
            alert.addAction(UIAlertAction(title: "OK", style: .default))
            present(alert, animated: true, completion: nil)
            count = EnumCard.Count
        }
        else
        {
            var card = 0
            let randInt = Int.random(in: 0 ..< count)
            for i in 0 ... randInt {
                while check[card] {
                    card += 1
                }
                if i < randInt {
                    card += 1
                }
            }
            
            check[card] = true
            
            count -= 1
            lblCount.text = count.description
            
            if card == 52 {
                lblCard.text = "JK"
            } else {
                lblCard.text = mark[card / 13] + number[card % 13]
            }
            
            eachCard[card].text = lblCard.text
        }
    }
}

 さて問題です。♣️だけでなく、他のマークも出すようにするにはどうすれば良いでしょう。とりあえずJKは出さなくて良いです。

目次 解説編

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA