Swift、同確立を含む抽選

謎のプリン語る。
プログラミングの役立つ情報とか、どうでもいい雑談とか書いてます。
一人書く人増えました。

Swift、同確立を含む抽選

みやびプリン 500 316

500 320

Swift、同確立を含む抽選 - サムネイル

※この記事は7年以上前の記事です。
現在は状況が異なる可能性がありますのでご注意ください。

どうも、だいぶごぶさたです。
最近は、Swiftに挑戦しております。
(ゲーム作れるようになりたい)

ってわけで、表記の件だが、
今までどのプログラムでもやったことなかったから、
どんなアルゴリズムでやればいいかわからんかった。
なんで、いろいろ調べて、確立抽選の方法はあったが、
同確立のものを含むものはなかった。
応用してなんとかできたので、方法を公開しようかと。

下記手順でやる。

  1. 1、商品名と、確立の数値の列挙型を作り、関連付ける。
  2. 2、列挙型を、case単体が入った配列にし、確立数値がkeyの辞書にする。
  3. 3、抽選ボックスを作る

まず商品名と確立の列挙型、その関連付け

// 出現率基本
enum BaseRatioList: Float {
    case Empty = 0.0,
    ratio0250 = 25.0,
    ratio0200 = 20.0,
    ratio0100 = 10.0,
    ratio0050 = 5.0,
    ratio0010 = 1.0,
    ratio0005 = 0.5,
    ratio0001 = 0.1;
}

// 商品名リスト
enum PrizeList: String {
    case Empty = "はずれ",
    HinokiShield = "ひのきのたて",
    HinokiRod = "ひのきのぼう",
    HinokiBath = "ひのきのふろ",
    MithrilSword = "ミスリルソード",
    MithrilGauntlet = "ミスリルのこて",
    GenjiGauntlet = "源氏の籠手",
    MastorSword = "マスターソード";

    // 商品によって確立を返すメソッド
    func prizeByRatio() -> Float {
        switch self {
        case .Empty: return BaseRatioList.ratio0050.rawValue;
        case .HinokiShield: return BaseRatioList.ratio0200.rawValue;
        case .HinokiRod: return BaseRatioList.ratio0200.rawValue;
        case .HinokiBath: return BaseRatioList.ratio0200.rawValue;
        case .MithrilSword: return BaseRatioList.ratio0050.rawValue + BaseRatioList.ratio0100.rawValue;
        case .MithrilGauntlet: return BaseRatioList.ratio0050.rawValue + BaseRatioList.ratio0100.rawValue;
        case .GenjiGauntlet: return BaseRatioList.ratio0010.rawValue * 4;
        case .MastorSword: return BaseRatioList.ratio0010.rawValue;
        }
    }
}

これで、商品と、その確立を定義できた。
続いて、列挙型を配列にして、辞書にする方法。
デフォルトではできないので、
下記エントリのものを使い、下記のようにする

Swift3.0版、Enumのcaseを配列で返すProtocol Extension - Qiita

public protocol EnumEnumerable {
    associatedtype Case = Self
}

public extension EnumEnumerable where Case: Hashable {
    private static var iterator: AnyIterator<Case> {
        var n = 0
        return AnyIterator {
            defer { n += 1 }
            
            let next = withUnsafePointer(to: &n) {
                UnsafeRawPointer($0).assumingMemoryBound(to: Case.self).pointee
            }
            return next.hashValue == n ? next : nil
        }
    }
    
    public static func enumerate() -> EnumeratedSequence<AnySequence<Case>> {
        return AnySequence(self.iterator).enumerated()
    }
    
    public static var cases: [Case] {
        return Array(self.iterator)
    }
    
    public static var count: Int {
        return self.cases.count
    }
}

~ 中略 ~

// 商品名リスト
// 列挙型名に、extentionを追加している
enum PrizeList: String, EnumEnumerable {
    case Empty = 0,

~ 中略 ~
    
    // 確立の数値をkeyにして、caseを引き出せる辞書を返すメソッド
    func prizeByRatioList() -> Dictionary<Int, Array<PrizeList>> {
        let casesOnly = PrizeList.cases;
        
        var ratios = Dictionary<Int, Array<PrizeList>>();
        
        for caseSingle in casesOnly {
            var inList = Array<PrizeList>();
            
            if let inDic = ratios[Int(caseSingle.prizeByRatio() * 100)] {
                // 同確立がすでにある場合、配列を取り出す
                inList = inDic;
            }
            // 確立数値をkeyに、配列を突っ込む
            inList.append(caseSingle);
            ratios[Int(caseSingle.prizeByRatio() * 100)] = inList;
        }
        
        // 同確立のものを、key値を合算して、辞書に入れなおす。
        for (key, value) in ratios {
            if value.count > 1 {
                // 配列要素が、1個のときは、やる必要ないので、2個以上の時
                ratios[key * value.count] = value;
                ratios[key] = nil;
            }
        }
        
        return ratios;
    }
}

これで、確立の数値をkeyにした、case型配列の辞書ができるようになった。

では、実際に使ってみようと。

class GameViewController: UIViewController {
    // 確立辞書の変数を用意
    let prizeRatioList = PrizeList.Empty.prizeByRatioList();

    override func viewDidLoad() {
        super.viewDidLoad()

        // 商品名を定義
        var prize: PrizeList = PrizeList.Empty;
        // 1万を最大値にとる乱数
        let prizeNo = arc4random_uniform(10000)
        var k = 0;
        for (key, prizeList) in self.prizeRatioList {
            // kを使ってるのは、重なり分を解消するため
            k = k + key;
            if Int(prizeNo) < k {
                // 確立で取得できたので、同確立内でどれがあたるかを取得
                let innerNo = arc4random_uniform(UInt32(prizeList.count));
                
                prize = prizeList[Int(innerNo)];
                
                break;
            }
        }
        
        if prize == PrizeList.Empty {
            print("残念!\(prize.rawValue)");
        } else {
            print("おめでとう!\(prize.rawValue)が当たりました!");
        }
    }
}

こんな感じで使う。
ちなみに確立の部分は、下記エントリを参考にさせていただきました。

Swiftで抽選マシン(ガチャ)を作ってみる その1 - Qiita

今回はけっこう勉強になったばい。

Swiftはなんというか、スクリプト言語に近い感覚で書けるので、
本当に、Webデベロッパ向け言語だなと痛感してます。
わかりやすい。
やっぱオブジェクト指向言語、敷居高いからね・・・。(C#ですら断念したという)
ちょっと、がんばって作ってみます。

しかし、まだゲームの根幹部分を作ってる段階で、
実装まで険しい道のり・・・。
でもやるぜ。

目指せセミプロプログラマー!!(セミかよ)

トラックバック(0)

トラックバックURL:

コメントする

ページトップへ戻る