Caml Defense is a tower defense game developed in OCaml as the final project for the course CS3110: Data Structures and Functional Programming.
My contribution includes the implementation of some parts of the game controller, the graphical user interface (GUI) component of the game, its integration with the game controller.
As shown in the diagram above, the key components are:
Similarly to TAG, the JSON represents a tower defense game, with fields like the monsters and their HPs and speeds, the towers and their types, etc. This JSON is inputted in order to start a game.
The parser is a filter, which takes in the JSON file and outputs a game state (a type in the mli file) to the back-end.
The back-end is the server of the client-server system between the back-end and the user. It stores data about the game state, as well as do computations to move the monster, buy and sell towers, etc.
The GUI is our ideal implementation to allow the user to play the game. It is the interface through which the client (the user) interacts with the database (the back-end), that then uses the back-end code to change the game state. The user clicks to do things like buy and sell towers, and the gold and lives are always displayed.
Text-based user interface (TUI)
The text-based user interface is our minimal implementation to allow the user to play the game. It is the interface through which the client (the user) interacts with the server (the back-end). Similarly to TAG, the user inputs commands like “buy fire tower” into a REPL, that then uses the back-end code to change the game state.
The user is simply the person playing the game. They interact with the back-end via either the GUI or the TUI by placing and selling the towers. The user is the client in the client-server relationship between the user and the back-end.
The basic modules' dependencies are as follows:
Game State module
The game state module is be the module in charge of storing and updating information about the game state. This is the information that will be exposed through the GUI and TUI, and that will the changed by interactions with the user, as well as other information. It also stores where all the monsters and towers are at a given time using a Hash table, how much gold a player has, the HP of the base and so on. It also contains the damage_position function, as that is called by both tower and monster.
The monster module computes the actions of the monsters for a round, and has the function that initializes monster. There are three different levels of difficulty, Easy, Medium, and Hard. The AI first computes the path from a coordinate to the base for different monsters by calling easy_actions/medium_actions/hard_actions. Then upgrade_monsters steps the old state by following these computed path. These functions in the monster module are called by both the GUI and the TUI.
The tower module holds the functions that deal with towers (placing, selling, updating and shooting the towers). These functions are called by both the GUI and the TUI.
The update module computes the movements for the monsters, places towers, upgrades tower, shoots towers, initializes monsters onto the board, and sells towers as necessary based on the use input.
The printer module handles the printing of the ascii representation of the map and well as other text-based information about the map, such as the hps of the towers and monsters and their placements. It also contains the longer print statements that are printed in the beginning of the game, so as to keep the text from cluttering the TUI code. It also makes it simpler to change print messages, as they are largely all located in the same place.
The GUI module provides the ideal interface through which the players can interact with the game to do things like place and sell towers, as well as to watch the game unfold and see the monsters move across the screen and see the towers shoot at them. We initially tried to implement this using lablgtk, but switched to Graphics and had much greater success with that.
The TUI module provides the minimal interface in order for primitive interactive testing and playing. During the game play, the player can type the valid commands as strings given in our instructions in the game play console, such as “Upgrade (1,3)”, “Place Fire Tower (1,4)”. The types of towers are pre-specified in a provided game_state.mli file. The REPL section and the game display section are independent from each other, so that the user can send their commands any time during the game. When the REPL takes in a new command, the command get piped into the game state to be displayed in the next updating interval.
At the beginning of each game, the Parser module pipes and processes a valid json file into a game state by initializing the map, tower type, monster type, initial gold, etc. If the map happens to invalid, the parser would report error in the early stage.
Our game maintains the gamestate module as the primary data structure during the game play. The state, as shown in the mli file, is a record that contains the round number, a map of the game, the game difficulty, and the gold. The game map is a hash map mapping positions to what is at that position -- either a tower, monster, base, monster cave, or Nil. While Hashtbl There are also a few variants for types like the type of towers and monsters and commands inputted by the user.