システム開発

【Spring boot】ブラックジャックRestAPIの開発

アイコン名を入力

こんにちは、シロクマです!

今日はSpring bootでRestAPIの作成方法を紹介したいと思います。

サンプルとして、トランプゲームのブラックジャックができるプログラムを作ったので

これに沿って説明します!

この記事でわかること

  1. Sprig bootでRest API開発するやり方
  2. ブラックジャック開発するやり方

※ソース全量は以下になります。

https://github.com/mission-Bell/21Quest

Contents

アプリケーション概要

まず、ブラックジャックを知らない方のために以下説明です。

ブラックジャック英語: Blackjack)は、トランプを使用するゲームの一種。カジノで行われるカードゲームではポーカーバカラと並ぶ人気ゲームである。カードの合計点数が21点を超えないように、プレイヤーがディーラーより高い点数を得ることを目指す。バカラやおいちょかぶと似たスタイルのゲームである。

ポントゥーン(pontoon)21(twenty-one)という別名もある。

https://ja.wikipedia.org/wiki/%E3%83%96%E3%83%A9%E3%83%83%E3%82%AF%E3%82%B8%E3%83%A3%E3%83%83%E3%82%AF

ルールとして単純で、「カードの合計点数が21点を超えないように」するというゲームになります。

作成するのは、RestAPIなのでAPI仕様書を作成しています。

ざっくり説明です。

  1. /health・・・アプリケーションの状態チェック用
  2. /game/beginning・・・ゲームの開始要求
  3. /game/continuation・・・ゲームを継続要求
  4. /game/ending・・・ゲーム終了要求
  5. /cards/disclosure・・・カード開示要求
  6. /cards/distribution・・・カード配布要求

イメージとしては②→⑥→⑤→③→④の順番でゲームが進行していきます。

全てGETメソッドにしていますが、基本クライアント側はURLへリクエストを投げるだけです。

今回はサンプルとして、一人のプレイヤーvs CPUとしているためプレイヤーの管理などを簡略化しているためです。

環境構築

開発環境としては、以下になります。

  • Spring boot:3.3.0
  • Java:17
  • maven:3.9.7

その他、必要なものは2024/6現在の最新のものをpom.xmlに入れています。

※pom.xmlの一部

Spring boot initialzrなどでSpring bootプロジェクトを作成後、bashで依存関係を解決すれば環境構築としては完了です。

https://start.spring.io

$ mvn install

エラーなくいければOKです。

Modelクラス作成

Modelクラスとしては、以下を用意します。

Modelクラスは、クライアントとのやりとりに用いるための情報を格納しています。

  1. TOQuestBaseUserModel
  2. TOQuestPlayerModel
  3. TOQuestCardMasterModel
  4. TOQuestCardsModel

それぞれ、①②と③用のベースクラス、②プレイヤークラス、③CPUクラス、④カードクラスです。

package com.hyomoto.toquest.common.model;

import java.util.List;

import com.hyomoto.toquest.business.model.TOQuestCardsModel;

import lombok.Getter;
import lombok.Setter;

/**
 * 21Questのユーザ基底モデルクラス
 */
public class TOQuestBaseUserModel extends TOQuestBaseModel{
    
    @Getter
    @Setter
    private List<TOQuestCardsModel> handOfCards;

    /**
     * カードを一枚手札に加える
     * @param toQuestCardsModel
     */
    public void addCard(TOQuestCardsModel toQuestCardsModel){
        this.handOfCards.add(toQuestCardsModel);
    }

    /**
     * カードを複数枚手札に加える
     * @param toQuestCardsModel
     */
    public void addCard(List<TOQuestCardsModel> toQuestCardsModels){
        toQuestCardsModels.forEach( toQuestCardsModel -> {
            this.addCard(toQuestCardsModel);

        });
    } 

}

TOQuestBaseUserModelには、プレイヤー、CPUで共通で利用する手札フィールドを持たせています。

この辺りはアナログのトランプゲームと一緒ですね。

package com.hyomoto.toquest.business.model;

import java.util.List;

import org.springframework.stereotype.Component;

import com.hyomoto.toquest.common.model.TOQuestBaseModel;
import com.hyomoto.toquest.common.model.TOQuestBaseUserModel;
import com.hyomoto.toquest.common.util.TOQuestConstants;

import lombok.Getter;
import lombok.Setter;

/**
 * 21Questにおけるプレイヤーモデル
 * ユーザに関連するものを引き受ける。
 */
@Component
public class TOQuestPlayerModel extends TOQuestBaseUserModel{
    
    @Getter
    @Setter
    private int moneyInPossession;
    @Getter
    @Setter
    private boolean isWin;

    /**
     * インスタンスの初期値を設定する。
     */
    public TOQuestPlayerModel(){
        this.moneyInPossession = TOQuestConstants.INITIAL_MONEY;
        this.isWin = false;
    }
}

TOQuestPlayerModelには、お金と勝利フラグのフィールドを追加しています。

プレイヤーがゲームに参加する場合は参加料を支払い、勝てば賭け金の二倍をもらい、負ければ失うイメージです。

※TOQuestCardMasterModelについては特別な実装していないので割愛

package com.hyomoto.toquest.business.model;

import com.hyomoto.toquest.common.model.TOQuestBaseModel;

import lombok.Getter;
import lombok.Setter;

/**
 * 21Questで使うカードモデルクラス
 * 1インスタンス=カード1枚
 */
public class TOQuestCardsModel extends TOQuestBaseModel{

    @Getter
    private String suit;
    @Getter
    private int rank;
    @Getter
    private int point;
    @Getter
    @Setter
    private boolean isBack;

    /**
     * 
     * @param suit ダイヤ、クローバー、ハート、スペード
     * @param rank 1〜13
     * @param point 21Questにおける各カードごとのポイント。最大10
     */
    public TOQuestCardsModel(String suit,int rank,int point){
        this.suit = suit;
        this.rank = rank;
        this.point = point;
        this.isBack = false;

    }    

}

TOQuestCardsModelは、実際のトランプが持つ属性をフィールドに追加しています。

suitはクローバ、スペード、ハート、ダイヤです。

rankは1〜13の数です。

pointはブラックジャック特有ですが、10以上のカードは全て10点として扱います。

例えば、クローバ13+ハート13の場合は、20点になるイメージです。

Serviceクラス作成

Serviceクラスは、以下を用意しています。

Serviceクラスでは、Controllerクラスから呼び出された後、Modelクラスを使って実際の処理を行なっていきます。

  1. BlackJackRuleService
  2. CardManagerService

①はブラックジャック全体を取り仕切るサービスになります。メインロジックですね。

②はカードの管理を行うクラスです。デッキの作成やカードの配布などを行なっています。

package com.hyomoto.toquest.business.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.hyomoto.toquest.business.model.TOQuestCardMasterModel;
import com.hyomoto.toquest.business.model.TOQuestCardsModel;
import com.hyomoto.toquest.business.model.TOQuestPlayerModel;
import com.hyomoto.toquest.common.model.TOQuestBaseModel;
import com.hyomoto.toquest.common.service.TOQuestBaseService;
import com.hyomoto.toquest.common.util.TOQuestConstants;

import lombok.extern.slf4j.Slf4j;

/**
 * 21Questによるメインロジッククラス
 * 基本的には、コントローラからこのクラスが呼ばれる。
 */
@Slf4j
@Service
public class BlackJackRuleService extends TOQuestBaseService {

    private final CardManagerService cardManagerService;
    private final TOQuestPlayerModel toQuestPlayerModel;
    private final TOQuestCardMasterModel toQuestCardMasterModel;

    @Autowired
    public BlackJackRuleService(CardManagerService cardManagerService,
                                 TOQuestPlayerModel toQuestPlayerModel,
                                 TOQuestCardMasterModel toQuestCardMasterModel){
        this.cardManagerService = cardManagerService;
        this.toQuestPlayerModel = toQuestPlayerModel;
        this.toQuestCardMasterModel = toQuestCardMasterModel;

    }

    /**
     * 
     * @return List<TOQuestBaseModel> プレイヤーとゲームマスターモデルのリスト
     */
    public List<TOQuestBaseModel> cardsDistribute(){
        // ゲーム開始に先立ち、カードデッキからカードを2枚ドロー
        List<TOQuestCardsModel> toQuestCardsModels_Player = cardManagerService.distributeCards(2);
        List<TOQuestCardsModel> toQuestCardsModels_Master = cardManagerService.distributeCards(2);
        // 引いたカードをプレイヤーの手札に加える
        toQuestPlayerModel.setHandOfCards(toQuestCardsModels_Player);

        // ゲームマスターの2枚目のカードについて裏側設定をONにする
        toQuestCardsModels_Master.get(1).setBack(true);
        // ゲームマスターのカードについて、裏返し対応を行う。(裏返し=ダミーカードをセット)
        cardManagerService.flipCards(toQuestCardsModels_Master,toQuestCardMasterModel.toString(),true);        
        // ゲームマスターの手札に処理したカードを加える
        toQuestCardMasterModel.setHandOfCards(toQuestCardsModels_Master);

        // 戻り値生成
        List<TOQuestBaseModel> toQuestBaseModels = new ArrayList<TOQuestBaseModel>(Arrays.asList(toQuestPlayerModel,toQuestCardMasterModel));

        return toQuestBaseModels;

    }

    /**
     * 
     * @return List<TOQuestBaseModel> プレイヤーとゲームマスターモデルのリスト
     */
    public List<TOQuestBaseModel> cardsRedistribute(){
        // プレイヤーからのカード再配布要求に伴いカードをデッキから一枚引く
        List<TOQuestCardsModel> toQuestCardsModels_Player = cardManagerService.distributeCards(1);
        // 引いたカードをプレイヤーの手札に加える
        toQuestPlayerModel.addCard(toQuestCardsModels_Player);
        // 現時点でカードの合計ポイントを出す
        int playerPoint =cardsPointTally(toQuestPlayerModel.getHandOfCards());

        // カードの合計ポイントがバースト値(=合計値>21)の場合、バースト処理
        if ( isBurst(playerPoint) ){
            
            // 所持金の計算
            moneyCalc(toQuestPlayerModel);
            // 戻り値生成
            List<TOQuestBaseModel> toQuestBaseModels = new ArrayList<TOQuestBaseModel>(Arrays.asList(toQuestPlayerModel,toQuestCardMasterModel));

            return toQuestBaseModels;
        }
        // ゲームマスターについては、まずカードを引く・引かないの判断をこの時点でのカード合計ポイントで行う。
        // そのため、まずダミーに変更したカードを元に戻し、合計ポイントを算出する。
        cardManagerService.flipCards(toQuestCardMasterModel.getHandOfCards(),toQuestCardMasterModel.toString(),false);  
        int masterPoint =cardsPointTally(toQuestCardMasterModel.getHandOfCards());

        // 合計ポイントが、カードを引く基準値以上である場合、カードを引く
        if ( masterPoint < 16 ) {
            // 一枚カードを引く
            List<TOQuestCardsModel> toQuestCardsModels_Master = cardManagerService.distributeCards(1);
            // 引いたカードを手札に加える
            toQuestCardMasterModel.addCard(toQuestCardsModels_Master);
            // 現時点でのカード合計値を出す
            masterPoint = cardsPointTally(toQuestCardsModels_Master);

            // ゲームマスターがバーストしていた場合、プレイヤーの勝利処理
            if ( isBurst(masterPoint) ){

                toQuestPlayerModel.setWin(true);   
                // 所持金計算
                moneyCalc(toQuestPlayerModel);
                // 戻り値生成
                List<TOQuestBaseModel> toQuestBaseModels = new ArrayList<TOQuestBaseModel>(Arrays.asList(toQuestPlayerModel,toQuestCardMasterModel));
    
                return toQuestBaseModels;
            }
            // カードマスターの追加カードについて、裏返し設定を行う
            toQuestCardMasterModel.getHandOfCards().get(2).setBack(true);
            // カードマスターのカードについて、裏返し処理を行う
            cardManagerService.flipCards(toQuestCardMasterModel.getHandOfCards(),toQuestCardMasterModel.toString(),true);  
        }

        List<TOQuestBaseModel> toQuestBaseModels = new ArrayList<TOQuestBaseModel>(Arrays.asList(toQuestPlayerModel,toQuestCardMasterModel));
        return toQuestBaseModels;
        
    }

    /**
     * 
     * @return List<TOQuestBaseModel> プレイヤーとゲームマスターモデルのリスト
     */
    public List<TOQuestBaseModel> cardOpen(){
        // プレイヤー手札のポイント取得
        int playerPoint = cardsPointTally(toQuestPlayerModel.getHandOfCards());
        // ゲームマスターの手札について裏返していたものを元に戻す
        cardManagerService.flipCards(toQuestCardMasterModel.getHandOfCards(),toQuestCardMasterModel.toString(),false);          
        // ゲームマスター手札のポイント取得
        int masterPoint = cardsPointTally(toQuestCardMasterModel.getHandOfCards());
        
        log.info(String.format("%s_%s", playerPoint,masterPoint));
        // プレイヤー勝利判定
        toQuestPlayerModel.setWin(isWinner(playerPoint,masterPoint));
        log.info(String.format("%s", toQuestPlayerModel.isWin()));

        // プレイヤーの所持金処理
        moneyCalc(toQuestPlayerModel);
        
        List<TOQuestBaseModel> toQuestBaseModels = new ArrayList<TOQuestBaseModel>(Arrays.asList(toQuestPlayerModel,toQuestCardMasterModel));
        return toQuestBaseModels;
    }

    /**
     * カードの合計ポイント計算
     * @param toQuestCardsModels
     * @return
     */
    public int cardsPointTally(List<TOQuestCardsModel> toQuestCardsModels){
        int sum = 0;
        for (TOQuestCardsModel toQuestCardsModel : toQuestCardsModels) {
            sum += toQuestCardsModel.getPoint();
        }
        return sum;
        
    }

    /**
     * バーストしているかどうかの判断
     * @param sumPoint
     * @return
     */
    public boolean isBurst(int sumPoint){
        if ( sumPoint > TOQuestConstants.BLACK_JACK ) {
            return true;
        }
        return false;
        
    }

    /**
     * 誰が買ったか判断
     * @param playerPoint
     * @param masterPoint
     * @return
     */
    public boolean isWinner(int playerPoint,int masterPoint){
        if (playerPoint > masterPoint) {
            return true;
        }
        return false;
        
    }

    /**
     * お金の計算
     * @param toQuestPlayerModel
     */
    public void moneyCalc(TOQuestPlayerModel toQuestPlayerModel){
        int calcedMoney = 0;
        // プレイヤーが勝っていた場合、プレイヤーの所持金を増やす
        if ( toQuestPlayerModel.isWin() ) {
            calcedMoney = toQuestPlayerModel.getMoneyInPossession() + TOQuestConstants.STAKE;
        // プレイヤーが負けていた場合、プレイヤーの所持金を減らす
        } else {
            calcedMoney = toQuestPlayerModel.getMoneyInPossession() - TOQuestConstants.STAKE;
        }
        toQuestPlayerModel.setMoneyInPossession(calcedMoney);
    }
}

長いのでかいつまんで以下二つです。

・手札の裏返しについて

今回のブラックジャックは対戦相手がCPUのためCPUの手札のうち一枚をプレイヤーに開示し、もう一枚を隠しておく必要があります。実装方法としては色々ありますが、簡便に済ませるためダミーカードを生成することで実装しています。(flipCardsがそれです。)

・バーストについて

ブラックジャックのルール上、手札の合計が21点を超えた場合負けになります。そのためバースト処理を追加しています。また、CPUが3枚目のカードを手札に追加をするかどうかは、CPUの手札の合計値によります。

※このあたりのロジックをもう少し検討しないとゲームとして面白くないかもしれません。

package com.hyomoto.toquest.business.service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Service;

import com.hyomoto.toquest.business.model.TOQuestCardsModel;
import com.hyomoto.toquest.common.service.TOQuestBaseService;
import com.hyomoto.toquest.common.util.TOQuestConstants;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * カード管理クラス
 */
@Service
@Slf4j
public class CardManagerService extends TOQuestBaseService {

    @Getter
    private Iterator<TOQuestCardsModel> cardsDeck;

    @Getter
    @Setter
    private Map<String,TOQuestCardsModel> escapeCards;

    @Getter
    private TOQuestCardsModel dummyCardsModel;

    /**
     * インスタンス初期値の設定。主にダミーカード
     */
    public CardManagerService(){
        this.dummyCardsModel = new TOQuestCardsModel("***", 0, 0);
        dummyCardsModel.setBack(true);

    }

    /**
     * カードデッキ生成メソッド
     */
    public void createCardDeck(){
        // カード数のカードリストを用意
        List<TOQuestCardsModel> tOQuestCardsModels = new ArrayList<TOQuestCardsModel>(TOQuestConstants.CARD_MAX_NUMBER - 1);
        // トランプの枚数(ジョーカぬき)のカードについて初期設定を行う
        for(int rank = 1; rank <= TOQuestConstants.CARD_MAX_RANK; rank++){
            
            int point;
            // rankが10を超える場合、すべてpointを10にする
            if (rank > 10) {
                point = 10;
            } else {
            point = rank; 

            }
            // 各suit毎にカードを作成
            tOQuestCardsModels.add(new TOQuestCardsModel(TOQuestConstants.SUIT_SPADE, rank,point));
            tOQuestCardsModels.add(new TOQuestCardsModel(TOQuestConstants.SUIT_CLUB, rank,point));
            tOQuestCardsModels.add(new TOQuestCardsModel(TOQuestConstants.SUIT_HEART, rank,point));
            tOQuestCardsModels.add(new TOQuestCardsModel(TOQuestConstants.SUIT_DIAMOND, rank,point));
        }
        // カードが全て生成できたら、シャッフルする
        Collections.shuffle(tOQuestCardsModels);
        // イテレータとしてカードデッキを格納
        cardsDeck = tOQuestCardsModels.iterator();
    }

    /**
     * 
     * @param numberCardRequested 要求カード数
     * @return List<TOQuestCardsModel> カードデッキから引いたカード
     */
    public List<TOQuestCardsModel> distributeCards (int numberCardRequested) {
        List<TOQuestCardsModel> tOQuestCardsModels = new ArrayList<TOQuestCardsModel>();
        for (int i = 0 ; i < numberCardRequested; i++) {
            tOQuestCardsModels.add(cardsDeck.next());           
        } 
        return tOQuestCardsModels;
    }

    // 
    /**
     * 裏返し要求のカードについてダミーカードと差し替える。
     * @param toQuestCardsModels
     * @param targetIndex
     * @param targetName
     */
    public void changeFromTargetCardToDummyCard(List<TOQuestCardsModel> toQuestCardsModels,int targetIndex, String targetName){
        escapeCards.put(targetName, toQuestCardsModels.get(targetIndex));
        toQuestCardsModels.set(targetIndex, dummyCardsModel);

    }

    /**
     * 裏返し要求のカードについてダミーカードを元のカードに戻す
     * @param toQuestCardsModels
     * @param targetIndex
     * @param targetName
     */
    public void changeFromDummyCardToTargetCard(List<TOQuestCardsModel> toQuestCardsModels,int targetIndex,String targetName){
        toQuestCardsModels.set(targetIndex, escapeCards.get(targetName));

    }

    /**
     * カードの裏返し要求を受け付ける
     * @param toQuestCardsModels
     * @param instalnceID
     * @param doDummy
     */
    public void flipCards(List<TOQuestCardsModel> toQuestCardsModels,String instalnceID,boolean doDummy){
        int i = 0;
        // ダミーカードにするかどうか判断
        if (doDummy){
            // ダミーカードと差し替えるカードについては、一時的に退避
            escapeCards = new HashMap<String,TOQuestCardsModel>();
            for (TOQuestCardsModel toQuestCardsModel : toQuestCardsModels){
                // 裏返しフラグがONのカードの場合、裏返し実施
                if (toQuestCardsModel.isBack()) {
                    // 退避カードについては、インスタンスID+位置情報の名札を付与
                    String targetName = String.format("%s_%s",instalnceID,i);
                    // 裏返し処理実施
                    changeFromTargetCardToDummyCard(toQuestCardsModels, i, targetName);                    
                }
                i++;
            }
        } else {
            for (TOQuestCardsModel toQuestCardsModel : toQuestCardsModels){

                // 裏返しフラグがONの場合、元に戻す処理実施
                if (toQuestCardsModel.isBack()) {
                    String targetName = String.format("%s_%s",instalnceID,i);
                    // 元に戻す処理実施
                    changeFromDummyCardToTargetCard(toQuestCardsModels, i, targetName);                    
                }
                i++;
            }
            escapeCards = null;
        }
    }

}

こちらも長いのでかいつまんで説明です。

・デッキの生成

このクラスの主たる目的は、カードデッキの管理です。51枚から構成されるカードについて生成し、配布要求があればデッキからカードを引いて要求元に返します。そのため、カードデッキは、要求されたカードを排除する必要がありますので、Iteratorで実装しています。

・カードの裏返し

他サービスからflipCardsが呼ばれた場合、一時的に裏返し処理をこなうカードをダミーカードに差し替えます。差し替える前のカードは一時的にこのクラス内で保持しておき、返却要求があった際にダミーカードと再度差し替えます。

Controllerクラス作成

ラストはControllerクラスの作成です。こちらは、クライアントと直接やりとりするクラスとなるのでエンドポイントを記述していきます。

作成するクラスとしては一つだけです。(TOQusetReceptionControllerクラス)

package com.hyomoto.toquest.business.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.hyomoto.toquest.business.model.TOQuestPlayerModel;
import com.hyomoto.toquest.business.service.BlackJackRuleService;
import com.hyomoto.toquest.business.service.CardManagerService;
import com.hyomoto.toquest.common.controller.TOQuestBaseController;
import com.hyomoto.toquest.common.model.TOQuestBaseModel;
import com.hyomoto.toquest.common.util.TOQuestConstants;

/**
 * 
 * 21Questのリクエストを受け付けるクラス
 * 基本は、このクラスを経由してサービスへ繋ぐ
 * 
 */
@RestController
public class TOQusetReceptionController extends TOQuestBaseController {

    private final TOQuestPlayerModel toQuestPlayerModel;
    private final BlackJackRuleService blackJackRuleService;
    private final CardManagerService cardManagerService;

    @Autowired
    public TOQusetReceptionController(TOQuestPlayerModel toQuestPlayerModel,
                                      BlackJackRuleService blackJackRuleService,
                                      CardManagerService cardManagerService){
        this.toQuestPlayerModel = toQuestPlayerModel;
        this.blackJackRuleService = blackJackRuleService;
        this.cardManagerService = cardManagerService;

    }

    /**
     * 
     * @return toQuestPlayerModel プレイヤーモデルクラス
     */
    @GetMapping("/game/beginning")
    public TOQuestPlayerModel gameStart(){
        cardManagerService.createCardDeck();
        return toQuestPlayerModel;
        
    }

     /**
     * 
     * @return toQuestPlayerModel プレイヤーモデルクラス
     */
    @GetMapping("/game/continuation")
    public TOQuestPlayerModel gameContinue(){
        cardManagerService.createCardDeck();
        return toQuestPlayerModel;
        
    }

     /**
     * 
     * @return FINISH_MESSAGE 終了メッセージ
     */
    @GetMapping("/game/ending")
    public String gamefinish(){
        return TOQuestConstants.FINISH_MESSAGE;
        
    }

    /**
     * 
     * @return List<TOQuestBaseModel> プレイヤーとゲームマスターモデルのリスト
     */
    @GetMapping("/cards/disclosure")
    public List<TOQuestBaseModel> cardsOpen(){
        
        return blackJackRuleService.cardOpen();
        
    }

    /**
     * 
     * @return List<TOQuestBaseModel> プレイヤーとゲームマスターモデルのリスト
     */
    @GetMapping("/cards/distribution")
    public List<TOQuestBaseModel> cardsDistribute(){
        return blackJackRuleService.cardsDistribute();
        
    }

     /**
     * 
     * @return List<TOQuestBaseModel> プレイヤーとゲームマスターモデルのリスト
     */
    @GetMapping("/cards/redistribution")
    public List<TOQuestBaseModel> cardsRedistribute(){
        return blackJackRuleService.cardsRedistribute();
        
    }
}

基本的には、アプリケーション概要で述べたAPI仕様書に沿ったエンドポイントの設定をしています。返却値も同様です。

動作確認

動作確認として、JUnitを使ったテストをやってみます。

テスト内容としては、以下です。ゲームの勝敗決定まで見てみます。

  1. ゲーム開始要求
  2. カード配布要求
  3. カード開示要求
    @Test
    public void cardsRedistribute()throws UnsupportedEncodingException, Exception{
        String responseString = getResponse("/game/beginning");
        responseString = getResponse("/cards/distribution");
        responseString = getResponse("/cards/redistribution");
        log.info(responseString);
    }
2024-06-18T10:20:13.852Z  INFO 60059 --- [toquest] [           main] c.h.c.TOQusetReceptionControllerTest     : [{"handOfCards":[{"suit":"club","rank":6,"point":6,"back":false},{"suit":"heart","rank":5,"point":5,"back":false}],"moneyInPossession":900,"win":false},{"handOfCards":[{"suit":"club","rank":8,"point":8,"back":false},{"suit":"club","rank":7,"point":7,"back":true}]}]

分かりにくいので以下が整形したものです。一つ目のオブジェクトがプレイヤーで二つ目がCPUとなります。

プレイヤーのpoint合計値が11であるのに対し、CPUは15となっています。そのため、プレイヤーの勝利フラグはfalseとなっていますね。つまり、プレイヤーの敗北ということです。

[
    {"handOfCards":
        [
            {"suit":"club",
             "rank":6,
             "point":6,
             "back":false
            },
           {"suit":"heart",
            "rank":5,
            "point":5,
            "back":false
           }
        ],
     "moneyInPossession":900,
     "win":false
    },
    {"handOfCards":
        [
            {"suit":"club",
             "rank":8,
             "point":8,
             "back":false
            },
            {"suit":"club",
             "rank":7,
             "point":7,
             "back":true
            }
        ]
    }
]

まとめ

はい、というわけでSpring bootでRestAPIを作ってみました。ソースの全量は本稿上部にリンクがありますので気になる方は確認してみてください。

今回ブラックジャックを行うRestAPIを作ったのは、そもそもSpring bootの入門解説記事はWebにあるのに対し、具体的な開発を一通り解説している記事がないような気がしたからです。

本稿が少しでもお役に立てれば幸いです。