Skip to content

Files

Latest commit

 

History

History

docs

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

LPOO_35 - FarmVille

FarmVille was an agriculture-simulation social network game developed and published by Zynga in 2009.

In this text-based implementation, you can manage a farm, including its cropfields and stockyards. You can also go to the market where you can buy and sell items, a house where you can rest or save the game, and a warehouse where your items are stored.

In this exciting new version you need to pay attention to the weather! Rain is beneficial for your crops and animals, but if a storm comes, or there is too much sun, you might lose some or even all of your hard work.

This project was developed by Diogo Costa ([email protected]), Pedro Gonçalo Correia ([email protected]) and Rui Alves ([email protected]) for LPOO 2020-21.

IMPLEMENTED FEATURES

  • Movement - The farmer may be controlled with the WSAD keys, moving as long as there isn't any obstacle obstructing his way and he is inside the playable region.
  • Interaction with buildings - Edifices, crop fields and stockyards have light brown paths to mark interactive positions. Pressing the SPACE bar when the farmer is in one such position will trigger an interaction with that building (typically opening a popup menu).
  • Crop fields - The crop field is used to plant crops. After planting, the crop takes some time to grow, during which the farmer can remove the crop if he changes his mind. The crop's type and its gradual growth can be seen visually when looking at the crop field: the crops grow as the harvest time is approaching. When the crop is ready to harvest the farmer may harvest it.
  • Crops - The farmer is able to plant several types of crops: parsley, corn, carrots and wheat. Each has different seed prices to be planted, base harvest amounts and required time to grow.
  • Stockyards and animals - Besides crop fields, the farm may also have stockyards. The animals must be fed in order to produce something. For example, the farmer may feed cows with wheat, wait some time, and then obtain milk from the cows. In the case of chickens, they are fed with corn, and after some time they produce eggs. In each stockyard, animals can be bought up to a given amount. More animals means more production, however, it also means more food to feed the animals. If there are too many animals, it may become too hard to harvest the required food. In that case, it might be worth considering selling some animals.
  • Weather - From time to time (by random chance based on the current weather), the weather can change, with possible weathers being sunny, cloudy, rainy, windstorm and thunderstorm. The current weather affects the crop fields and animals (positively or negatively). For example, a sunny weather will dry the plants and wear out the animals, and a windstorm may damage your harvest and production. On the other hand, rain is beneficial for your farm. Yet, after the plant is ready to harvest (or the animal product is ready to collect), even beneficial weathers will make them rot over time, instead of boosting them: it is important to harvest and collect as soon as possible!
  • HUD - At the bottom of the farm screen, a HUD bar is displayed where the current time, weather and your money may be seen.
  • Resting - The farmer may enter the house to rest at any time, making the in game time go faster so that the player doesn't have to wait so long for the crops to grow or the animals to produce.
  • Saving and loading game - The house may also be used to save the current game state, which will be stored in a file to be loaded at a later time.
  • Popup Menus - Popup menus are UI elements with labels and buttons that may be clicked with the mouse (and are highlighted when hovered with the mouse). These popups do not occupy the whole screen, so it is still possible to see part of the farm behind it. While a popup is opened, time in the farm passes as normal, unless it is the pause menu. However, the farmer or any menus behind it cannot be moved until that menu is closed.
  • Main menu and pause menu - When the game starts, instead of directly opening the farm, the main menu is opened, where a new game may be started. If there is currently a save file, it can loaded instead. There is also a button to exit the game. Additionally, while in game, the player may pause the game with the ESCAPE button, which will stop time in game and open a popup menu asking if the user wants to return to the main menu.
  • Inventory and warehouse - When the farmer harvests crops or gets products from animals, they are stored in the warehouse. The farmer may go to the warehouse to see the products currently stored. Be careful, however, as there is a limit to the amount to products stored in the warehouse. It might be a good idea to sell items before that limit is reached, because if there is not enough space for more products, any new products obtained will be discarded.
  • Market and currency - The market is where the farmer may go to sell the products obtained in order to get money. It's also the place to build new crop fields or stockyards. Any new constructions must be placed without intersecting existing constructions. Furthermore, it is possible to demolish existing stockyards or crop fields (as long as one crop field always remains in the farm). While there is a cost to build, there is no cost (nor return) to demolish.

Farm overview

docs/screenshots/farm_overview.png

The crop field plant menu

docs/screenshots/plant_menu.png

Farmer trying to build a new crop field in an invalid position

docs/screenshots/new_building_blocked.png

PLANNED FEATURES

  • Tools - Tools may be bought (and possibly upgraded) from the market to aid the farmer in his work. There are many possible examples. A water bucket may be filled in a fountain and used to water the plants the animals (which gives harvest and collect bonuses). A hoe may be used to plow crop fields before planting crops (and thus get a harvest bonus). A knife may possibly be used to kill animals and get meat.
  • Upgrades - At the market there may be a possibility to buy upgrades to the main edifices. Upgrading the House will make the time pass faster while resting (it's easier to rest in a comfortable house). Upgrading the market will increase the sell price of items and decrease the building price and upgrade price (for tools and edifices). Upgrading the warehouse will increase its capacity.

Tools menu

docs/mockups/tools_menu.png

Upgrade menu

docs/mockups/upgrade_menu.png

DESIGN

Transition between screens (for example between the game and menus)

Problem in Context

When applying the MVC architectural pattern, one problem that arises is that, depending on the state of the game (for example whether a menu is opened), different controls, views or even models may be used. Handling all those possibilities in the GameController class would be a violation of the Single Responsibility Principle and require heavy use of conditionals to determine the appropriate behavior based on the context. Moreover, many of the states are similar: there are only two big different types of states (menus and farm) but many variants of each. For example in the farm the player may be controlling the farmer, resting, controlling the demolition marker or (when building) the new building position. The menu might be a popup menu or the main menu.

The Pattern

We have applied the State pattern. With this pattern, each possible state of the controller is represented in a different subclass of GameControllerState. Each concrete state controls its own behavior regarding the keyboard, the mouse and the passage of time, and has a viewer and the relevant model associated. The state can be set in the GameController class so that it is possible to switch to a different state of the application by switching to another subclass. This pattern allowed to address the identified problems because the code related to each particular state can be separated in different classes (Single Responsibility Principle), new states that may be introduced in the future can be in their own class instead of changing the existing classes (Open/Closed Principle), and the heavy use of conditionals is avoided.

Implementation

In order to handle similar variants of the states, two abstract state classes where created implementing GameControllerState: FarmController and MenuController. Each has concrete subclasses for each of the possible variants.

The following diagram shows how the pattern’s roles were mapped to the application classes. diagrams/implementation_p1.png

These classes can be found in the following files:

Consequences

The use of the State pattern to solve this problem has the following benefits:

  • The possible states of the controller become explicit in the code, instead of relying on flags and conditionals. The code is more organized and it is easier to navigate to each state's logic.
  • Polymorphism is used to select the correct state. New states can be added simply by creating a new class implementing GameControllerState with the desired functionality.
  • Each controller state may have its own model and viewer associated.
  • If two or more states are very similar, code duplication may be avoided using inheritance.

Popup menus

Problem in Context

Some actions in the game result in a popup menu being opened. Popup menus are a state of the controller that attaches new behavior to the previous state. Creating a new subclass of the base state for each popup menu would lead to code duplication. Adding the popup logic to the existing states would be a violation of the Single Responsibility Principle and also of the Open/Closed Principle as each new popup would imply modifying the superclass.

The Pattern

We solved this problem with the Decorator pattern. The PopupMenuController class is a decorator that wraps the previous state of the controller, removing its reactions to the keyboard and mouse (but not to the passage of time) and adding its own. This new controller has a viewer that is itself a decorator of GameViewer (the base viewer class), showing the popup menu on top of the previous viewer. The popup may be closed by setting the GameController state to the PopupMenuController's inner state. With this pattern, the behavior of existing states was extended without making a new subclass for each. Furthermore, the Single Responsibility Principle and the Open/Closed Principle are conserved as this new class has the sole responsibility of adding/removing behavior to existing classes at runtime instead of modifying their code directly.

Implementation

Like in the case of the controllers, there are two abstract classes for the two different types of viewer states, and each concrete viewer state is a subclass of one of the two.

The following diagrams shows how the pattern’s roles were mapped to the application classes. The first diagram shows the use of the decorator pattern in the controller. The second one shows the use of the decorator pattern in the viewer. diagrams/implementation_p2a.png

These classes can be found in the following files:

diagrams/implementation_p2b.png

Consequences

The use of the Decorator pattern to solve this problem has the following benefits:

  • New states of the controller can have popup menus using this same decorator.
  • PopupMenuControllers implement GameControllerState, so they may wrap other popups. Nesting popups is in itself very useful as it allows, for example, the use of confirmation windows inside popup menus.

Create many different menus

Problem in Context

In the game there are many different menus, with different sizes, buttons and labels. It is important to be able to generate them without too much code duplication. In order to conserve the Single Responsibility Principle, it is also important to separate the creation of each menu from the menu controller and model themselves.

The Pattern

We created an abstract class MenuControllerBuilder based on Factory Method to build the menus. This class is responsible for building a menu and declares several factory methods (to get the menu width, height, buttons, labels, etc.) to be implemented in subclasses.

Each concrete creator implements those methods, returning the properties of the menu they are responsible to create. The Single Responsibility Principle is thus conserved, as the definition of the properties and the creation of the menu are separated. New menus can be easily implemented without changing existing ones, thus conserving the Open/Closed Principle. New menus may extend existing ones by subclassing them, however, they should never remove buttons or labels from their superclass so that they are extending their functionality instead of changing it. For that, the respective factory methods always call the respective superclass method before adding the new buttons/labels.

Implementation

The following diagram shows how the pattern’s roles were mapped to the application classes. Because there is a very large number of menu builders, menu controllers and products, only some were included in the diagram for brevity. The return of each factory method (all protected methods in the diagram are factory methods) is a product. diagrams/implementation_p3.png

These classes can be found in the following files:

Consequences

This approach has the following benefits:

  • The definition of the properties of the menu and its creation are separated, thus conserving the Single Responsibility Principle.
  • New menu builders may be added without changing existing builders, thus conserving the Open/Closed Principle.
  • Menus may reuse buttons and properties by inheriting from more generic builders.

Action of the buttons

Problem in Context

The menus are composed of a list of buttons and labels. Each button is intended to produce a certain action specific to that button on click. This means that it must store in some way that action, so that the controller can execute it when reacting to a mouse click on that button. Otherwise, there would be no way for the controller to know how to respond to that click. One possibility would be to delegate to each button the execution of its action, however, as the button is part of the model, that would violate the MVC pattern, as the model should just store the content of the game and not control it (thus also violating the Single Responsibility Principle).

The Pattern

We used the Command pattern to tackle this problem. It turns each action into a stand-alone object that contains all information about it. Each button controller stores a command and executes it when it detects a click in the respective button. There are many concrete commands executing actions on some receiver. With this pattern, the model does not know anything about the command or the controller, thus not violating the MVC pattern. Besides, the Single Responsibility Principle is also conserved, as the invoker isn't responsible for knowing the concrete action associated with the button, but instead it just executes the stored action when it is clicked.

Implementation

The following diagrams shows how the pattern’s roles were mapped to the application classes. Because there is a very large number of commands, only some of the commands and respective receivers were included in the diagram. The clients are typically subclasses of MenuControllerBuilder, but were not included in the diagram for more simplicity. diagrams/implementation_p4.png

These classes can be found in the following files:

Note that a similar design was implemented in FarmBuilder to build a new Farm. In that case, the only concrete creator is NewGameFarmBuilder.

Consequences

The use of the Command pattern to solve this problem has the following benefits:

  • The responsibilities of creating the action (e.g. menu builder), invoking the action (button controller) and performing the action (command) are all separated in different classes. This is a benefit, because those steps happen in different moments of the program (conserves the Single Responsibility Principle).
  • New commands can be added without breaking existing code (Open/Closed Principle)
  • After the command is created, its execution can be deferred until the button is clicked.
  • The commands are stand-alone objects that may be used to solve other problems regarding generic actions other than buttons (for example, interactions between the farmer and buildings).

Some buttons execute multiple actions

Problem in Context

Some buttons have actions that may be divided into multiple existing simpler actions. An example of this is when a button to plant a crop is clicked. This button must produce the action of planting the crop but also of closing the popup menu. Creating a different command for every such action might lead to code duplication when the existing commands could have been reused.

The Pattern

We used the Composite pattern to tackle this problem. A new command CompoundCommand contains a list of commands and when executed it executes all commands in that list in order. This allows the buttons that need to execute complex actions to create a CompoundCommand and add to it all the simpler commands that composes it, thus reducing code duplication and creation of other command classes.

Implementation

The following diagram shows how the pattern’s roles were mapped to the application classes. diagrams/implementation_p5.png

These classes can be found in the following files:

Consequences

The use of the Composite pattern to solve this problem has the following benefits:

  • A ButtonController may store a command that is actually composed of many smaller commands without having to know it.
  • Code duplication is reduced as instead of many complex commands there is only need to implement a set of simpler ones.

Some buttons actions depends on conditions

Problem in Context

Some buttons must execute different actions depending on some condition. For example, the FEED button in the stockyard menu when the animals are not producing must execute the FeedAnimalsCommand if there is enough food in the inventory, or open an alert popup otherwise (or when there is no animal in the stockyard). Moreover, this condition should not be evaluated when building the menu, as it can change later (for example, the user may buy an animal in the same menu, thus becoming able to feed them).

The Solution

In order to solve this problem, we implemented a ConditionalCommand which receives a condition and may set a command to execute when the condition is true and another to execute when the condition is false. The command only evaluates the condition when executed. To ease the creation of instances of this command, it is implemented with a fluent builder pattern design in mind. Its setters return the instance itself. There is also a method elseIf() which receives a condition and creates another instance of ConditionalCommand, nesting it in the ifFalseCommand of the current one, and returns the new one. This way, "else if" conditions can be added in a easy to read way, while keeping the structure of the code flat.

Implementation

The following diagram shows the relevant classes in the solution of this problem. diagrams/implementation_p6.png

These classes can be found in the following files:

Consequences

This solution has the following benefits:

  • This solution allows the condition to be evaluated lazily, at the time of the execution of the command, thus there is no risk of changing between the evaluation and the execution.
  • This solution allows to reuse existing simpler commands to create more complex commands with conditions, thus helping in reducing code duplication.
  • The setters and elseIf() methods allow creating instances of this class in a easy to read way, without losing too much of the readability of traditional if statements.

Switching menu when ready to harvest

Problem in Context

After designing the Popup menus and the respective builders as described previously in the report, one problem that remained was that the menu shown when interacting with a crop field while a crop is growing should switch to the harvesting menu when that crop is ready to harvest. At the time of the intermediate delivery of the project, menu builders did not build controllers and could only define their buttons and labels, width and height, and title, so there was no way for the menu to check without user input if the crop is ready to harvest. This meant that the menu would remain in the growing state after the remaining time to grow reached zero.

The Solution and implementation

After trying to come up with design solutions or alternatives and trying to identify the root issue with the design at the time, the decided solution was to create a subclass of PopupMenuController specifically to extend it with a reaction to the passage of time. This class receives a Command in its constructor to determine what should happen when time passes. The menu builders have a method that returns the MenuController they intend to build so by overriding this method, the CropFieldGrowingMenuControllerBuilder and the ProducingMenuControllerBuilder can create an instance of this subclass of PopupMenuController, adding a command that checks if the crop or product is ready and switches the menu if that's the case.

The following diagram shows the relevant classes in the solution of this problem. diagrams/implementation_p7.png

These classes can be found in the following files:

Consequences

  • This solution is generic enough that it can be applied both to the crop fields and to the stockyards, as well as other menus that might need in the future to execute an action or check periodically.
  • However, this solution creates a new subclass of PopupMenuController for a very specific reason and forces menu builders to know and decide the menu controller they will create.

Define interaction of each building

Problem in Context

Each building in the game, such as the house and the crop field, may have an interactive zone and an action associated with that interaction. The controller of each building would then have two responsibilities: checking whether the given position is inside the interactive zone and if so do the action of that building. This is a violation of the Single Responsibility Principle and also results in code duplication as each building would reuse the same logic to check if the given position is inside the interactive zone. This would in turn result in the Shotgun Surgery code smell, as if that check were to change, every building controller would be affected.

The Pattern

To solve this problem we took advantage of the Command interface and applied the Factory Method pattern. The BuildingController is an abstract class that declares an abstract method to return a Command representing the interaction action and implements a method to react to the interaction of a generic building. There is a controller extending this class for each building, implementing the abstract method to return the relevant Command. This way, the creation of the Command is separated from its execution, as the later requires checking whether the interaction took place at that building's interactive zone, thus conserving the Single Responsibility Principle. The code duplication is also avoided because that check is done in the BuildingController class instead of being in every concrete building's controller. This design was also applied to the case of demolishing buildings, as the mechanism is very similar.

Implementation

Because the house, market and warehouse have the same demolish reaction, their respective controllers are subclasses of EdificeController, which implements the getDemolishCommand() method.

The following diagram shows how the pattern’s roles were mapped to the application classes. diagrams/implementation_p8.png

These classes can be found in the following files:

Consequences

The use of the Factory Method pattern to solve this problem has the following benefits:

  • The creation of the Command and its use are not tightly coupled.
  • All subclasses of BuildingController (including new ones) only need to define its interaction commands and not the logic to detect an interaction thus preserving the Single Responsibility Principle.
  • The Command of the interaction for each building is independent from the others, thus preserving the Open/Closed Principle as new buildings can be inserted without breaking existing code.

Regions

Problem in Context

There were many places in the program where there were logic to check if something is within a given region. However, many of those regions were rectangles, which led to code duplication. Some examples included:

The Solution

To solve this problem we refactored the code, creating a new interface to represent a region. We then created classes to represent the types of regions needed, most notably the rectangle. Instead of repeating the logic of checking whether something is inside a rectangle or some other region, the rest of the code now creates a Region and uses it to do the needed checks.

Implementation

The following diagram shows the relevant classes in the solution of this problem, plus two
examples of the regions being used (in Button and in BuildingController). diagrams/implementation_p9.png

These classes can be found in the following files:

Consequences

  • This approach reduces code duplication.
  • This approach is in favor of the Single Responsibility Principle because logic to calculate if something is within a region is separated from the rest of the code.
  • It's easy to create new types of regions as needed, thus preserving the Open/Closed Principle.

CropField States

Problem in Context

Depending on whether there is a crop or not planted on the crop field, and whether it is ready to harvest, the behavior of some methods of the crop field should be different. Besides that, the crop field must become ready to harvest when the time reaches zero or not planted when the harvest amount reaches zero. An analogous problem exists in the stockyard, for the products of the animals.

The Pattern

To solve this problem we used the State Pattern. Each crop field has a state, declared as an interface, and there are three concrete possible states: not planted, planted, and ready to harvest.

In this way, the Single Responsibility Principle is conserved, because each class is responsible only for the behavior of its state, and the Open/Closed Principle, as it is possible to add more states without modifying existing ones.

A very similar solution was used for the stockyard, as the problem was similar.

Implementation

The following diagram shows how the pattern’s roles were mapped to the application classes. For brevity, only the case of the crop field (not the stockyard) was included, as both are very similar: diagrams/implementation_p10.png

These classes can be found in the following files:

A very similar application of the state pattern, but for stockyards, can be found in the module:

Consequences

  • The possible states become explicit in the code, instead of relying on flags and conditionals. The code is more organized and it is easier to navigate to each state's logic.
  • Polymorphism is used to select the correct state. New states can be added simply by creating a new class implementing GameControllerState with the desired functionality.

KeyboardReactor for elements controlled by the user

Problem in Context

Because there are many elements that may be controlled by the user (albeit only one at a time), such as the farmer, the demolish marker and new buildings, the code for the respective controllers was very similar, as can be seen in:

The Pattern

To solve this problem we used the Template Method Pattern. A new abstract class with the common functionality was created and all three problematic classes became subclasses of this new one.

Two step methods were created in the base class, getEntityPosition() and moveEntity(Position position), implemented in the three subclasses with the logic specific to each. In this way, code duplication was reduced.

Those steps are used by the super class in its template method reactKeyboard(GUI.KEYBOARD_ACTION action).

Implementation

The following diagram shows how the pattern’s roles were mapped to the application classes. diagrams/implementation_p11.png

These classes can be found in the following files:

Note that the FarmerController uses the farmer inside the Farm model, because the farmer must always exist (even if not being controlled or shown in the screen) so that when the control returns to the farmer, it is still in the same position.

Consequences

  • This pattern reduced code duplication.
  • The concrete classes only have to implement their specific part of the algorithm, which is in favor of the Single Responsibility Principle.
  • New similar controllers controlled by the user may be added without having to duplicate code or changing existing classes.

KNOWN CODE SMELLS AND REFACTORING SUGGESTIONS

Alternative Classes with Different Interfaces

The CropFieldState and StockyardState interfaces have a great number of common methods. Even though some of them have different names, the semantics are the same. Furthermore, the classes that implement both interfaces have very similar logic. CropFieldState is implemented by NotPlanted, Planted and ReadyToHarvest while StockYardState is implemented by NotProducing, Producing and ReadyToCollect. These two different implementations lead also to Duplicate Code.

To solve this problem, since both interfaces have very similar methods, we should start by Rename Method to make the similar methods have also the same name. After that, we could have CropFieldState and StockyardState extend a common interface: Produceable, extracting the common methods into this new interface. This interface could be implemented by three abstract classes, representing the three states analogous to the states already mentioned for the cropfields and stockyards. These classes would implement the logic that is common to both cases. Finally, concrete states can extend the respective abstract state and implement the methods that are specific to the cropfield or stockyard, respectively.

Data Class

The Farm class is a Data Class since its only responsibility is to store fields and access them with getters and setters. Furthermore, since it has a great number of attributes it forces the class to have also a great number of setters and getters making it Large Class too, bloating the code.

To solve this smell, we could use Move Field and Move Method from Farm to the respective controllers of the attributes moved. For example, the Weather attribute could be moved to the WeatherController eliminating the need for the WeatherController to have an instance of the whole farm.

This could be applied to other controllers, so that each controller would have an instance of the respective model (similar to what ButtonController currently does) and also of the respective viewer, manipulating the model and calling the viewer when necessary. In that case the instances of the controllers would have to be stored, instead of being created each time they are used.

Lazy Class

The PositionRegion class was designed to represent a region with a single position. Thus, it ended up having an instance of a Position and implementing only a single and simple method of the Region interface.

To tackle this problem we could make Position class implement the Region interface nullifying the need for the PositionRegion class. However this might not be worth it, because both classes are semantically different, even if the implementation of PositionRegion is too simple.

SOLVED CODE SMELLS

In this section we describe the code smells identified in the intermediate report and how we solved them.

Middle man

Many of the viewer classes (for example, FarmerViewer) were Middle Men as their sole purpose was to delegate the drawing work to the respective drawer (gui) class. This added needless complexity to the program and also made testing the viewers harder (if the viewer is just delegating work to the drawer, its tests would be almost identical to the ones of the drawer and it couldn't be tested isolated from the drawer).

To improve the code, we used the Inline Class refactor, inlining the respective drawer class into the viewer class. In the concrete example given, the draw method of FarmerDrawer was moved to FarmerViewer, replacing the viewer's draw() method that only delegated work. After that, FarmerDrawer was removed.

Data class

The classes that were in model.weather and in model.farm.building.crop_field.crop were Data classes that only stored constants and included getters for those constants. These classes only bloated the code and were replaced with concrete instances of a single class.

The solution was, for each of the mentioned packages, to store the relevant constants in a file and use Collapse Hierarchy, merging the classes of each of those packages into a single class. The set of concrete instances was then loaded from the file.

Duplicate code

The classes that were in controller.weather.state had very similar logic in their updateWeather methods. Besides being Duplicate code, it was a long chain of if statements that made it harder to add more weathers or modify the behavior of existing ones.

To solve this problem, we first solved the Data Class code smell identified in the previous section in the package model.weather. The created file that stores the constants for each weather also stores the probabilities of weather change. This means that the weather instance passed to updateWeather contains the information relative to those probabilities. The chained if statements were replaced with a for loop iterating through all probabilities and respective weather changes. Then, we used Collapse Hierarchy to merge all the classes in controller.weather.state, because at that point they had identical updateWeather methods. Finally, the resulting class was inlined in WeatherController because it no longer made sense to have a state and thus both classes had the same responsibility.

TESTING

COVERAGE REPORT

Coverage Report

SELF-EVALUATION

  • Diogo Costa: 33%
  • Pedro Gonçalo Correia: 37%
  • Rui Alves: 30%