Skip to content

Commit

Permalink
Merge pull request #228 from Arquisoft/feat/game-configuration
Browse files Browse the repository at this point in the history
feat: game configuration
  • Loading branch information
gony02 authored Apr 14, 2024
2 parents 0168183 + 871dddd commit c535cdc
Show file tree
Hide file tree
Showing 14 changed files with 499 additions and 107 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package lab.en2b.quizapi.commons.utils;

import lab.en2b.quizapi.game.Game;
import lab.en2b.quizapi.game.GameMode;
import lab.en2b.quizapi.questions.question.QuestionCategory;

import java.util.List;

import static lab.en2b.quizapi.game.GameMode.KIWI_QUEST;

public class GameModeUtils {
public static List<QuestionCategory> getQuestionCategoriesForGamemode(GameMode gamemode, List<QuestionCategory> questionCategoriesForCustom){
if(gamemode == null){
gamemode = KIWI_QUEST;
}
return switch (gamemode) {
case KIWI_QUEST -> List.of(QuestionCategory.ART, QuestionCategory.MUSIC, QuestionCategory.GEOGRAPHY);
case FOOTBALL_SHOWDOWN -> List.of(QuestionCategory.SPORTS);
case GEO_GENIUS -> List.of(QuestionCategory.GEOGRAPHY);
case VIDEOGAME_ADVENTURE -> List.of(QuestionCategory.VIDEOGAMES);
case ANCIENT_ODYSSEY -> List.of(QuestionCategory.MUSIC,QuestionCategory.ART);
case RANDOM -> List.of(QuestionCategory.values());
case CUSTOM -> questionCategoriesForCustom;
};
}
public static void setGamemodeParams(Game game){
switch(game.getGamemode()){
case KIWI_QUEST:
game.setRounds(9L);
game.setRoundDuration(30);
break;
case FOOTBALL_SHOWDOWN:
game.setRounds(9L);
game.setRoundDuration(30);
break;
case GEO_GENIUS:
game.setRounds(9L);
game.setRoundDuration(30);
break;
case VIDEOGAME_ADVENTURE:
game.setRounds(9L);
game.setRoundDuration(30);
break;
case ANCIENT_ODYSSEY:
game.setRounds(9L);
game.setRoundDuration(30);
break;
case RANDOM:
game.setRounds(9L);
game.setRoundDuration(30);
break;
default:
game.setRounds(9L);
game.setRoundDuration(30);
}
}
}
54 changes: 49 additions & 5 deletions api/src/main/java/lab/en2b/quizapi/game/Game.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lab.en2b.quizapi.commons.user.User;
import lab.en2b.quizapi.commons.utils.GameModeUtils;
import lab.en2b.quizapi.game.dtos.CustomGameDto;
import lab.en2b.quizapi.questions.answer.Answer;
import lab.en2b.quizapi.questions.question.Question;
import lab.en2b.quizapi.questions.question.QuestionCategory;
import lombok.*;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;

import static lab.en2b.quizapi.game.GameMode.*;

@Entity
@Table(name = "games")
@NoArgsConstructor
Expand All @@ -20,7 +24,6 @@
@Setter
@Builder
public class Game {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Setter(AccessLevel.NONE)
Expand All @@ -35,7 +38,8 @@ public class Game {
@NonNull
private Integer roundDuration;
private boolean currentQuestionAnswered;

@Enumerated(EnumType.STRING)
private GameMode gamemode;
@ManyToOne
@NotNull
@JoinColumn(name = "user_id")
Expand All @@ -52,6 +56,19 @@ public class Game {
@OrderColumn
private List<Question> questions;
private boolean isGameOver;
@Enumerated(EnumType.STRING)
private List<QuestionCategory> questionCategoriesForCustom;

public Game(User user,GameMode gamemode,String lang, CustomGameDto gameDto){
this.user = user;
this.questions = new ArrayList<>();
this.actualRound = 0L;
setLanguage(lang);
if(gamemode == CUSTOM)
setCustomGameMode(gameDto);
else
setGameMode(gamemode);
}

public void newRound(Question question){
if(getActualRound() != 0){
Expand Down Expand Up @@ -110,16 +127,43 @@ public boolean answerQuestion(Long answerId){
return q.isCorrectAnswer(answerId);
}
public void setLanguage(String language){
if(language == null){
language = "en";
}
if(!isLanguageSupported(language))
throw new IllegalArgumentException("The language you provided is not supported");
this.language = language;
}
public void setCustomGameMode(CustomGameDto gameDto){
setRounds(gameDto.getRounds());
setRoundDuration(gameDto.getRoundDuration());
this.gamemode = CUSTOM;
setQuestionCategoriesForCustom(gameDto.getCategories());
}
public void setGameMode(GameMode gamemode){
if(gamemode == null){
gamemode = KIWI_QUEST;
}
this.gamemode = gamemode;
GameModeUtils.setGamemodeParams(this);
}

public void setQuestionCategoriesForCustom(List<QuestionCategory> questionCategoriesForCustom) {
if(gamemode != CUSTOM)
throw new IllegalStateException("You can't set custom categories for a non-custom gamemode!");
if(questionCategoriesForCustom == null || questionCategoriesForCustom.isEmpty())
throw new IllegalArgumentException("You can't set an empty list of categories for a custom gamemode!");
this.questionCategoriesForCustom = questionCategoriesForCustom;
}

public List<QuestionCategory> getQuestionCategoriesForGamemode(){
return GameModeUtils.getQuestionCategoriesForGamemode(gamemode,questionCategoriesForCustom);
}
private boolean isLanguageSupported(String language) {
return language.equals("en") || language.equals("es");
}

public boolean shouldBeGameOver() {
return getActualRound() >= getRounds() && !isGameOver;
return getActualRound() >= getRounds() && !isGameOver && currentRoundIsOver();
}
}
49 changes: 39 additions & 10 deletions api/src/main/java/lab/en2b/quizapi/game/GameController.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package lab.en2b.quizapi.game;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import lab.en2b.quizapi.game.dtos.AnswerGameResponseDto;
import lab.en2b.quizapi.game.dtos.GameAnswerDto;
import lab.en2b.quizapi.game.dtos.GameResponseDto;
import jakarta.validation.Valid;
import lab.en2b.quizapi.game.dtos.*;
import lab.en2b.quizapi.questions.question.QuestionCategory;
import lab.en2b.quizapi.questions.question.dtos.QuestionResponseDto;
import lombok.RequiredArgsConstructor;
Expand All @@ -24,39 +25,50 @@ public class GameController {
@Operation(summary = "Starts new game", description = "Requests the API to create a new game for a given authentication (a player)")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "400", description = "Given when: \n * language provided is not valid \n * gamemode provided is not valid \n * body is not provided with custom game", content = @io.swagger.v3.oas.annotations.media.Content),
@ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content),
})
@PostMapping("/new")
public ResponseEntity<GameResponseDto> newGame(Authentication authentication){
return ResponseEntity.ok(gameService.newGame(authentication));
@Parameters({
@Parameter(name = "lang", description = "The language of the game", example = "en"),
@Parameter(name = "gamemode", description = "The gamemode of the game", example = "KIWI_QUEST")
})
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "The custom game dto, only required if the gamemode is CUSTOM")
@PostMapping("/play")
public ResponseEntity<GameResponseDto> newGame(@RequestParam(required = false) String lang, @RequestParam(required=false) GameMode gamemode, @RequestBody(required = false) @Valid CustomGameDto customGameDto, Authentication authentication){
if(gamemode == GameMode.CUSTOM && customGameDto == null)
throw new IllegalArgumentException("Custom game mode requires a body");
return ResponseEntity.ok(gameService.newGame(lang,gamemode,customGameDto,authentication));
}

@Operation(summary = "Starts a new round", description = "Starts the round (asks a question and its possible answers to the API and start the timer) for a given authentication (a player)")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content),
})
@Parameter(name = "id", description = "The id of the game to start the round for", example = "1")
@PostMapping("/{id}/startRound")
public ResponseEntity<GameResponseDto> startRound(@PathVariable Long id, Authentication authentication){
return ResponseEntity.ok(gameService.startRound(id, authentication));
}

@Operation(summary = "Starts a new round", description = "Gets the question and its possible answers from the API for a given authentication (a player)")
@Operation(summary = "Gets the current question", description = "Gets the question and its possible answers from the API for a given authentication (a player)")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content),
})
@Parameter(name = "id", description = "The id of the game to get the current question for", example = "1")
@GetMapping("/{id}/question")
public ResponseEntity<QuestionResponseDto> getCurrentQuestion(@PathVariable Long id, Authentication authentication){
return ResponseEntity.ok(gameService.getCurrentQuestion(id, authentication));
}

@Operation(summary = "Starts a new round", description = "Starts the round (getting a question and its possible answers and start the timer) for a given authentication (a player)")
@Operation(summary = "Answers the question", description = "Answers the question for the current game")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "400", description = "Not a valid answer", content = @io.swagger.v3.oas.annotations.media.Content),
@ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content),
})
@Parameter(name = "id", description = "The id of the game to answer the question for", example = "1")
@PostMapping("/{id}/answer")
public ResponseEntity<AnswerGameResponseDto> answerQuestion(@PathVariable Long id, @RequestBody GameAnswerDto dto, Authentication authentication){
return ResponseEntity.ok(gameService.answerQuestion(id, dto, authentication));
Expand All @@ -65,9 +77,10 @@ public ResponseEntity<AnswerGameResponseDto> answerQuestion(@PathVariable Long i
@Operation(summary = "Changing languages", description = "Changes the language of the game for a given authentication (a player) and a language supported. Changes may are applied on the next round.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "400", description = "Not a valid answer", content = @io.swagger.v3.oas.annotations.media.Content),
@ApiResponse(responseCode = "400", description = "Not a valid language to change to", content = @io.swagger.v3.oas.annotations.media.Content),
@ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content),
})
@Parameter(name = "id", description = "The id of the game to change the language for", example = "1")
@PutMapping("/{id}/language")
public ResponseEntity<GameResponseDto> changeLanguage(@PathVariable Long id, @RequestParam String language, Authentication authentication){
return ResponseEntity.ok(gameService.changeLanguage(id, language, authentication));
Expand All @@ -78,12 +91,28 @@ public ResponseEntity<GameResponseDto> changeLanguage(@PathVariable Long id, @Re
@ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content),
})
@Parameter(name = "id", description = "The id of the game to get the summary for", example = "1")
@GetMapping("/{id}/details")
public ResponseEntity<GameResponseDto> getGameDetails(@PathVariable Long id, Authentication authentication){
return ResponseEntity.ok(gameService.getGameDetails(id, authentication));
}

@GetMapping("/questionCategories")
@Operation(summary = "Get the list of gamemodes a game can have")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content)
})
@GetMapping("/gamemodes")
public ResponseEntity<List<GameModeDto>> getQuestionGameModes(){
return ResponseEntity.ok(gameService.getQuestionGameModes());
}

@Operation(summary = "Get the list of categories a game can have")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "403", description = "You are not logged in", content = @io.swagger.v3.oas.annotations.media.Content)
})
@GetMapping("/question-categories")
public ResponseEntity<List<QuestionCategory>> getQuestionCategories(){
return ResponseEntity.ok(gameService.getQuestionCategories());
}
Expand Down
12 changes: 12 additions & 0 deletions api/src/main/java/lab/en2b/quizapi/game/GameMode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package lab.en2b.quizapi.game;

public enum GameMode {
KIWI_QUEST,
FOOTBALL_SHOWDOWN,
GEO_GENIUS,
VIDEOGAME_ADVENTURE,
ANCIENT_ODYSSEY,
RANDOM,
CUSTOM
}

Loading

0 comments on commit c535cdc

Please sign in to comment.