feat: Hangman interaction
This commit is contained in:
parent
a05788c53b
commit
7167b4c34c
@ -3,6 +3,7 @@ package cz.jzitnik.chronos.controllers;
|
||||
import cz.jzitnik.chronos.entities.Interaction;
|
||||
import cz.jzitnik.chronos.entities.Item;
|
||||
import cz.jzitnik.chronos.interactions.InteractionService;
|
||||
import cz.jzitnik.chronos.payload.errors.NotFoundError;
|
||||
import cz.jzitnik.chronos.payload.requests.InteractionRequest;
|
||||
import cz.jzitnik.chronos.payload.responses.InteractionResponse;
|
||||
import cz.jzitnik.chronos.payload.responses.TakeItemsResponse;
|
||||
@ -53,6 +54,27 @@ public class CharacterController {
|
||||
);
|
||||
}
|
||||
|
||||
@GetMapping("/interacting")
|
||||
@CheckUser
|
||||
public ResponseEntity<UnifiedResponse<Boolean, Error>> isInteracting(@RequestParam String playerKey, @RequestParam Long characterId) {
|
||||
var player = playerRepository.findByPlayerKey(playerKey).get();
|
||||
var characterOptional = characterRepository.findById(characterId);
|
||||
|
||||
if (characterOptional.isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(UnifiedResponse.failure(new NotFoundError("Character was not found!")));
|
||||
}
|
||||
|
||||
var character = characterOptional.get();
|
||||
|
||||
var playerInteractingWithCharacter = character.getInteractionData().getPlayer();
|
||||
|
||||
if (playerInteractingWithCharacter == null || !playerInteractingWithCharacter.getId().equals(player.getId())) {
|
||||
return ResponseEntity.ok(UnifiedResponse.success(false));
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(UnifiedResponse.success(true));
|
||||
}
|
||||
|
||||
@PostMapping("/interact")
|
||||
@CheckUser
|
||||
public ResponseEntity<UnifiedResponse<InteractionResponse, Error>> interact(@RequestParam String playerKey, @RequestBody InteractionRequest interactionRequest) {
|
||||
|
@ -25,16 +25,16 @@ public class Interaction {
|
||||
@JsonIgnore
|
||||
private Character character;
|
||||
|
||||
public Interaction(String startText, String interactedWithText, Character character) {
|
||||
this.startText = startText;
|
||||
this.interactedWithText = interactedWithText;
|
||||
this.character = character;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
private String memory;
|
||||
|
||||
@ManyToOne
|
||||
@JsonIgnore
|
||||
private Player player;
|
||||
|
||||
public Interaction(String startText, String interactedWithText, Character character) {
|
||||
this.startText = startText;
|
||||
this.interactedWithText = interactedWithText;
|
||||
this.character = character;
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,219 @@
|
||||
package cz.jzitnik.chronos.interactions.list;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import cz.jzitnik.chronos.entities.Character;
|
||||
import cz.jzitnik.chronos.entities.Item;
|
||||
import cz.jzitnik.chronos.entities.ItemType;
|
||||
import cz.jzitnik.chronos.entities.Player;
|
||||
import cz.jzitnik.chronos.interactions.InteractionPlayer;
|
||||
import cz.jzitnik.chronos.payload.responses.InteractionResponse;
|
||||
import cz.jzitnik.chronos.repository.CharacterRepository;
|
||||
import cz.jzitnik.chronos.services.ItemService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
public class Hangman implements InteractionPlayer {
|
||||
@Autowired
|
||||
private CharacterRepository characterRepository;
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
|
||||
private static final int ATTEMPTS = 6; // Arbitrary max incorrect guesses
|
||||
|
||||
// This can be expanded at any time
|
||||
private final String[] words = {
|
||||
"dům", "pes", "les", "hra", "vlk", "kůň", "vůz", "most", "pán", "noc", "den", "čas", "hora",
|
||||
"sýr", "chleba", "pole", "věk", "slon", "osel", "šnek", "pták", "koza", "míč", "lípa", "knedlík",
|
||||
"čaj", "víno", "hráč", "strom", "nebe", "město", "stůl", "kniha", "kráva", "cesta", "divadlo",
|
||||
"okno", "hory", "slunce", "přítel", "rodina", "jahoda", "židle", "mravenec", "mléko", "sukně",
|
||||
"koberec", "tabule", "škola", "letadlo", "jablko", "písmeno", "košile", "ulice", "dopis",
|
||||
"zpráva", "kolega", "kamarád", "postel", "zmrzlina", "autobus", "tramvaj", "pohádka", "vánočka",
|
||||
"učitelka", "kancelář", "čokoláda", "letadlo", "zahrada", "studentka", "knihtisk", "televizor",
|
||||
"polévka", "zázračný", "papírnictví", "šampaňské", "kinematografie", "obloha", "křišťálový"
|
||||
};
|
||||
|
||||
// Memory
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
private static class HangmanMemory {
|
||||
private String word;
|
||||
private Set<java.lang.Character> correctGuessesSet;
|
||||
private Set<java.lang.Character> incorrectGuessesSet;
|
||||
private String currentProgress;
|
||||
}
|
||||
private static HangmanMemory readMemory(String memory) {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
try {
|
||||
return objectMapper.readValue(memory, HangmanMemory.class);
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
throw new RuntimeException("Somebody just fucked up db."); // If this happens idk what is wrong with my life
|
||||
}
|
||||
}
|
||||
private static String writeMemory(HangmanMemory memory) {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
try {
|
||||
return objectMapper.writeValueAsString(memory);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e); // Well hopefully this will never happen
|
||||
}
|
||||
}
|
||||
|
||||
// Response
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
private static class HangmanResponse {
|
||||
public enum Type {
|
||||
GAME_CREATED,
|
||||
GAME_WON,
|
||||
GAME_LOST,
|
||||
GAME_CONTINUE,
|
||||
}
|
||||
|
||||
private Type type;
|
||||
|
||||
private Set<java.lang.Character> correctGuessesSet;
|
||||
private Set<java.lang.Character> incorrectGuessesSet;
|
||||
private String currentProgress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResponse play(Player player, Character character, String data) {
|
||||
return null;
|
||||
if (character.getInteractionData().getPlayer() == null) {
|
||||
// New game
|
||||
try {
|
||||
return initGame(player, character);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Game already exists
|
||||
if (!character.getInteractionData().getPlayer().getId().equals(player.getId())) {
|
||||
// Another player tries to interact with character while somebody else is interacting with him
|
||||
return new InteractionResponse(false, "already_playing", new ArrayList<>());
|
||||
}
|
||||
|
||||
try {
|
||||
return guessChar(player, character, data);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e); // I hate error handling. This code is just shit but whatever
|
||||
}
|
||||
}
|
||||
|
||||
private InteractionResponse initGame(Player player, Character character) throws JsonProcessingException {
|
||||
var interactionData = character.getInteractionData();
|
||||
interactionData.setPlayer(player);
|
||||
|
||||
Random random = new Random();
|
||||
int index = random.nextInt(words.length);
|
||||
String randomWord = words[index];
|
||||
|
||||
String initialProgress = randomWord.replaceAll(".", "_");
|
||||
|
||||
interactionData.setMemory(writeMemory(
|
||||
new HangmanMemory(randomWord, new HashSet<>(), new HashSet<>(), initialProgress)
|
||||
));
|
||||
|
||||
characterRepository.save(character);
|
||||
|
||||
var response = new HangmanResponse(HangmanResponse.Type.GAME_CREATED, new HashSet<>(), new HashSet<>(), initialProgress);
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
return new InteractionResponse(true, objectMapper.writeValueAsString(response), new ArrayList<>());
|
||||
}
|
||||
|
||||
private InteractionResponse guessChar(Player player, Character character, String data) throws JsonProcessingException {
|
||||
var interactionMemory = readMemory(character.getInteractionData().getMemory());
|
||||
|
||||
if (data.equals("get_data")) {
|
||||
var response = new HangmanResponse(HangmanResponse.Type.GAME_CONTINUE, interactionMemory.getCorrectGuessesSet(), interactionMemory.getIncorrectGuessesSet(), interactionMemory.getCurrentProgress());
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
var responseText = objectMapper.writeValueAsString(response);
|
||||
|
||||
return new InteractionResponse(false, responseText, new ArrayList<>());
|
||||
}
|
||||
|
||||
String word = interactionMemory.getWord();
|
||||
var correctGuessesSet = interactionMemory.getCorrectGuessesSet();
|
||||
var incorrectGuessesSet = interactionMemory.getIncorrectGuessesSet();
|
||||
StringBuilder currentProgress = new StringBuilder(interactionMemory.getCurrentProgress());
|
||||
|
||||
if (data == null || data.isEmpty()) {
|
||||
return new InteractionResponse(false, "invalid_input", new ArrayList<>());
|
||||
}
|
||||
|
||||
char guessedChar = data.charAt(0);
|
||||
|
||||
if (correctGuessesSet.contains(guessedChar) || incorrectGuessesSet.contains(guessedChar)) {
|
||||
return new InteractionResponse(false, "already_guessed", new ArrayList<>());
|
||||
}
|
||||
|
||||
boolean isCorrect = false;
|
||||
for (int i = 0; i < word.length(); i++) {
|
||||
if (word.charAt(i) == guessedChar) {
|
||||
currentProgress.setCharAt(i, guessedChar);
|
||||
isCorrect = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isCorrect) {
|
||||
correctGuessesSet.add(guessedChar);
|
||||
} else {
|
||||
incorrectGuessesSet.add(guessedChar);
|
||||
}
|
||||
|
||||
interactionMemory.setCorrectGuessesSet(correctGuessesSet);
|
||||
interactionMemory.setIncorrectGuessesSet(incorrectGuessesSet);
|
||||
interactionMemory.setCurrentProgress(currentProgress.toString());
|
||||
|
||||
character.getInteractionData().setMemory(writeMemory(interactionMemory));
|
||||
|
||||
characterRepository.save(character);
|
||||
|
||||
|
||||
// Submit response
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
if (currentProgress.toString().equals(word)) {
|
||||
var item = new Item(ItemType.KEY_FRAGMENT, player);
|
||||
itemService.addItem(player, item);
|
||||
|
||||
character.setInteractedWith(true);
|
||||
characterRepository.save(character);
|
||||
|
||||
var items = new ArrayList<Item>();
|
||||
items.add(item);
|
||||
|
||||
var response = new HangmanResponse(HangmanResponse.Type.GAME_WON, correctGuessesSet, incorrectGuessesSet, currentProgress.toString());
|
||||
var responseText = objectMapper.writeValueAsString(response);
|
||||
return new InteractionResponse(true, responseText, items);
|
||||
} else if (incorrectGuessesSet.size() >= ATTEMPTS) {
|
||||
character.setInteractedWith(true);
|
||||
characterRepository.save(character);
|
||||
|
||||
var response = new HangmanResponse(HangmanResponse.Type.GAME_LOST, correctGuessesSet, incorrectGuessesSet, currentProgress.toString());
|
||||
var responseText = objectMapper.writeValueAsString(response);
|
||||
return new InteractionResponse(false, responseText, new ArrayList<>());
|
||||
}
|
||||
|
||||
var response = new HangmanResponse(HangmanResponse.Type.GAME_CONTINUE, correctGuessesSet, incorrectGuessesSet, currentProgress.toString());
|
||||
var responseText = objectMapper.writeValueAsString(response);
|
||||
|
||||
return new InteractionResponse(false, responseText, new ArrayList<>());
|
||||
}
|
||||
}
|
||||
|
@ -43,14 +43,35 @@ public class InitGameService {
|
||||
farmar.setInteraction(Interaction.Farmer);
|
||||
farmar.setInteractionData(new cz.jzitnik.chronos.entities.Interaction(
|
||||
"Tak si zahrajeme kámen nůžky papír",
|
||||
"Se mnou jsi již hrál kámen nůžky papír. Znovu už ti fragment klíče nedám",
|
||||
"Se mnou někdo již hrál kámen nůžky papír. Další fragment klíče nemám.",
|
||||
farmar
|
||||
));
|
||||
stodola_characters.add(farmar);
|
||||
stodola.setCharacters(stodola_characters);
|
||||
|
||||
// Shop
|
||||
var shop = new Room(
|
||||
"Obchod",
|
||||
game
|
||||
);
|
||||
var shop_characters = new ArrayList<Character>();
|
||||
var cashier = new Character(
|
||||
"Prodavač",
|
||||
shop,
|
||||
"Ahoj já jsem prodavač a budeš se mnou hrát šibenici."
|
||||
);
|
||||
cashier.setInteraction(Interaction.Cashier);
|
||||
cashier.setInteractionData(new cz.jzitnik.chronos.entities.Interaction(
|
||||
"Tak si zahrajeme šibenici",
|
||||
"Se mnou někdo již hrál šibenici. Další fragment klíče nemám.",
|
||||
cashier
|
||||
));
|
||||
shop_characters.add(cashier);
|
||||
shop.setCharacters(shop_characters);
|
||||
|
||||
rooms.add(outside);
|
||||
rooms.add(stodola);
|
||||
rooms.add(shop);
|
||||
|
||||
game.setRooms(rooms);
|
||||
|
||||
|
@ -83,6 +83,12 @@ public interface ApiService {
|
||||
@Query("characterId") Long characterId
|
||||
);
|
||||
|
||||
@GET("game/characters/interacting")
|
||||
Call<UnifiedResponse<Boolean, Error>> isInteracting(
|
||||
@Query("playerKey") String playerKey,
|
||||
@Query("characterId") Long characterId
|
||||
);
|
||||
|
||||
@POST("game/characters/interact")
|
||||
Call<UnifiedResponse<InteractionResponse, Error>> interact(
|
||||
@Query("playerKey") String playerKey,
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cz.jzitnik.api.types;
|
||||
|
||||
public enum Interaction {
|
||||
Farmer
|
||||
Farmer,
|
||||
Cashier
|
||||
}
|
||||
|
104
frontend/src/main/java/cz/jzitnik/game/interactions/Hangman.java
Normal file
104
frontend/src/main/java/cz/jzitnik/game/interactions/Hangman.java
Normal file
@ -0,0 +1,104 @@
|
||||
package cz.jzitnik.game.interactions;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import cz.jzitnik.api.ApiService;
|
||||
import cz.jzitnik.api.requests.InteractionRequest;
|
||||
import cz.jzitnik.api.responses.InteractionResponse;
|
||||
import cz.jzitnik.api.types.Character;
|
||||
import cz.jzitnik.utils.Cli;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.Console;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class Hangman {
|
||||
private Character character;
|
||||
private ApiService apiService;
|
||||
private String playerKey;
|
||||
|
||||
@Getter
|
||||
private static class HangmanResponse {
|
||||
public enum Type {
|
||||
GAME_CREATED,
|
||||
GAME_WON,
|
||||
GAME_LOST,
|
||||
GAME_CONTINUE,
|
||||
}
|
||||
|
||||
private Type type;
|
||||
|
||||
private Set<java.lang.Character> correctGuessesSet;
|
||||
private Set<java.lang.Character> incorrectGuessesSet;
|
||||
private String currentProgress;
|
||||
}
|
||||
|
||||
public void play() throws IOException {
|
||||
var response = apiService.isInteracting(playerKey, character.getId()).execute();
|
||||
var interacting = response.body().getData().get();
|
||||
|
||||
if (!interacting) {
|
||||
init();
|
||||
} else {
|
||||
// Get data where we have left of
|
||||
var body = apiService.interact(playerKey, new InteractionRequest("get_data", character.getId())).execute().body();
|
||||
var data = body.getData().get();
|
||||
gameContinue(data);
|
||||
}
|
||||
}
|
||||
|
||||
private void init() throws IOException {
|
||||
var body = apiService.interact(playerKey, new InteractionRequest("", character.getId())).execute().body();
|
||||
var data = body.getData().get();
|
||||
gameContinue(data);
|
||||
}
|
||||
|
||||
private void gameContinue(InteractionResponse data) throws IOException {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
var hangmanData = objectMapper.readValue(data.getResponseText(), HangmanResponse.class);
|
||||
|
||||
switch (hangmanData.getType()) {
|
||||
case GAME_WON -> {
|
||||
Cli.type(character, "Vyhrál jsi! Slovo bylo: " + hangmanData.getCurrentProgress());
|
||||
Cli.gotItems(data.getItems());
|
||||
}
|
||||
case GAME_LOST -> Cli.type(character, "Prohrál jsi! Slovo jsi celé neuhodl. Fragment klíče ti nedám!");
|
||||
case GAME_CONTINUE, GAME_CREATED -> {
|
||||
System.out.println("Špatně uhodnuté písmena: " + hangmanData.getIncorrectGuessesSet());
|
||||
System.out.println("Slovo: " + hangmanData.getCurrentProgress());
|
||||
|
||||
// I hate my life
|
||||
var guessingChar = askForChar(Stream.concat(hangmanData.getCorrectGuessesSet().stream(), hangmanData.getIncorrectGuessesSet().stream()).collect(Collectors.toSet()));
|
||||
|
||||
var response = apiService.interact(playerKey, new InteractionRequest(guessingChar, character.getId())).execute();
|
||||
|
||||
var body = response.body().getData().get();
|
||||
|
||||
gameContinue(body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String askForChar(Set<java.lang.Character> guessed) {
|
||||
Console console = System.console();
|
||||
|
||||
String data = console.readLine("Zadejte jedno písmeno: ");
|
||||
|
||||
if (data.length() != 1) {
|
||||
Cli.error("Neplatný vstup!");
|
||||
return askForChar(guessed);
|
||||
}
|
||||
|
||||
if (guessed.contains(data.charAt(0))) {
|
||||
Cli.error("Toto písmeno jste již se snažil uhodnout!");
|
||||
return askForChar(guessed);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
@ -20,9 +20,10 @@ public class Interactions {
|
||||
|
||||
Cli.type(character, interactionData.getStartText());
|
||||
|
||||
// "Object _ =" is just for java compiler to ensure that all interactions are covered.
|
||||
switch (character.getInteraction()) {
|
||||
case Farmer -> {
|
||||
var options = new String[] {"Kámen", "Nůžky", "Papír"};
|
||||
case Farmer -> {
|
||||
var options = new String[]{"Kámen", "Nůžky", "Papír"};
|
||||
var selected = Cli.selectOptionIndex(Arrays.stream(options).toList());
|
||||
|
||||
var requestData = new InteractionRequest(String.valueOf(selected), character.getId());
|
||||
@ -35,6 +36,11 @@ public class Interactions {
|
||||
if (interactionResponse.isSuccess()) {
|
||||
Cli.gotItems(interactionResponse.getItems());
|
||||
}
|
||||
|
||||
}
|
||||
case Cashier -> {
|
||||
Hangman hangman = new Hangman(character, apiService, playerKey);
|
||||
hangman.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user