Developed by:

Muhammad Grandiv Lava Putra (22/493242/TK/54023) Muhamad Farrel Adrian (22/505897/TK/55394)

Problem Statement

Retail stores face numerous challenges in optimizing their operations, especially from the aspect of the store layout. The objective of this project is to optimize retail store operations to increase sales by analyzing how different store layouts affect active customer numbers, needed items acquisition, and the time customers spend in the store. Specifically, we aim to determine the optimal store layout to maximize sales by strategically placing high-demand items, thereby improving customer flow and reducing the time customers spend locating and purchasing items.

Theories

The simulation and modeling approach is based on retail analytics theory, which emphasizes that store layout significantly influences customer behavior, including the time spent in-store and item acquisition patterns. According to Larso, Bradlow, and Fader’s (2005) work on “An Exploratory Look at Supermarket Shopping Paths”, optimizing customer paths and the placement of high-demand items can enhance overall store performance.

The simulation and modeling approach utilized in this study is grounded in retail analytics theory, emphasizing that store layout significantly impacts customer behavior, including time spent in-store and item acquisition patterns. According to Larson, Bradlow, and Fader (2005), optimizing customer paths and strategically placing high-demand items can enhance overall store performance. Agent-Based Modeling (ABM) is particularly well-suited for such simulations, as it captures the interactions between individual agents (customers) and their environment, leading to emergent behaviors that reflect real-world complexities. ABM allows for the modeling of heterogeneous agent behaviors and their adaptation over time, providing a nuanced understanding of how different store layouts affect customer actions and store outcomes (Bonabeau, 2002; Macal & North, 2010).

The MESA library, a Python-based ABM framework, offers tools for building, visualizing, and analyzing these complex simulations. It supports customization and extensibility, making it possible to simulate various retail scenarios effectively (Kazil, 2020). The simulation results highlight the effectiveness of different store layouts: the horizontal layout against the entrance (Scenario 1) was found to be the most optimal, maximizing customer engagement and exploration time. This aligns with theories suggesting that visible, accessible layouts encourage longer in-store times and increased item discovery, thus enhancing overall store performance (Hui, Fader, & Bradlow, 2009).

Overall, the integration of ABM and retail analytics through the MESA library provides a powerful approach to optimizing store layouts. The findings underscore the importance of strategic shelf placement and layout design in influencing customer behavior and store efficiency. By simulating various scenarios, retailers can better understand how to design spaces that maximize customer satisfaction and operational success (Larson, Bradlow, & Fader, 2005; Sorensen, 2009; Thaler & Sunstein, 2008).

System Modeling

Agents:

It models customer behavior, focusing on how customers interact with the store environment. Each client is initialized with a specific position in the store, a list of items to collect, and a decision engine to guide their actions. The client’s primary attributes include their current position, items needed, items collected, current action, past actions, and the total time spent in the store.

Clients operate by evaluating their surroundings, deciding on movements, and performing actions such as picking items from shelves or checking out at exits. They utilize a decision engine to make movement choices and determine their next steps based on the store layout and their remaining needs. If they still have items to be picked, they will roam the store. The relative weights of each category also affects the client behavior. They will be more likely to go to the category which has higher relative weights. If the items they needed are zero, they will navigate to one of the exit doors. Hence, the lesser the exit doors, the higher chance of the clients to spend more time browsing through the shelves in the store.

Throughout the simulation, clients update their state, record their actions, and eventually leave the store upon completing their shopping list.

class Client(Agent):
    def __init__(self, pos, items_to_get, model):
        super().__init__(pos, model)
        self.mind = DecisionEngine(self)
        self.x, self.y = pos
        self.pos = pos
        self.need = items_to_get
        self.have = Counter()
        self.action = None
        self.past_actions = []
        self.time_total = 0

    def display(self):
        return {
            "Shape": "rect",
            "text": self.need_count(),
            "w": 1,
            "h": 1,
            "Filled": "true",
            "Layer": 0,
            "x": self.x,
            "y": self.y,
            "Color": "white" if not self.action else "yellow",
            "text_color": "red"
        }

    @property
    def neighbors(self):
        return self.model.grid.iter_neighbors((self.x, self.y), 
        True)

    @property
    def surround(self):
        return self.model.grid.iter_neighbors((self.x, self.y), 
        moore=True, radius=15)

    def done(self):
        for n in self.need.most_common():
            if n[1] > 0:
                return False
        return True

    def need_count(self):
        total = 0
        for n in self.need.most_common():
            total += n[1]
        return total

    def step(self):
        pass

    def advance(self):
        self.time_total += 1
        if self.action:
            self.action.step()
        elif not self.using_shelves() and not self.using_exits():
            moves = [pos for pos in self._possible_moves() 
            if self.model.grid.is_cell_empty(pos)] + [self.pos]
            next_pos = self.mind.make_decision(self.pos, moves)
            assert (next_pos in moves)
            if next_pos is not self.pos:
                self.model.grid.move_agent(self, next_pos)
                self.x, self.y = self.pos
              
    def using_shelves(self):
        if not self.done() and not self.action:
            for n in self.neighbors:
                if hasattr(n, "category") and n.category 
                in self.need and self.need[n.category] > 0:
                    self.action = PickAction(self, n)
                    return True
        return False

    def using_exits(self):
        if self.done() and not self.action:
            for n in self.neighbors:
                if hasattr(n, "check_out"):
                    self.action = CheckOutAction(self)
                    return True
        return False

    def remove(self):
        self.model.schedule.remove(self)
        self.model.grid.remove_agent(self)
        self.model.total_time_collector.commit_time
        (self.time_total, self.past_actions)

    def _possible_moves(self):
        return moveUtils.places_to_move(self.x, self.y, 
        self.model.width, self.model.height)

Environment: