この記事は公開されてから1年以上経過しているため、情報が古い可能性がございます。
ご注意ください。

このWordPress pluginを配布しています

過去、Java + Springでのガチャコードをサンプルとして公開いたします。

「レアリティ -> カード抽選」の2段階抽選を行うシンプルなガチャという想定です。

開発環境はこんな感じでした。

  • Java 7
  • Spring Framework (Sprint 4)
  • Maven 3
  • commons-lang3
  • lombock

数年前のもののため、Java7です。

pom.xml

dependencyにApache commonsのcommons-lang3を追加してください。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.0</version>
</dependency>

Randomsクラス

抽選処理用のRandomsクラスです。

import java.util.Collection;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.RandomUtils;

public class Randoms {

    private Randoms() {
    }

    public static <T> WeightedObject<T> detect(Collection<WeightedObject<T>> collection) {
        if (CollectionUtils.isEmpty(collection)) {
            return null;
        }
        int total = 0;
        for (WeightedObject<T> weighted : collection) {
            total += weighted.getWeight();
        }
        int random = RandomUtils.nextInt(0, total);
        total = 0;
        for (WeightedObject<T> weighted : collection) {
            total += weighted.getWeight();
            if (total > random) {
                return weighted;
            }
        }
        return null;
    }

    public static <T> WeightedObject<T> toWeightedObject(T object, Integer weight) {
        return new WeightedObject<T>(object, weight);
    }

    public static class WeightedObject<T> {
        public WeightedObject(T object, Integer weight) {
            this.object = object;
            this.weight = weight;
        }

        private T object;
        private Integer weight;

        public T getObject() {
            return object;
        }
        public int getWeight() {
            return weight;
        }
    }
}

このソースはGistで公開しております。
https://gist.github.com/atuweb/086eaf7edb49d8eec98c80a210f1a821

スキーマとValueObject

スキーマ

1次、2次抽選の排出率を設定するテーブルを用意します。

レアリティ設定テーブル(レアリティ設定ID, レアリティ, 重み)
カード設定テーブル(カード設定ID, レアリティ, カードID, 重み)

このほか、価格や排出設定を行うガチャ設定テーブルを用意するのが一般的だと思います。
このサンプルでは割愛いたします。

ValueObject

ガチャ処理用の構造体です。
lombockを使う前提の実装です。

また、複合主キーの場合にキーで1つの構造体を作る実装もあると思いますが、このサンプルでは簡略にいたしました。

GachaRarity.java

レアリティ設定テーブル用のValueObjectです。

import java.io.Serializable;

import lombok.Data;

@Data
public class GachaRarity implements Serializable {
    private static final long serialVersionUID = 1L;

    private Integer id;
    private Integer rarity;
    private Integer weight;
}

GachaCard.java

カード設定テーブル用のValueObjectです。

import java.io.Serializable;

import lombok.Data;

@Data
public class GachaCard implements Serializable {

    private static final long serialVersionUID = 1L;

    private Integer id;
    private Integer cardId;
    private Integer rarity;
    private Integer weight;
}

ガチャ処理

lotを実行すると、引数に従って抽選処理を行う実装です。
レアリティ設定テーブル、カード設定テーブルのIDを事前に決定されるものといたします。

@Service
public class GachaService {
    @Autowired
    private GachaRarityDao gachaRarityDao;
    @Autowired
    private GachaCardDao   gachaCardDao;

    private List<Integer> lot(Integer gachaRarityId, Integer gachaCardId, Integer lotCount) {
        // これが抽選結果を格納するリスト
        List<Integer> newCardIds = new ArrayList<>();

        // 連ガチャに対応するためループで指定回数繰り返す
        for (int i=1; i<=lotCount.intValue(); i++) {
            // 1次抽選でレアリティを決定する
            List<GachaRarity> rarities = gachaRarityDao.findListById(gachaRarityId);
            List<Randoms.WeightedObject<Integer>> rarityObjects = new ArrayList<Randoms.WeightedObject<Integer>>();
            for (GachaRarity rare : rarities) {
                rarityObjects.add(
                    Randoms.toWeightedObject(rare.getRarity(), rare.getWeight())
                );
            }
            Integer detectRarity = Randoms.detect(rarityObjects).getObject();

            // 2次抽選でレアリティに紐づくカード群から1枚を決定し、返却用のリストにアドする
            List<GachaCard> cards = gachaCardDao.findListByIdAndRarity(gachaCardId, detectRarity);
            List<Randoms.WeightedObject<Integer>> cardObjects = new ArrayList<Randoms.WeightedObject<Integer>>();

            for (GachaCard card : cards) {
                rarityObjects.add(
                    Randoms.toWeightedObject(card.getCardId(), card.getWeight())
                );
            }
            newCardIds.add(
                Randoms.detect(cardObjects).getObject()
            );
        }
        return newCardIds;
    }
}

daoは、それぞれキーに従ってDBのSELECT結果をリストで返す関数が実装されているイメージです。
テーブルの内容はそうそう頻繁に変わるものではないと思いますので、Springの特性を活かし、一度ロードしたものはヒープに乗せておくと良いですね。

おわりに

また、改善の余地がたくさんあるコードですので、ぜひサンプルから発展させたコードを書いてください。

ガチャの施策については過去にこんな記事も書きましたので、よろしければご覧ください。

[Web]ソシャゲ『ガチャ』の施策と設計
ガチャはソーシャルゲームの収益の柱ですが仕様が悪いとユーザさんが離れてしまう原因になってしまいます。 1万分ガチャしたの...

この記事はtomita@atuwebがお届けしました。




2016年05月11日:Gistへのリンクを追加

スポンサーリンク
ad_336
ad_336
  • このエントリーをはてなブックマークに追加
  • Evernoteに保存Evernoteに保存
コメントの入力は終了しました。