feat: add stand and get game features to blackjack game

This commit is contained in:
Jan-Marlon Leibl 2025-03-27 15:22:24 +01:00
parent 4e8530c861
commit d90fcdcf1e
Signed by: jleibl
GPG key ID: 300B2F906DC6F1D5
13 changed files with 446 additions and 32 deletions

View file

@ -10,3 +10,11 @@ Content-Type: application/json
POST http://localhost:8080/blackjack/54/hit
Authorization: Bearer {{token}}
###
POST http://localhost:8080/blackjack/202/stand
Authorization: Bearer {{token}}
###
GET http://localhost:8080/blackjack/202
Authorization: Bearer {{token}}

View file

@ -26,6 +26,23 @@ public class BlackJackGameController {
this.userService = userService;
}
@GetMapping("/blackjack/{id}")
public ResponseEntity<Object> getGame(@PathVariable Long id, @RequestHeader("Authorization") String token) {
Optional<UserEntity> optionalUser = userService.getCurrentUser(token);
if (optionalUser.isEmpty()) {
return ResponseEntity.notFound().build();
}
UserEntity user = optionalUser.get();
BlackJackGameEntity game = blackJackService.getBlackJackGame(id);
if (game == null || !Objects.equals(game.getUserId(), user.getId())) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(game);
}
@PostMapping("/blackjack/{id}/hit")
public ResponseEntity<Object> hit(@PathVariable Long id, @RequestHeader("Authorization") String token) {
Optional<UserEntity> optionalUser = userService.getCurrentUser(token);
@ -40,13 +57,24 @@ public class BlackJackGameController {
return ResponseEntity.notFound().build();
}
if (game.getState() != BlackJackState.IN_PROGRESS) {
Map<String, String> errorResponse = new HashMap<>();
errorResponse.put("error", "Invalid state");
return ResponseEntity.badRequest().body(errorResponse);
return ResponseEntity.ok(blackJackService.hit(game));
}
@PostMapping("/blackjack/{id}/stand")
public ResponseEntity<Object> stand(@PathVariable Long id, @RequestHeader("Authorization") String token) {
Optional<UserEntity> optionalUser = userService.getCurrentUser(token);
if (optionalUser.isEmpty()) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(blackJackService.hit(game, user));
UserEntity user = optionalUser.get();
BlackJackGameEntity game = blackJackService.getBlackJackGame(id);
if (game == null || !Objects.equals(game.getUserId(), user.getId())) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(blackJackService.stand(game));
}
@PostMapping("/blackjack/start")

View file

@ -24,12 +24,15 @@ public class BlackJackGameEntity {
@GeneratedValue
private Long id;
@Version
@JsonIgnore
private Long version;
@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
@JsonIgnore
private UserEntity user;
// Expose UserID to JSON output
public Long getUserId() {
return user != null ? user.getId() : null;
}

View file

@ -3,6 +3,7 @@ package de.szut.casino.blackjack;
import de.szut.casino.user.UserEntity;
import de.szut.casino.user.UserRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List;
@ -26,6 +27,7 @@ public class BlackJackService {
return optionalBlackJackGame.orElse(null);
}
@Transactional
public BlackJackGameEntity createBlackJackGame(UserEntity user, BigDecimal betAmount) {
BlackJackGameEntity game = new BlackJackGameEntity();
game.setUser(user);
@ -46,21 +48,79 @@ public class BlackJackService {
game.setState(state);
userRepository.save(user);
blackJackGameRepository.save(game);
return game;
return blackJackGameRepository.save(game);
}
public BlackJackGameEntity hit(BlackJackGameEntity game, UserEntity user) {
@Transactional
public BlackJackGameEntity hit(BlackJackGameEntity game) {
game = blackJackGameRepository.findById(game.getId()).orElse(game);
if (game.getState() != BlackJackState.IN_PROGRESS) {
return game;
}
CardEntity drawnCard = drawCardFromDeck(game);
drawnCard.setCardType(CardType.PLAYER);
game.getPlayerCards().add(drawnCard);
game.setState(handleState(game, user));
game.setState(handleState(game, game.getUser()));
if (game.getState() == BlackJackState.PLAYER_WON) {
updateUserBalance(game, true);
} else if (game.getState() == BlackJackState.PLAYER_LOST) {
updateUserBalance(game, false);
}
return blackJackGameRepository.save(game);
}
@Transactional
public BlackJackGameEntity stand(BlackJackGameEntity game) {
game = blackJackGameRepository.findById(game.getId()).orElse(game);
if (game.getState() != BlackJackState.IN_PROGRESS) {
return game;
}
while (calculateHandValue(game.getDealerCards()) < 17) {
CardEntity dealerCard = drawCardFromDeck(game);
dealerCard.setCardType(CardType.DEALER);
game.getDealerCards().add(dealerCard);
}
int playerValue = calculateHandValue(game.getPlayerCards());
int dealerValue = calculateHandValue(game.getDealerCards());
if (dealerValue > 21 || playerValue > dealerValue) {
game.setState(BlackJackState.PLAYER_WON);
updateUserBalance(game, true);
} else {
game.setState(BlackJackState.DRAW);
updateUserBalance(game, false);
}
return blackJackGameRepository.save(game);
}
@Transactional
private void updateUserBalance(BlackJackGameEntity game, boolean isWin) {
UserEntity user = game.getUser();
user = userRepository.findById(user.getId()).orElse(user);
BigDecimal balance = user.getBalance();
BigDecimal betAmount = game.getBet();
if (isWin) {
balance = balance.add(betAmount.multiply(BigDecimal.valueOf(2)));
}
else if (game.getState() == BlackJackState.DRAW) {
balance = balance.add(betAmount);
}
user.setBalance(balance);
userRepository.save(user);
}
private void initializeDeck(BlackJackGameEntity game) {
for (Suit suit : Suit.values()) {
for (Rank rank : Rank.values()) {

View file

@ -4,5 +4,5 @@ public enum BlackJackState {
IN_PROGRESS,
PLAYER_BLACKJACK,
PLAYER_LOST,
STANDOFF,
DRAW,
}

View file

@ -19,6 +19,10 @@ public class CardEntity {
@JsonIgnore
private Long id;
@Version
@JsonIgnore
private Long version;
@ManyToOne
@JoinColumn(name = "game_id", nullable = false)
@JsonBackReference