At the start of a round, given a set of decks, the players choose one, and they are assigned a hero. Taking turns, players place cards on the table and attack / use abilities on each other until one of the heroes die.
As the name states, this is basically the engine.
This class aggregates the most important elements of the game: the table and the two players. The main responsibility of this class is to take the input and generate the output (where the command processing takes place). It includes not only the commands that affect the game, but also the debug and statistics commands. It also takes care of the start of a game and the end of a turn since I considered it is the engine's responsibility (but could easily migrate these to the table).
This class is the main functionality of a placeable card.
It contains all the getters and setters needed to interact with a minion card and an abstract method for a card's ability. This method is / can be implemented by all the specific types of minions.
Let X be a specific type of minion; X extends Minion.
To create all the specific minions, the class uses a Factory design pattern.
This way, there is no need for multiple if-clauses in the rest of the code, but only entries in the NAME_TO_CLASS
map.
This class is the main functionality of a hero card.
It contains all the getters and setters needed to interact with a hero card and an abstract method for a card's ability. This method is / can be implemented by all the specific types of heroes.
Let X be a specific type of hero; X extends Hero.
To create all the specific heroes, the class uses a Factory design pattern.
This way. there is no need for multiple if-clauses in the rest of the code, but only entries in the NAME_TO_CLASS
map.
NOTE: Why I chose to have really distinct implementations for Heroes and Minions is because I thought it would be a better idea when thinking of expanding the functionality of the game.
To add a new concrete object, we only need to add a new entry in the NAME_TO_CLASS
map.
Then whenever we need to create a concrete object, we use the createX() method.
This method looks up the NAME_TO_CLASS
map and retrieves the correct constructor for the concrete object.
In case of failure, it throws an exception.
This class is responsible for what is happening within the game itself (e.g: placed cards management, checking validity of attacks).
It takes care of respecting the game rules and throws all the encountered errors back to the caller (which is always going to be in GameEngine
).
I considered the players should be a component of the table, because they don't exist if there is no table, so their life times should be basically the same.
This class is responsible for all the player specific actions like: choosing the deck and managing the hand, the hero, and the mana. (aggregation)
It contains methods to retrieve information about the current hand, to shuffle the chosen deck, manage mana and extract cards from the hand.
These are all operations performed by the Table
.
I couldn't find a better/more generic way to handle the actions and the output mapping without sacrificing either of them.
- GameEngine could have benefited from the Singleton design pattern because the program only needs one instance.
- I am proud of the Factory design pattern in Minions and Heroes, but I think I would have been better off with Visitor, had I known of its existence.
- Could have done a better job regarding rows like having the Player hold their front and back row instead of the Table.
- Should have had an interface for a Card and the Minion and Hero should have implemented it.
- Should have drawn more distinctive lines between GameEngine and Table.
PS: At the time of coding I did not know about a lot of these concepts like interfaces and Visitor design pattern.