요구 조건
- WebSocket 통신을 이용한 유저간 1:1 인디언 포커 게임을 실행할 수 있어야 한다
- 게임은 1판에 총 3개의 라운드가 진행되야 한다
- 각 라운드는 규칙에 맞게 동작할 수 있어야 한다
- 게임 종료 후 유저의 선택에 따라 게임을 다시 시작하거나 게임방 또는 로비로 나가도록 구현되야 한다
게임 규칙
- 숫자 1~10까지의 숫자 카드 덱 2개를 사용한다
- 라운드 종료 시 더 높은 숫자 카드를 가진 유저가 라운드에서 승리하고 베팅된 포인트를 모두 가져간다
- 라운드 시작 시 플레이어는 각각 카드를 1장 받는다(자신의 카드는 라운드 종료 시까지 확인할 수 없음)
- 베팅은 다음 3가지 중 하나를 선택할 수 있음
- CHECK
- 선턴인 유저 : 게임에 참가한 유저 중 포인트가 더 적은 유저의 10% 값 만큼 베팅한다
- 후턴인 유저 : 상대가 베팅한 포인트만큼 베팅한다
- 둘 다 CHECK인 경우 : 베팅 후 라운드를 종료된다
- RAISE
- CHECK 이후 또는 먼저하는 경우 : 초기 베팅 값(포인트가 적은 유저의 10%)의 2배만큼 베팅한다
- RAISE 이후 하는 경우 : RAISE 값의 2배 만큼 베팅한다
- RAISE 이후 CHECK나 DIE 가 나오면 베팅을 종료하고 라운드가 종료된다
- RAISE 이후 CHECK 할 때 베팅할 포인트가 부족한 경우 모든 포인트가 베팅된다
- DIE(FOLD)
- 라운드를 종료하고 선택한 유저는 라운드 패배 처리 된다
- 승리한 유저는 베팅된 포인트를 모두 획득한다
- CHECK
- 라운드 종료 시 두 명의 카드 값이 같을 경우 무승부 처리 또는 임의로 지정한 덱의 카드를 가진 유저가 승리하도록 처리한다
- 무승부 : 라운드에 베팅된 포인트를 다음 라운드로 이월하고 해당 라운드에서 승리한 유저가 포인트를 전부 가져간다
- 승리 : 1번덱 혹은 2번덱의 카드를 가진 유저가 승리처리 되고 포인트를 라운드에 걸린 포인트를 전부 가져간다
- 라운드 종료 시 보유한 포인트가 0인 유저는 다음 라운드를 진행할 수 없으며 즉시 게임에서 패배 처리 된다
- 3번의 라운드 진행이 끝난 후 3번의 라운드에서 더 많은 포인트를 획득한 유저가 게임에서 승리한다
- 게임 종료 후 유저는 게임을 다시 시작할 지 로비로 나갈지 선택할 수 있다
- 둘 다 수락한 경우 : 카드 및 베팅 정보를 초기화하고 1라운드부터 게임을 다시 시작한다
- 수락과 거절이 나온 경우 : 수락한 유저는 게임 방으로 돌아가 다른 참가자를 기다리게 되고, 거절한 유저는 로비로 나가게 된다
- 둘 다 거절한 경우 : 둘 다 로비로 나가게 되고 게임방과 게임은 삭제된다
API 명세
- 게임 구동(라운드 시작, 플레이어 행동, 라운드 종료, 게임 종료) : API주소/gameRoom/{gameRoomId}/{gameState}
- 게임 준비, 게임 종료 후 유저 선택 추가할 수 있음
- 구현하지 않는 API 파트 생략
초기 ERD
- 기능 구현 중 필요한 사항으로 인한 수정 필요
초기 설계
리팩토링 및 테스트 과정을 통한 로직 수정 작업 필요한 상태임
Entity / Enum Class
Game Entity
- 게임을 위한 도메인 모델, 카드 게임의 상태와 로직을 관리
- 게임의 진행 상황을 추적하고, 플레이어 간 상호작용 및 게임의 논리적 흐름을 관리
package com.example.socketpractice.domain.game.entity;
import com.example.socketpractice.domain.user.entity.User;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.HashSet;
import java.util.Set;
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Game {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ElementCollection(fetch = FetchType.LAZY)
@Enumerated(EnumType.STRING) // Enum 타입을 저장
private Set<Card> usedCards = new HashSet<>();
@ManyToOne(fetch = FetchType.LAZY)
private User playerOne;
@ManyToOne(fetch = FetchType.LAZY)
private User playerTwo;
@Enumerated(EnumType.STRING) // 카드 Enum을 저장
private Card playerOneCard;
@Enumerated(EnumType.STRING) // 카드 Enum을 저장
private Card playerTwoCard;
private int betAmount;
private int pot; // 현재 라운드의 포트
private int nextRoundPot; // 다음 라운드로 이월할 포트
@ManyToOne(fetch = FetchType.LAZY)
private User foldedUser;
// 플레이어가 라운드에서 획득한 포인트
private int playerOneRoundPoints;
private int playerTwoRoundPoints;
// Constructor and methods
public Game(User playerOne, User playerTwo) {
this.playerOne = playerOne;
this.playerTwo = playerTwo;
this.usedCards = new HashSet<>();
}
public void addUsedCard(Card card) {
this.usedCards.add(card);
}
public void setPlayerOneCard(Card card) {
this.playerOneCard = card;
}
public void setPlayerTwoCard(Card card) {
this.playerTwoCard = card;
}
public void setBetAmount(int betAmount) {
this.betAmount = betAmount;
}
// 게임 팟을 가져옵니다.
public int getPot() {
return pot;
}
// 게임 팟을 설정합니다.
public void setPot(int pot) {
this.pot = pot;
}
// 게임에서 포기한 유저를 설정합니다.
public void setFoldedUser(User user) {
this.foldedUser = user;
}
public void addPlayerOneRoundPoints(int points) {
this.playerOneRoundPoints += points;
}
public void addPlayerTwoRoundPoints(int points) {
this.playerTwoRoundPoints += points;
}
public void setNextRoundPot(int pot) {
// 다음 라운드로 이월할 포트 금액을 설정합니다.
this.nextRoundPot += pot; // 이월될 금액을 누적합니다.
}
public void resetRound() {
// 라운드 관련 정보를 초기화하는 메서드
// 카드를 초기화하고, 포트를 다음 라운드로 이월하며, 현재 라운드의 포트를 다음 라운드의 포트로 설정합니다.
this.pot = this.nextRoundPot; // 다음 라운드로 이월된 포트 금액을 현재 포트로 설정합니다.
this.nextRoundPot = 0; // 다음 라운드 포트 초기화
// 추가로 필요한 라운드 관련 정보 초기화 로직을 여기에 구현합니다.
}
// 게임과 관련된 상태를 초기화하는 메서드
public void resetGame() {
// 사용된 카드 목록을 비우고, 각 플레이어의 라운드별 획득 포인트를 0으로 리셋합니다.
usedCards.clear();
playerOneRoundPoints = 0;
playerTwoRoundPoints = 0;
// 초기 베팅 금액과 팟을 리셋합니다.
pot = 0;
nextRoundPot = 0;
// 각 플레이어의 카드를 null 또는 초기 상태로 설정할 수 있습니다.
// 예를 들어, 플레이어의 카드 필드가 있다면 이를 초기화합니다.
// playerOneCard = null;
// playerTwoCard = null;
// 기타 필요한 상태 초기화 로직
// 예: 라운드 수, 게임의 상태, 시간 제한 등을 초기화할 수 있습니다.
}
}
GameState
- 게임의 다양한 상태를 나타내는 enum(열거형)
- 게임의 라이프사이클을 추적하는 데 사용
package com.example.socketpractice.domain.game.entity;
import lombok.Getter;
@Getter
public enum GameState {
/* 게임 상태 Enum Class
* READY : 게임 준비
* START : 게임 시작
* ACTION : 유저 행동
* BET : 배팅
* END : 게임 종료 */
READY("READY"),
START("START"),
ACTION("ACTION"),
END("END"),
BET("BET")
;
private final String gameState;
GameState(String gameState) {
this.gameState = gameState;
}
}
Card
- 게임에서 사용되는 카드를 나타내는 enum(열거형)
- 게임 내에서 사용되는 카드의 종류와 그 속성을 정의하고 게임 로직이 카드 간의 상호작용을 적절히 처리할 수 있도록 함
package com.example.socketpractice.domain.game.entity;
import lombok.Getter;
@Getter
public enum Card {
DECK1_CARD1(1, 1),
DECK1_CARD2(1, 2),
DECK1_CARD3(1, 3),
DECK1_CARD4(1, 4),
DECK1_CARD5(1, 5),
DECK1_CARD6(1, 6),
DECK1_CARD7(1, 7),
DECK1_CARD8(1, 8),
DECK1_CARD9(1, 9),
DECK1_CARD10(1, 10),
DECK2_CARD1(2, 1),
DECK2_CARD2(2, 2),
DECK2_CARD3(2, 3),
DECK2_CARD4(2, 4),
DECK2_CARD5(2, 5),
DECK2_CARD6(2, 6),
DECK2_CARD7(2, 7),
DECK2_CARD8(2, 8),
DECK2_CARD9(2, 9),
DECK2_CARD10(2, 10)
;
private final int number;
private final int deckNumber;
Card(int number, int deckNumber) {
this.number = number;
this.deckNumber = deckNumber;
}
}
Betting
- 게임에서의 베팅 옵션을 나타내는 enum(열거형)
- 게임 내에서 플레이어가 선택할 수 있는 베팅 행동을 정의
package com.example.socketpractice.domain.game.entity;
import lombok.Getter;
@Getter
public enum Betting {
/* 배팅 상태 Enum Class
* CHECK : 상대와 같은 판돈 걸기
* RAISE : 판돈의 2배 걸기
* DIE : 라운드 포기하기 */
CHECK("CHECK"),
RAISE("RAISE"),
DIE("DIE")
;
private final String betting;
Betting(String betting) {
this.betting = betting;
}
}
UserChoice
- 게임 플레이어가 게임 진행 후에 선택할 수 있는 옵션을 나타내는 enum(열거형)
- 게임을 다시 시작할 건지, 게임에서 나갈지 옵션을 선택할 수 있음
package com.example.socketpractice.domain.game.entity;
public enum UserChoice {
PLAY_AGAIN("PLAY_AGAIN"), // 게임을 다시 하기로 선택
LEAVE("LEAVE") // 게임방에서 나가기로 선택
;
private final String userChoice;
UserChoice(String userChoice) {
this.userChoice = userChoice;
}
}
GameRoom
- 카드 게임의 방을 나타내는 entity, 게임 방과 관련된 데이터 및 로직을 캡슐화함
- 게임 방의 생성, 게임의 시작 및 종료 등의 프로세스를 관리
- 게임의 참여자 정보와 현재 게임의 상태를 추적하며, 게임 방과 게임 세션의 관리
package com.example.socketpractice.domain.game.entity;
import com.example.socketpractice.domain.user.entity.User;
import jakarta.persistence.*;
import lombok.Getter;
import java.util.Date;
@Entity
@Getter
@Table(name = "Game_room")
public class GameRoom {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "room_id")
private Long roomId;
@Column(name = "create_at")
private Date createAt;
@Column(name = "room_name")
private String roomName;
/* 유저 및 게임 관련*/
@ManyToOne(fetch = FetchType.LAZY)
private User playerOne;
@ManyToOne(fetch = FetchType.LAZY)
private User playerTwo;
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Game currentGame;
public void setRoomId(Long roomId) {
this.roomId = roomId;
}
public void setCreateAt(Date createAt) {
this.createAt = createAt;
}
public void setRoomName(String roomName) {
this.roomName = roomName;
}
public void startNewGame(User playerOne, User playerTwo) {
this.currentGame = new Game(playerOne, playerTwo);
}
public void setCurrentGame(Game game) {
this.currentGame = game;
}
// 게임을 종료할 때 호출하는 메서드입니다.
public void endCurrentGame() {
this.currentGame = null;
}
}
Controller
GameController
- 인디언 포커 게임의 실행 및 종료와 관련된 요청을 처리하는 컨트롤러
package com.example.socketpractice.domain.game.controller;
import com.example.socketpractice.domain.chat.entity.ChatMessage;
import com.example.socketpractice.domain.game.service.GameRoomService;
import com.example.socketpractice.domain.game.service.GameService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.handler.annotation.DestinationVariable;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Tag(name = "게임 실행 컨트롤러", description = "인디언 포커 게임 실행 및 종료 컨트롤러입니다.")
@Slf4j
@Controller
@RequestMapping("/gameRoom")
public class GameController {
private final SimpMessageSendingOperations messagingTemplate;
private final GameRoomService gameRoomService;
private final GameService gameService;
public GameController(SimpMessageSendingOperations messagingTemplate, GameRoomService gameRoomService, GameService gameService) {
this.messagingTemplate = messagingTemplate;
this.gameRoomService = gameRoomService;
this.gameService = gameService;
}
@MessageMapping("/{gameRoomId}/{gameState}")
public void handleGameState(@DestinationVariable Long gameRoomId, @DestinationVariable String gameState, @Payload ChatMessage chatMessage) {
switch (gameState) {
case "START":
gameService.startRound(gameRoomId);
break;
case "ACTION":
gameService.playerAction(gameRoomId, chatMessage.getSender(), chatMessage.getContent());
break;
case "END":
gameService.endRound(gameRoomId);
break;
}
/* 게임 상태 업데이트 메시지를 클라이언트에 전송 */
String destination = "/topic/gameRoom/" + gameRoomId;
messagingTemplate.convertAndSend(destination, chatMessage);
}
}
Repository
GameRepository
- Game 엔티티에 대한 데이터 액세스를 처리하는 JPA 리포지토리
package com.example.socketpractice.domain.game.repository;
import com.example.socketpractice.domain.game.entity.Game;
import org.springframework.data.jpa.repository.JpaRepository;
public interface GameRepository extends JpaRepository<Game, Long> {
}
Service
GameService
- 게임 관련 로직을 처리하는 서비스 클래스
- 게임의 전반적인 흐름을 제어하고, 게임 상태를 관리하는 중추적인 역할을 수행
package com.example.socketpractice.domain.game.service;
import com.example.socketpractice.domain.game.entity.Card;
import com.example.socketpractice.domain.game.entity.Game;
import com.example.socketpractice.domain.game.entity.GameRoom;
import com.example.socketpractice.domain.game.entity.UserChoice;
import com.example.socketpractice.domain.game.repository.GameRepository;
import com.example.socketpractice.domain.game.repository.GameRoomRepository;
import com.example.socketpractice.domain.user.entity.User;
import com.example.socketpractice.domain.user.repository.UserRepository;
import jakarta.persistence.EntityNotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
@Slf4j
@Service
public class GameService {
private final GameRepository gameRepository;
private final GameRoomRepository gameRoomRepository;
private final UserRepository userRepository;
private static final EnumSet<Card> ALL_CARDS = EnumSet.allOf(Card.class);
public GameService(GameRepository gameRepository, GameRoomRepository gameRoomRepository, UserRepository userRepository) {
this.gameRepository = gameRepository;
this.gameRoomRepository = gameRoomRepository;
this.userRepository = userRepository;
}
/* 게임 실행 관련 로직
* 1. 라운드 시작 로직
* 2. 플레이어 행동 처리 로직 - 채팅, 배팅
* 3. 라운드 종료 로직
* 4. 게임 종료 로직 */
public void startRound(Long gameRoomId) {
/* gameRoomId 사용 게임 룸 정보 검증 및 게임 인스턴스 확인 및 생성*/
GameRoom gameRoom = gameRoomRepository.findById(gameRoomId)
.orElseThrow(() -> new EntityNotFoundException("Game room not found with ID: " + gameRoomId));
Game game = gameRoom.getCurrentGame();
if (game == null) {
gameRoom.startNewGame(gameRoom.getPlayerOne(), gameRoom.getPlayerTwo());
game = gameRoom.getCurrentGame();
gameRoomRepository.save(gameRoom);
}
/* 게임 라운드 시작 로직*/
// 이전 라운드에서 사용된 카드를 제외한 카드 목록을 생성합니다.
Set<Card> usedCards = game.getUsedCards();
List<Card> availableCards = new ArrayList<>(EnumSet.complementOf(EnumSet.copyOf(usedCards)));
// 카드를 섞습니다.
Collections.shuffle(availableCards);
// 랜덤 카드를 플레이어에게 할당합니다.
Card playerOneCard = availableCards.get(0);
Card playerTwoCard = availableCards.get(1);
game.setPlayerOneCard(playerOneCard);
game.setPlayerTwoCard(playerTwoCard);
// 사용된 카드를 추적합니다.
game.addUsedCard(playerOneCard);
game.addUsedCard(playerTwoCard);
// 초기 베팅 금액을 계산하고 설정합니다.
int betAmount = calculateInitialBet(game.getPlayerOne(), game.getPlayerTwo());
game.setBetAmount(betAmount);
// 변경사항을 데이터베이스에 저장합니다.
gameRoomRepository.save(gameRoom);
// 게임 상태 업데이트, 채팅 시간 관리는 클라이언트 단에서 처리
}
@Transactional
public void playerAction(Long gameRoomId, String nickname, String gameState) {
// 플레이어 행동 처리(채팅, 배팅)
GameRoom gameRoom = gameRoomRepository.findById(gameRoomId)
.orElseThrow(() -> new EntityNotFoundException("Game room not found with ID: " + gameRoomId));
Game game = gameRoom.getCurrentGame();
if (game == null) {
throw new IllegalStateException("Game not started or already ended.");
}
User user = userRepository.findByNickname(nickname)
.orElseThrow(() -> new EntityNotFoundException("User not found with username: " + nickname));
switch (gameState.toUpperCase()) {
case "CHECK":
performCheckAction(game, user);
break;
case "RAISE":
performRaiseAction(game, user);
break;
case "DIE":
performDieAction(game, user);
break;
default:
throw new IllegalArgumentException("Unknown action: " + gameState);
}
gameRoomRepository.save(gameRoom);
}
@Transactional
public void endRound(Long gameRoomId) {
GameRoom gameRoom = gameRoomRepository.findById(gameRoomId)
.orElseThrow(() -> new EntityNotFoundException("Game room not found with ID: " + gameRoomId));
Game game = gameRoom.getCurrentGame();
if (game == null) {
throw new IllegalStateException("No game is currently active in this room.");
}
User playerOne = game.getPlayerOne();
User playerTwo = game.getPlayerTwo();
Card playerOneCard = game.getPlayerOneCard();
Card playerTwoCard = game.getPlayerTwoCard();
int pot = game.getPot(); // 이번 라운드의 팟
User roundWinner = determineWinner(playerOne, playerOneCard, playerTwo, playerTwoCard);
if (roundWinner != null) {
if (roundWinner.equals(playerOne)) {
game.addPlayerOneRoundPoints(pot);
} else {
game.addPlayerTwoRoundPoints(pot);
}
} else {
// 무승부인 경우, 다음 라운드로 팟을 이월합니다.
game.setNextRoundPot(pot);
}
// 라운드 정보를 초기화하는 로직을 추가합니다. 예를 들어, 카드 초기화, 팟 초기화 등
game.resetRound();
gameRoomRepository.save(gameRoom);
}
@Transactional
public void endGame(Long gameRoomId) {
GameRoom gameRoom = gameRoomRepository.findById(gameRoomId)
.orElseThrow(() -> new EntityNotFoundException("Game room not found with ID: " + gameRoomId));
Game game = gameRoom.getCurrentGame();
if (game == null) {
throw new IllegalStateException("No game is currently active in this room.");
}
// 게임의 결과를 가져오고, 게임 관련 데이터를 초기화합니다.
processGameResults(gameRoom);
// 사용자의 선택을 받아 상태를 결정합니다.
processUserChoices(gameRoom);
}
private int calculateInitialBet(User playerOne, User playerTwo) {
int playerOnePoints = playerOne.getPoints();
int playerTwoPoints = playerTwo.getPoints();
int lowerPoints = Math.min(playerOnePoints, playerTwoPoints);
return lowerPoints / 10; // 10%의 포인트를 초기 베팅 금액으로 설정
}
private void performCheckAction(Game game, User user) {
// 첫 번째 플레이어가 '체크'할 경우 현재 베팅 금액에 변화를 주지 않습니다.
boolean isFirstPlayer = game.getPlayerOne().equals(user);
if (!isFirstPlayer) {
// 두 번째 플레이어는 현재 베팅 금액을 팟에 추가합니다.
int userPoints = user.getPoints();
int currentBet = game.getBetAmount();
if (userPoints >= currentBet) {
user.setPoints(userPoints - currentBet); // 유저의 포인트를 감소시킵니다.
game.setPot(game.getPot() + currentBet); // 팟을 증가시킵니다.
} else {
// 유저의 포인트가 현재 베팅 금액보다 적다면 예외를 발생시킵니다.
throw new IllegalStateException("User does not have enough points to check.");
}
}
}
private void performRaiseAction(Game game, User user) {
int raiseAmount = game.getBetAmount() * 2;
int userPoints = user.getPoints();
// The user can only raise if they have enough points.
if (userPoints >= raiseAmount) {
game.setBetAmount(raiseAmount);
user.setPoints(userPoints - raiseAmount);
} else {
throw new IllegalStateException("User does not have enough points to raise.");
}
}
private void performDieAction(Game game, User user) {
// The user forfeits the round and possibly the game.
game.setFoldedUser(user);
}
private static User getWinner(Game game) {
User playerOne = game.getPlayerOne();
User playerTwo = game.getPlayerTwo();
int playerOneTotalPoints = game.getPlayerOneRoundPoints();
int playerTwoTotalPoints = game.getPlayerTwoRoundPoints();
User gameWinner;
if (playerOneTotalPoints > playerTwoTotalPoints) {
gameWinner = playerOne;
playerOne.incrementWins();
playerTwo.incrementLosses();
} else if (playerTwoTotalPoints > playerOneTotalPoints) {
gameWinner = playerTwo;
playerTwo.incrementWins();
playerOne.incrementLosses();
} else {
// 무승부 처리
gameWinner = null;
}
return gameWinner;
}
private User determineWinner(User playerOne, Card playerOneCard, User playerTwo, Card playerTwoCard) {
// 두 플레이어의 카드를 비교하여 승자를 결정하는 로직
// 예시 로직을 사용하여 승자를 결정합니다. 실제 게임 룰에 맞게 수정할 필요가 있습니다.
if (playerOneCard.getNumber() > playerTwoCard.getNumber()) {
return playerOne;
} else if (playerOneCard.getNumber() < playerTwoCard.getNumber()) {
return playerTwo;
} else {
// 무승부인 경우
return null;
}
}
private void processGameResults(GameRoom gameRoom) {
Game game = gameRoom.getCurrentGame();
User playerOne = game.getPlayerOne();
User playerTwo = game.getPlayerTwo();
int playerOneTotalPoints = game.getPlayerOneRoundPoints();
int playerTwoTotalPoints = game.getPlayerTwoRoundPoints();
// 승자 결정
User gameWinner = getWinner(game);
// 게임 데이터 초기화
game.resetGame();
// 게임 룸 업데이트
// 이 부분은 gameWinner의 값에 따라 게임 룸의 상태를 업데이트할 수도 있습니다.
// 예를 들어, 게임의 결과를 기록하거나 특정 게임 룸 설정을 변경할 수 있습니다.
// 승자가 결정된 경우 추가 처리
if (gameWinner != null) {
// 승자의 승리 횟수를 데이터베이스에 반영합니다.
userRepository.save(gameWinner);
// 게임 룸의 상태를 '대기 중'이나 '게임 종료' 등 적절한 상태로 업데이트할 수 있습니다.
}
// 무승부인 경우의 처리 로직도 여기에 포함될 수 있습니다.
// 예를 들어, 게임 룸을 계속 활성 상태로 두거나, 무승부를 사용자에게 알릴 수 있습니다.
}
public void processUserChoices(GameRoom gameRoom) {
// 이 예제에서는 사용자의 선택을 특정 저장소나 상태에서 가져오는 것으로 가정합니다.
// 실제로는 사용자의 선택을 관리하는 별도의 로직이 필요합니다.
// 사용자의 선택에 따라 게임방의 상태를 결정하고 조치를 취합니다.
UserChoice playerOneChoice = getUserChoice(gameRoom.getPlayerOne());
UserChoice playerTwoChoice = getUserChoice(gameRoom.getPlayerTwo());
if (playerOneChoice == UserChoice.PLAY_AGAIN && playerTwoChoice == UserChoice.PLAY_AGAIN) {
// 둘 다 다시 하기를 선택한 경우, 게임방을 재설정하고 새 게임을 시작합니다.
gameRoom.startNewGame(gameRoom.getPlayerOne(), gameRoom.getPlayerTwo());
} else if (playerOneChoice == UserChoice.LEAVE && playerTwoChoice == UserChoice.LEAVE) {
// 둘 다 나가기를 선택한 경우, 게임방을 종료하고 삭제합니다.
gameRoomRepository.delete(gameRoom);
} else {
// 한 명만 게임을 계속하려는 경우
if (playerOneChoice == UserChoice.PLAY_AGAIN) {
// Player One는 게임방으로 이동, Player Two는 로비로 이동
} else {
// Player Two는 게임방으로 이동, Player One은 로비로 이동
}
// 여기에서는 해당 플레이어를 로비로 보내는 로직을 구현해야 합니다.
}
}
private UserChoice getUserChoice(User player) {
// 이 메서드는 사용자의 선택을 반환합니다.
// 예제에서는 선택을 바로 반환하고 있지만, 실제로는 사용자가 선택한 내용을 어딘가에서 가져와야 합니다.
// 예: 데이터베이스, 세션, 캐시 등
return UserChoice.PLAY_AGAIN; // 예시로 항상 'PLAY_AGAIN'을 반환하고 있습니다.
}
}
초기 기능 구현 후 반성점
- 게임 및 라운드 결과 반환을 위한 DTO 설계를 하지 않았음
- 초기 ERD 설계에서 테이블 수정 사항이 많이 발생했음(WebSocket을 사용한 웹 게임 구현이 처음이어서 설계 시 문제가 많았음)
- 초기 구현 로직에 수정이 필요한 부분이 많음(테스트 및 리팩토링 과정이 필요함)
수정 계획
- 구현한 코드들에 대한 리팩토링
'항해 99 > Spring' 카테고리의 다른 글
Override, Overload / JPA 더티체킹 / JVM (0) | 2024.04.04 |
---|---|
Call by Reference (0) | 2024.04.04 |
WebSocket - 실제 코드 분석 (1) | 2024.04.03 |
WebSocket - STOMP 2 (0) | 2024.03.30 |
WebSocket - STOMP 1 (1) | 2024.03.29 |