Software Engineering virtual experience program on Forage

In this job simulation, I experienced what life as a Software Engineer at EA would be like.

This simulation comprised 4 Task to be completed at my own pace.

Task One

Scenario

You work as a Junior Software Engineer at Electronic Arts. You are on the team responsible for developing the Sims series. The Sims 4 has already been released, but your team is still issuing updates which add content to the game and squash newly discovered bugs.

You are responsible for thinking of and building a new feature for the game. 

What is a Feature Proposal

A Feature Proposal is a document which describes a new feature you’d like to add to an application. When you have a great idea, you need some way to communicate it to your team. Feature proposals provide a handy format for conveying these ideas and recording them for posterity.

My Feature Proposal


Quick Hairstyle Switch


Overview:
The Quick Hairstyle Switch feature allows Sims to save multiple hairstyle looks that can be easily switched at mirrors and dressers. This feature enhances the realism of daily life by enabling Sims to change their hairstyles without entering the Create-A-Sim (CAS) mode, similar to how a user might quickly restyle their hair in real life.


Value Statement:
Introducing the Quick Hairstyle Switch feature would enhance gameplay by making it more dynamic and user-friendly. Players can express their Sims' personalities more fully by easily changing hairstyles to match different activities or moods. This addition would reduce repetitive trips to CAS for minor adjustments, providing a smoother and more immersive experience.


Solution:
The Quick Hairstyle Switch feature integrates with existing game mechanics by extending the current CAS system. Players can save multiple hairstyles for each Sim in CAS, which will be accessible through a new "Hairstyle Favorites" menu when interacting with mirrors and dressers. This feature will use a similar interface to the outfit management system, making it intuitive for players. Hairstyles will be independent of the Sim's current outfit, allowing for a variety of style-hairstyle combinations with greater ease. Selecting a saved hairstyle will instantly update the Sim's appearance without leaving the active gameplay mode, allowing for quick and seamless changes. Additionally, special hairstyling animations may be introduced to improve the user experience and make hairstyle transitions smoother.


Evaluation Statement:
Moving forward with the Quick Hairstyle Switch feature would provide numerous benefits and some challenges. On the positive side, this feature would enhance realism by allowing quick hairstyle changes that reflect real-life routines, adding depth to daily interactions. It would also improve user experience by streamlining gameplay, reducing the need to frequently enter CAS, thus saving time and maintaining immersion. Furthermore, increased customization options would allow players to better express their Sims' individuality and adapt their looks to different situations, enhancing overall player satisfaction. However, introducing more customization options may initially overwhelm new players, increasing the complexity of the game. While the development effort required is significant, the potential to enhance player satisfaction and retention justifies the investment. Overall, the Quick Hairstyle Switch feature would significantly enrich the game by offering players a practical and enjoyable way to manage their Sims' appearances, enhancing both realism and gameplay enjoyment.


Task Two

Scenario

Your proposal was a hit! Everyone on your team is excited to include the new feature in the next Sims 4 update. Your project manager has split the rather large job of developing the entire feature into several more granular coding tasks (tickets) that can be accomplished by individual developers.


The first order of business is to figure out how to break the behavior described in the ticket into a collection of objects. Once you have an idea of what sort of objects you’re going to be working with and how they relate, you can move on to their implementation.

In this case, you figure it would be helpful to use a Class Diagram to visualize the subsystem. This will help you reason about how the objects are related to one another, and help you catch caveats and edge cases you might otherwise miss.

Class Diagram

The intention of the exercice was not to capture the entire system but rather to visuale the implementation needed for the feature proposed. Therefore I imagined some parts of the current Sims 4 code base, building upon it and create this class diagram for my proposal.

Task Three

Scenario

It’s time to write some actual code! All of the objects you outlined in the previous step need to be implemented in C++, a high performance coding language. In this task, you will be defining several classes in a single header file, without including their implementations.

QuickHairstyle.h

#pragma once

#include <string>
#include <vector>
#include <map>

// Enumeration for WearableCategory
// Note: This enum does not cover all categories needed in a full implementation.
enum class EWearableCategory 
{
    Hair,
    Top,
    Bottom,
    Shoes
};

// Abstract class Wearable
// Represents a general wearable item, such as hair, tops, bottoms, or shoes.
class Wearable 
{
public:
    virtual ~Wearable() = default;
    virtual std::string getName() const = 0;
    virtual EWearableCategory getCategory() const = 0;
    virtual void wear() = 0;
};

// Enumeration for OutfitType
 // Note: This enum does not cover all types of outfits needed in a full implementation.
enum class EOutfitType 
{
    Casual,
    Formal,
    Athletic,
    Sleepwear
};

// Class Outfit
// Manages a collection of wearables that make up an outfit.
class Outfit {
public:
    void addWearable(Wearable* wearable);
    std::vector<Wearable*> getWearables() const;
    void wearOutfit();

private:
    EOutfitType m_type;
    std::vector<Wearable*> vec_wearables;
};

// Class Sim
// Represents a Sim character with the ability to change outfits and save wearable items.
// All the other methods of a Sim are neglected in this exercise.
class Sim {
public:
    Sim(const std::string& name);
    std::string getName() const;
    EOutfitType getCurrentOutfitType() const;
    Outfit* getCurrentOutfit() const;
    std::vector<Wearable*> getSavedWereable() const;
    void changeOutfit(EOutfitType type, Outfit* outfit);
    void changeWaerableInCurrentOutfit(Wearable* wearable);
    void saveWearable(Wearable* wearable);

private:
    std::string m_name;
    EOutfitType m_currentOutfit;
    std::map<EOutfitType, Outfit> map_outfits;
    std::map<EWearableCategory, Wearable*> map_savedWearables;
};

// Abstract class Action
// Defines a general action that can be performed by a Sim.
class Action {
public:
    virtual ~Action() = default;
    virtual void act(Sim* sim) = 0;
    virtual void openActionMenu(Sim* sim) = 0;

protected:
    Action(const std::string& id); // Protected constructor to prevent direct instantiation
    std::string getId() const;

private:
    std::string m_id;
};

// Class ChangeWearableAction
// Represents an action that changes the wearable item of a Sim.
class ChangeWearableAction : public Action {
public:
    ChangeWearableAction(const std::string& id, Wearable* wearable);
    void act(Sim* sim) override;
    void openActionMenu(Sim* sim) override;

private:
    Wearable* m_wearable;
};

// Class InteractableObject
// Represents objects in the game that Sims can interact with.
// In our example we are interested in objects such as mirrors and wardrobes.
// All the other methods of a InteractableObject are neglected in this exercise.
class InteractableObject {
public:
    InteractableObject(const std::string& name);
    std::string getName() const;
    void addAction(Action* action);
    void openActionsMenu(Sim* sim);
    void interact(Sim* sim, Action* action);

private:
    std::string m_name;
    std::vector<Action*> vec_actions;
}

Task Four

Scenario

You’ve wrapped up work on the last ticket - those classes were quick to implement once you had their definitions mapped out. Another one down, it feels good to knock these out. You’ve submitted a pull request with your changes to the relevant git repo.

Soon, another engineer will review your changes and give you feedback, after which they’ll be merged in. It’s always better to have more eyes on a given piece of code.

You rummage through the sticky notes which briefly outline each ticket. A yellow one catches your eye - looks like a bugfix for the inventory system. Apparently items aren’t actually going anywhere when they’re removed. Can’t have that. Time to make a patch!

Task description

The current version of the inventory system does not fully implement item removal. An object removed from the inventory is left in the item list with a quantity of 0. Implement a way to fully remove an item from the inventory when its quantity reaches 0. While reviewing the code, consider whether there are data structures more appropriate for item storage, or if there is code that can be streamlined and condensed.

Inventory patch

#include <string>
#include<iostream>
#include <utility>
#include <unordered_map>
#include <memory> 

class Item {
private:
    std::string name;
    int quantity;
    float price;

public:
    Item(
        std::string name,
        int quantity,
        float price
    ) :
        name{ std::move(name) },
        quantity{ quantity },
        price{ price } {

    }

    std::string get_name() const {
        return name;
    }

    int get_quantity() const {
        return quantity;
    }

    void set_quantity(int new_quantity) {
        quantity = new_quantity;
    }

    float get_price() const {
        return price;
    }

    bool is_match(const std::string& other) {
        return name == other;
    }
};

class Inventory {
private:
    std::unordered_map<std::string, std::unique_ptr<Item>> items;
    float total_money;

    static void display_data(Item& item) {
        std::cout << "\nItem name: " << item.get_name();
        std::cout << "\nQuantity: " << item.get_quantity();
        std::cout << "\nPrice: " << item.get_price();
    }

public:
    Inventory() :
        total_money{ 0 } {

    }

    void add_item() {
        std::string name;
        int quantity;
        float price;

        std::cin.ignore();
        std::cout << "\nEnter item name: ";
        std::cin >> name;

        auto it = items.find(name);
        if (it != items.end()) {
            std::cout << "\nItem already exists.";
        }
        else {
            std::cout << "Enter quantity: ";
            std::cin >> quantity;
            std::cout << "Enter price: ";
            std::cin >> price;

            items[name] = std::make_unique<Item>(name, quantity, price);
        }
    }

    void sell_item() {
        std::string item_to_check;
        std::cin.ignore();
        std::cout << "\nEnter item name: ";
        std::cin >> item_to_check;

        auto it = items.find(item_to_check);
        if (it != items.end()) {
            remove_item(it->second);
        }
        else {
            std::cout << "\nThis item is not in your Inventory";
        }
    }

    void remove_item(std::unique_ptr<Item>& item) {
        int input_quantity;
        std::cout << "\nEnter number of items to sell: ";
        std::cin >> input_quantity;

        int quantity = item->get_quantity();
        if (input_quantity <= quantity) {
            float price = item->get_price();
            float money_earned = price * input_quantity;
            int remaining_quantity = quantity - input_quantity
            if (remaining_quantity == 0) {
                items.erase(item->get_name());
            }
            else {
                item->set_quantity(remaining_quantity);
            }
            std::cout << "\nItems sold";
            std::cout << "\nMoney received: " << money_earned;
            total_money += money_earned;
        }
        else {
            std::cout << "\nCannot sell more items than you have.";
        }
    }

    void list_items() {
        if (items.empty()) {
            std::cout << "\nInventory empty.";
            return;
        }

        for (const auto& pair : items) {
            display_data(*pair.second);
            std::cout << "\n";
        }
    }
};

// no need to modify anything here
int main() {
    int choice;
    Inventory inventory_system;
    std::cout << "Welcome to the inventory!";

    while (1) {
        std::cout << "\n\nMENU\n"
            << "1. Add new item\n"
            << "2. Sell item\n"
            << "3. List items\n"
            << "4. Exit\n\n"
            << "Enter your choice: ";
        std::cin >> choice;

        switch (choice) {
        case 1:
            inventory_system.add_item();
            break;

        case 2:
            inventory_system.sell_item();
            break;

        case 3:
            inventory_system.list_items();
            break;

        case 4:
            exit(0);

        default:
            std::cout << "\nInvalid choice entered";
            std::cin.clear();
            std::cin.ignore(INT_MAX, '\n');
            break;
        }
    }
}

Want to know more?

Checkout the simulation here:

© 2024 Federica Bucchieri. All Rights reserved

© 2024 Federica Bucchieri. All Rights reserved