Copyright 2024 Andrei-Madalin Coman (comanandreimadalin@gmail.com)
This project is a Java-based framework for creating and running a card game.
Currently, the variety of cards / what cards can do / actions is pretty limited.
It needs an input json file and will write a json file where you can see the result of most of the actions called.
To run, it will need an input json file described here.
- Actions:
- All of them are a subclass of the abstract class
BaseAction. - General actions: The actions that we expect the player to use (
PlaceCard). - Statistical actions: Information about the games played until now, in the current test (
GetPlayerWins). - Debugging utilities: Inspect the game state (
GetPlayerDeck).
- All of them are a subclass of the abstract class
- Cards:
- All of them are a subclass of the abstract class
BaseCard. - After that, we specialize them into
BaseHeroorBaseMinionCard.- If the minion has a special ability, it will extend
BaseSpecialCard.
- If the minion has a special ability, it will extend
- Specific implementations examples
EmpressThorina,Disciple,Berserker.
- All of them are a subclass of the abstract class
- Game Management:
TestandMainhave the scope to open the test json file, deserialize it usingInputDeserializerand then running.playAllGames()on it.- The actual game logic is present in
InputandGame.
- Player:
- Player-specific details:
PlayerDataholds the data got from input and will suffer no changes from start to finish.Playeris the actual class which will be used to simulate the game.
- Player-specific details:
- Json:
- Custom deserializers and serializers for processing data.
Main.java: Main class that runs all tests from ./input and verify them against the corresponding file from ./ref/Test.java: It's used to test a single input json present in ./input. The output will be put inout.txt
- Navigate to the appropriate package:
- if it is a common minion:
cards/minion/specific. - if it is a minion that has an ability:
cards/minion/specials. - if it is a hero:
cards/hero/specific.
- if it is a common minion:
- Create a new class that extends the corresponding base class.
- NOTE: you need a constructor that has only the parameters present in
BaseCardas that one will be called byBaseCardDeserializer.
- NOTE: you need a constructor that has only the parameters present in
- Add the class in an entry in the corresponding map present in
BaseCardDeserializer.
- Navigate to the appropriate package.
- To determine it, read section Project Structure > Source Code > Actions.
- Create a new class that extends
BaseAction.- NOTE: you need a constructor that has only JsonNode's as parameters.
- Add the class in an entry in BOTH maps present in
BaseActionDeserializer. - Add the class in an entry in the SimpleModule named
CUSTOM_SERIALIZERSpresent inJsonUtils.- You can use
EmptySerializer<>(...)if you don't want to have the command printed in the output json.
- You can use
ROOT
├── playerOneDecks (Object)
│ ├── nrCardsInDeck (Number, Optional)
│ ├── nrDecks (Number, Optional)
│ └── decks (Array of Arrays)
│ ├── Deck 1 (Array)
│ │ ├── Card (Object)
│ │ │ ├── mana (Number)
│ │ │ ├── attackDamage (Number)
│ │ │ ├── health (Number)
│ │ │ ├── description (String)
│ │ │ ├── colors (Array of Strings)
│ │ │ └── name (String)
│ │ └── (Repeat for other cards)
│ ├── (Repeat for other decks)
│ .
│ .
│ .
│
├── playerTwoDecks (Object, same structure as playerOneDecks)
└── games (Array)
├── Game (Object)
│ ├── startGame (Object)
│ │ ├── playerOneDeckIdx (Number)
│ │ ├── playerTwoDeckIdx (Number)
│ │ ├── shuffleSeed (Number)
│ │ ├── playerOneHero (Object)
│ │ │ ├── mana (Number)
│ │ │ ├── description (String)
│ │ │ ├── colors (Array of Strings)
│ │ │ └── name (String)
│ │ └── playerTwoHero (Object, same structure as playerOneHero)
│ ├── startingPlayer (Number)
│ └── actions (Array)
│ ├── Action (Object)
│ │ ├── command (String)
│ │ ├── <command_argument1> (Optional)
│ │ ├── <command_argument2> (Optional)
│ │ .
│ │ .
│ │ .
│ │
│ ├── (Repeat for other actions)
│ .
│ .
│ .
│
├── (Repeat for other games)
.
.
.
ROOT
└── command ("endPlayerTurn", "getCardsOnTable", "getPlayerTurn", "getFrozenCardsOnTable", "getTotalGamesPlayed",
"getPlayerOneWins", "getPlayerTwoWins")
ROOT
├── command ("placeCard")
└── handIdx Number
ROOT
├── command ("getCardsInHand", "getPlayerDeck", "getPlayerHero", "getPlayerMana")
└── playerIdx (Number)
ROOT
├── command ("cardUsesAttack", "cardUsesAbility")
├── cardAttacker (Object)
│ ├── x (Number)
│ └── y (Number)
└── cardAttacked (Object)
├── x (Number)
└── y (Number)
ROOT
├── command ("useAttackHero")
└── cardAttacker (Object)
├── x (Number)
└── y (Number)
ROOT
├── command ("getCardAtPosition")
├── x (Number)
└── y (Number)
ROOT
├── command ("useHeroAbility")
└── affectedRow (Number)
- We needed two different Player objects because it supports multiple games in one test.
- It is not interactive. It only processes an input json where all the actions are predefined.
- Most probably this won't be subject to change as this project is more intended for me to understand the logic behind a game of this genre more than making the game more user-friendly.
- Although, if you still want to try it out, see this.
- It is build with jdk21.
- If for some reason jdk21 can't be used, the project is verified to work with jdk11 too, but without Maven.