Skip to content

Commit

Permalink
Merge pull request #35 from Game-as-a-Service/feature/16/backend-flow…
Browse files Browse the repository at this point in the history
…-game-start

Feature/16/backend flow game start
  • Loading branch information
ShadowSunCat authored Sep 15, 2023
2 parents c81b5c7 + 74cc385 commit 247bc0b
Show file tree
Hide file tree
Showing 40 changed files with 1,391 additions and 131 deletions.
25 changes: 24 additions & 1 deletion backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<properties>
<java.version>17</java.version>
<org.mapstruct.version>1.5.3.Final</org.mapstruct.version>
<org.projectlombok.version>1.18.26</org.projectlombok.version>
</properties>
<dependencies>
<dependency>
Expand Down Expand Up @@ -44,6 +45,7 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
<optional>true</optional>
</dependency>
<dependency>
Expand All @@ -56,17 +58,28 @@
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.1.214</version>
<version>2.2.220</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Expand Down Expand Up @@ -108,6 +121,16 @@
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
<!-- other annotation processors -->
</annotationProcessorPaths>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,23 @@
package tw.waterballsa.gaas.unoflip.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import tw.waterballsa.gaas.unoflip.presenter.GameJoinPresenter;
import tw.waterballsa.gaas.unoflip.service.SseService;
import tw.waterballsa.gaas.unoflip.usecase.GameJoinUseCase;
import tw.waterballsa.gaas.unoflip.vo.GameJoinResult;
import tw.waterballsa.gaas.unoflip.vo.JoinRequest;
import tw.waterballsa.gaas.unoflip.vo.JoinResult;
import tw.waterballsa.gaas.unoflip.vo.Response;
import tw.waterballsa.gaas.unoflip.response.JoinResult;
import tw.waterballsa.gaas.unoflip.response.Response;

@RestController
@RequiredArgsConstructor
public class GameController {

private final GameJoinUseCase gameJoinUseCase;
private final GameJoinPresenter gameJoinPresenter;
private final SseService sseService;

public GameController(GameJoinUseCase gameJoinUseCase, GameJoinPresenter gameJoinPresenter, SseService sseService) {
this.gameJoinUseCase = gameJoinUseCase;
this.gameJoinPresenter = gameJoinPresenter;
this.sseService = sseService;
}

@PostMapping("join/{playerId}")
public Response<JoinResult> join(@PathVariable String playerId, @RequestBody JoinRequest joinRequest) {
GameJoinResult gameJoinResult = gameJoinUseCase.join(playerId, joinRequest.playerName());
sseService.sendMessage(gameJoinPresenter.broadcastEvent(playerId, gameJoinResult));
return gameJoinPresenter.response(playerId, gameJoinResult);
return gameJoinUseCase.join(playerId, joinRequest.playerName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package tw.waterballsa.gaas.unoflip.domain;

import tw.waterballsa.gaas.unoflip.domain.eumns.Card;

import java.util.List;

record DealResult(List<HandCard> playersHandCard, Card discardCard, List<Card> drawPileCards) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package tw.waterballsa.gaas.unoflip.domain;

import tw.waterballsa.gaas.unoflip.domain.eumns.Card;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.IntStream;

class Dealer {
public static DealResult deal() {
List<Integer> cardNumbers = Card.getAllIds();
Collections.shuffle(cardNumbers);

List<HandCard> playersHandCards = new ArrayList<>();

playersHandCards.add(createHandCard(0, 7, cardNumbers));
playersHandCards.add(createHandCard(7, 14, cardNumbers));
playersHandCards.add(createHandCard(14, 21, cardNumbers));
playersHandCards.add(createHandCard(21, 28, cardNumbers));

Card discardCard = Card.getLightInstance(cardNumbers.get(28));

List<Card> drawPileCards = getRandomCardList(29, 112, cardNumbers);

return new DealResult(playersHandCards, discardCard, drawPileCards);
}

private static HandCard createHandCard(int startInclusive, int endExclusive, List<Integer> cardNumbers) {
return new HandCard(getRandomCardList(startInclusive, endExclusive, cardNumbers));
}

private static List<Card> getRandomCardList(int startInclusive, int endExclusive, List<Integer> cardNumbers) {
return IntStream.range(startInclusive, endExclusive).mapToObj(i -> Card.getLightInstance(cardNumbers.get(i))).toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package tw.waterballsa.gaas.unoflip.domain;

import tw.waterballsa.gaas.unoflip.domain.eumns.Card;

import java.util.Collections;
import java.util.List;

public class HandCard {
private final List<Card> cards;

public HandCard(List<Card> cards) {
this.cards = cards;
}

public List<Integer> toCardIds() {
return cards.stream().map(Card::getId).toList();
}

int size() {
return cards.size();
}

List<Card> getCards() {
return Collections.unmodifiableList(cards);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package tw.waterballsa.gaas.unoflip.domain;

import lombok.Getter;
import lombok.Setter;

@Getter
public class Player {

private final PlayerInfo playerInfo;
@Setter
private HandCard handCard;

public Player(PlayerInfo playerInfo) {
this.playerInfo = playerInfo;
}

public int getPosition() {
return playerInfo.position();
}

public String getId() {
return playerInfo.id();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package tw.waterballsa.gaas.unoflip.domain;

public record PlayerInfo(String id, String name, Integer position) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package tw.waterballsa.gaas.unoflip.domain;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class Players {
private final Map<String, Player> playerMap = new HashMap<>();

public boolean exists(String playerId) {
return playerMap.get(playerId) != null;
}

public void add(PlayerInfo playerInfo) {
playerMap.put(playerInfo.id(), new Player(playerInfo));
}

public List<PlayerInfo> toInfoList() {
return playerMap.values().stream().map(Player::getPlayerInfo).toList();
}

public HandCard getPlayerHandCard(String playerId) {
return Optional.ofNullable(playerMap.get(playerId))
.map(Player::getHandCard)
.orElseThrow(() -> new IllegalArgumentException("player %s not exists".formatted(playerId)));
}

public void setHandCard(String playerId, HandCard handCard) {
Player player = Optional.ofNullable(playerMap.get(playerId)).orElseThrow(() -> new IllegalArgumentException("player %s not exists".formatted(playerId)));
player.setHandCard(handCard);
}

public int size() {
return playerMap.size();
}

public List<String> getIds() {
return playerMap.values().stream().map(Player::getId).toList();
}

public String getPlayerId(int position) {
return playerMap.values().stream()
.filter(player -> position == player.getPosition())
.findFirst()
.map(Player::getId)
.orElseThrow(() -> new IllegalArgumentException("position %d not exists".formatted(position)));
}
}
Original file line number Diff line number Diff line change
@@ -1,49 +1,96 @@
package tw.waterballsa.gaas.unoflip.domain;

import tw.waterballsa.gaas.unoflip.vo.PlayerInfo;
import lombok.Getter;
import tw.waterballsa.gaas.unoflip.domain.eumns.Card;
import tw.waterballsa.gaas.unoflip.domain.eumns.Direction;
import tw.waterballsa.gaas.unoflip.domain.eumns.GameMode;
import tw.waterballsa.gaas.unoflip.domain.eumns.GameStatus;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class UnoFlipGame {
private static final int MAX_PLAYER_NUMBER = 4;
private final List<Card> drawPileList = new ArrayList<>();
private final List<Card> discardPileList = new ArrayList<>();

@Getter
private final Players players = new Players();
@Getter
private final int tableId;
private final List<PlayerInfo> playerInfoList = new ArrayList<>();
@Getter
private String actionPlayerId;
@Getter
private GameStatus status;
@Getter
private Direction direction;
@Getter
private GameMode mode;

public UnoFlipGame(int tableId) {
this.tableId = tableId;
this.status = GameStatus.WAITING;
this.direction = Direction.RIGHT;
this.mode = GameMode.LIGHT;
}

public int getTableId() {
return tableId;
public boolean isFull() {
return players.size() >= MAX_PLAYER_NUMBER;
}

public void join(String playerId, String playerName) {
if (isPlayerAlreadyInGame(playerId)) {
throw new RuntimeException("player already in game");
}
public List<PlayerInfo> getPlayerInfoList() {
return players.toInfoList();
}

playerInfoList.add(new PlayerInfo(playerId, playerName, getAvailablePosition()));
public List<Card> getDrawPile() {
return Collections.unmodifiableList(drawPileList);
}

public List<PlayerInfo> getPlayerInfoList() {
return Collections.unmodifiableList(playerInfoList);
public List<Card> getDiscardPile() {
return Collections.unmodifiableList(discardPileList);
}

public boolean isFull() {
return playerInfoList.size() >= MAX_PLAYER_NUMBER;
public void join(String playerId, String playerName) {
if (isPlayerAlreadyInGame(playerId)) {
throw new RuntimeException("player already in game");
}

players.add(new PlayerInfo(playerId, playerName, getAvailablePosition()));
}

private boolean isPlayerAlreadyInGame(String playerId) {
return playerInfoList.stream().anyMatch(playerInfo -> playerId.equals(playerInfo.playerId()));
return players.exists(playerId);
}

private int getAvailablePosition() {
if (isFull()) {
throw new RuntimeException("game is full");
throw new IllegalStateException("game is full");
}

return playerInfoList.size() + 1;
return players.size() + 1;
}

public void start() {
status = GameStatus.STARTED;
actionPlayerId = players.getPlayerId(getInitPosition());

DealResult dealResult = Dealer.deal();

setPlayersHandCard(dealResult);
discardPileList.add(dealResult.discardCard());
drawPileList.addAll(dealResult.drawPileCards());
}

private int getInitPosition() {
return (int) (Math.random() * MAX_PLAYER_NUMBER) + 1;
}

private void setPlayersHandCard(DealResult dealResult) {
int handCardListIdx = 0;
for (String playerId : players.getIds()) {
players.setHandCard(playerId, dealResult.playersHandCard().get(handCardListIdx++));
}
}

}
Loading

0 comments on commit 247bc0b

Please sign in to comment.