{ "cells": [ { "cell_type": "markdown", "source": [ "# **Simulating the Bullwhip Effect in a Multi-Tier Supply Chain**" ], "metadata": { "id": "UWS_u9PyEcRh" } }, { "cell_type": "markdown", "metadata": { "id": "evlSIeEs2W67" }, "source": [ "# Settings" ] }, { "cell_type": "code", "source": [ "# Define cost per unit for storage and backorder penalties\n", "STORAGE_COST_PER_UNIT = 0.5 # Cost incurred for storing each unit of product\n", "BACKORDER_PENALTY_COST_PER_UNIT = 1 # Cost penalty for each unit in backorder (unfulfilled orders)\n", "\n", "# Define the number of weeks for the simulation\n", "WEEKS_TO_PLAY = 41 # Total simulation period in weeks\n", "\n", "# Set delay in supply chain (in weeks)\n", "QUEUE_DELAY_WEEKS = 2 # Delay time in weeks between order and delivery in supply chain\n", "\n", "# Initial values for various stock-related metrics\n", "INITIAL_STOCK = 12 # Starting stock level for each actor\n", "INITIAL_COST = 0 # Initial cost at the beginning of the simulation\n", "INITIAL_CURRENT_ORDERS = 0 # Initial orders at the start of the simulation\n", "\n", "# Customer ordering pattern settings\n", "CUSTOMER_INITIAL_ORDERS = 5 # Initial order quantity for customers in early weeks\n", "CUSTOMER_SUBSEQUENT_ORDERS = 9 # Increased order quantity for customers in subsequent weeks\n", "\n", "# Target stock level for supply chain actors\n", "TARGET_STOCK = 12 # Desired stock level to balance demand and minimize costs" ], "metadata": { "id": "PHolsibV-ELk" }, "execution_count": 11, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "vdx7B5rL2W6_" }, "source": [ "# Customers" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "id": "aYQeuUoE2W6_" }, "outputs": [], "source": [ "# Define the Customer class to simulate customer behavior\n", "class Customer:\n", " def __init__(self):\n", " # Initialize the total beer received by the customer\n", " self.totalBeerReceived = 0\n", " return\n", "\n", " def RecieveFromRetailer(self, amountReceived):\n", " # Update the total amount of beer received based on supply from the retailer\n", " self.totalBeerReceived += amountReceived\n", " return\n", "\n", " def CalculateOrder(self, weekNum):\n", " # Calculate the customer order based on the week number\n", " # Order amount changes after the initial weeks to reflect demand increase\n", " if weekNum <= 5:\n", " result = CUSTOMER_INITIAL_ORDERS\n", " else:\n", " result = CUSTOMER_SUBSEQUENT_ORDERS\n", " return result\n", "\n", " def GetBeerReceived(self):\n", " # Retrieve the total amount of beer received by the customer\n", " return self.totalBeerReceived" ] }, { "cell_type": "markdown", "metadata": { "id": "m3hjiAxA2W6_" }, "source": [ "# SupplyChainQueue" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "id": "9OsbgZl32W6_" }, "outputs": [], "source": [ "class SupplyChainQueue:\n", "\n", " def __init__(self, queueLength):\n", " # Initialize the supply chain queue with a specified length\n", " # 'queueLength' represents the maximum number of orders that can be held in the queue\n", " self.queueLength = queueLength\n", " self.data = [] # List to store orders in the queue\n", " return\n", "\n", " def PushEnvelope(self, numberOfCasesToOrder):\n", " # Attempt to place a new order in the queue\n", " # 'numberOfCasesToOrder' represents the quantity of product ordered\n", " orderSuccessfullyPlaced = False # Track if order was successfully added\n", "\n", " if len(self.data) < self.queueLength: # Check if queue has space for the order\n", " self.data.append(numberOfCasesToOrder) # Add order to the queue\n", " orderSuccessfullyPlaced = True # Mark order as successfully placed\n", "\n", " return orderSuccessfullyPlaced # Return whether the order was successfully added\n", "\n", " def AdvanceQueue(self):\n", " # Move the queue forward by removing the oldest order (FIFO - First In, First Out)\n", " # This simulates the delay in processing orders over time\n", " self.data.pop(0) # Remove the first order in the queue\n", " return\n", "\n", " def PopEnvelope(self):\n", " # Retrieve the next order to be delivered from the front of the queue\n", " if len(self.data) >= 1: # Check if there is at least one order in the queue\n", " quantityDelivered = self.data[0] # Get the first order in the queue\n", " self.AdvanceQueue() # Remove it from the queue after delivery\n", " else:\n", " quantityDelivered = 0 # No orders to deliver if the queue is empty\n", "\n", " return quantityDelivered # Return the quantity to be delivered\n", "\n", " def PrettyPrint(self):\n", " # Print the current state of the queue, mainly for debugging purposes\n", " print(self.data)\n", " return" ] }, { "cell_type": "markdown", "metadata": { "id": "7QKiWeU72W7A" }, "source": [ "# SupplyChainActor" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "id": "IimSKBfe2W7A" }, "outputs": [], "source": [ "class SupplyChainActor:\n", "\n", " def __init__(self, incomingOrdersQueue, outgoingOrdersQueue, incomingDeliveriesQueue, outgoingDeliveriesQueue):\n", " # Initialize stock, orders, and cost attributes\n", " self.currentStock = INITIAL_STOCK # Initial stock level for the actor\n", " self.currentOrders = INITIAL_CURRENT_ORDERS # Initial orders to be fulfilled\n", " self.costsIncurred = INITIAL_COST # Initial cost at the start of the simulation\n", "\n", " # Set up queues for managing orders and deliveries\n", " self.incomingOrdersQueue = incomingOrdersQueue # Queue for orders from downstream actors\n", " self.outgoingOrdersQueue = outgoingOrdersQueue # Queue for orders to upstream actors\n", " self.incomingDeliveriesQueue = incomingDeliveriesQueue # Queue for deliveries from upstream\n", " self.outgoingDeliveriesQueue = outgoingDeliveriesQueue # Queue for deliveries to downstream\n", "\n", " self.lastOrderQuantity = 0 # Store the quantity ordered in the last round for tracking purposes\n", " return\n", "\n", " def PlaceOutgoingDelivery(self, amountToDeliver):\n", " # Place the calculated delivery quantity in the outgoing deliveries queue\n", " self.outgoingDeliveriesQueue.PushEnvelope(amountToDeliver)\n", " return\n", "\n", " def PlaceOutgoingOrder(self, weekNum):\n", " # Place an order based on the week number, using an \"anchor and maintain\" strategy after an initial period\n", " if weekNum <= 4:\n", " amountToOrder = 4 # Initial equilibrium order amount for the first few weeks\n", " else:\n", " # After initial weeks, determine order based on current orders and stock levels\n", " amountToOrder = 0.5 * self.currentOrders # Order amount scales with outstanding orders\n", "\n", " # Adjust order to reach target stock level\n", " if (TARGET_STOCK - self.currentStock) > 0:\n", " amountToOrder += TARGET_STOCK - self.currentStock\n", "\n", " # Add the order to the outgoing orders queue\n", " self.outgoingOrdersQueue.PushEnvelope(amountToOrder)\n", " self.lastOrderQuantity = amountToOrder # Track the last order quantity for reference\n", " return\n", "\n", " def ReceiveIncomingDelivery(self):\n", " # Receive a delivery from upstream by popping the first item in the incoming deliveries queue\n", " quantityReceived = self.incomingDeliveriesQueue.PopEnvelope()\n", "\n", " # Add the received quantity to the current stock\n", " if quantityReceived > 0:\n", " self.currentStock += quantityReceived\n", " return\n", "\n", " def ReceiveIncomingOrders(self):\n", " # Receive an order from downstream by popping the first item in the incoming orders queue\n", " thisOrder = self.incomingOrdersQueue.PopEnvelope()\n", "\n", " # Add the incoming order to the current orders to be fulfilled\n", " if thisOrder > 0:\n", " self.currentOrders += thisOrder\n", " return\n", "\n", " def CalcBeerToDeliver(self):\n", " # Calculate the quantity of beer to deliver based on current stock and orders\n", " deliveryQuantity = 0\n", "\n", " # If current stock can fulfill all current orders, deliver the full amount\n", " if self.currentStock >= self.currentOrders:\n", " deliveryQuantity = self.currentOrders\n", " self.currentStock -= deliveryQuantity # Reduce stock by delivered quantity\n", " self.currentOrders -= deliveryQuantity # Reduce outstanding orders accordingly\n", " # If stock is insufficient, deliver as much as possible and backorder the rest\n", " elif self.currentStock > 0 and self.currentStock < self.currentOrders:\n", " deliveryQuantity = self.currentStock # Deliver all available stock\n", " self.currentStock = 0 # Stock becomes zero after delivery\n", " self.currentOrders -= deliveryQuantity # Reduce outstanding orders by delivered amount\n", " return deliveryQuantity\n", "\n", " def CalcCostForTurn(self):\n", " # Calculate the costs for the current turn, including storage and backorder penalties\n", " inventoryStorageCost = self.currentStock * STORAGE_COST_PER_UNIT # Cost for holding inventory\n", " backorderPenaltyCost = self.currentOrders * BACKORDER_PENALTY_COST_PER_UNIT # Cost for unfulfilled orders\n", " costsThisTurn = inventoryStorageCost + backorderPenaltyCost # Total cost for this turn\n", " return costsThisTurn\n", "\n", " def GetCostIncurred(self):\n", " # Return the total costs incurred by this actor so far\n", " return self.costsIncurred\n", "\n", " def GetLastOrderQuantity(self):\n", " # Return the quantity of the last order placed by this actor\n", " return self.lastOrderQuantity\n", "\n", " def CalcEffectiveInventory(self):\n", " # Calculate effective inventory as the difference between stock and outstanding orders\n", " # This helps determine if the actor is in surplus or backorder\n", " return (self.currentStock - self.currentOrders)" ] }, { "cell_type": "markdown", "metadata": { "id": "ei3YZtmY2W7A" }, "source": [ "# Retailer" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "id": "dhUX7bLx2W7A" }, "outputs": [], "source": [ "class Retailer(SupplyChainActor):\n", "\n", " def __init__(self, incomingOrdersQueue, outgoingOrdersQueue, incomingDeliveriesQueue, outgoingDeliveriesQueue, theCustomer):\n", " # Initialize the retailer with supply chain queues and a customer instance\n", " # Inherit attributes and methods from the SupplyChainActor superclass\n", " super().__init__(incomingOrdersQueue, outgoingOrdersQueue, incomingDeliveriesQueue, outgoingDeliveriesQueue)\n", " self.customer = theCustomer # Customer instance associated with the retailer\n", " return\n", "\n", " def ReceiveIncomingOrderFromCustomer(self, weekNum):\n", " # Add the calculated customer order for the current week to the retailer's orders\n", " # CalculateOrder method from Customer class is used to get the order amount\n", " self.currentOrders += self.customer.CalculateOrder(weekNum)\n", " return\n", "\n", " def ShipOutgoingDeliveryToCustomer(self):\n", " # Ship the calculated amount of beer to the customer by calling CalcBeerToDeliver\n", " # RecieveFromRetailer method from Customer class is used to receive the quantity delivered\n", " self.customer.RecieveFromRetailer(self.CalcBeerToDeliver())\n", " return\n", "\n", " def TakeTurn(self, weekNum):\n", " # Define the series of actions the retailer performs each turn (weekly):\n", "\n", " # 1. Receive new delivery from the wholesaler\n", " # This step also advances the delivery queue by removing the first order in line\n", " self.ReceiveIncomingDelivery()\n", "\n", " # 2. Receive new order from the customer\n", " self.ReceiveIncomingOrderFromCustomer(weekNum)\n", "\n", " # 3. Calculate and ship the required amount to the customer\n", " # Directly call RecieveFromRetailer with a fixed delivery amount for the initial weeks\n", " if weekNum <= 4:\n", " self.customer.RecieveFromRetailer(4) # Fixed delivery amount in the first weeks\n", " else:\n", " self.customer.RecieveFromRetailer(self.CalcBeerToDeliver()) # Dynamic delivery based on stock\n", "\n", " # 4. Place an outgoing order to the wholesaler\n", " self.PlaceOutgoingOrder(weekNum)\n", "\n", " # 5. Update the retailer's costs for the current turn\n", " self.costsIncurred += self.CalcCostForTurn()\n", " return" ] }, { "cell_type": "markdown", "metadata": { "id": "SFRrZOtE2W7B" }, "source": [ "# Wholesaler" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "id": "Zq-elYeV2W7B" }, "outputs": [], "source": [ "class Wholesaler(SupplyChainActor):\n", "\n", " def __init__(self, incomingOrdersQueue, outgoingOrdersQueue, incomingDeliveriesQueue, outgoingDeliveriesQueue):\n", " # Initialize the wholesaler with supply chain queues by inheriting from SupplyChainActor\n", " super().__init__(incomingOrdersQueue, outgoingOrdersQueue, incomingDeliveriesQueue, outgoingDeliveriesQueue)\n", " return\n", "\n", " def TakeTurn(self, weekNum):\n", " # Define the steps the wholesaler performs each turn (weekly):\n", "\n", " # 1. Receive new delivery from the distributor\n", " # This step also advances the delivery queue by removing the first order in line\n", " self.ReceiveIncomingDelivery()\n", "\n", " # 2. Receive new order from the retailer\n", " # This step also advances the orders queue\n", " self.ReceiveIncomingOrders()\n", "\n", " # 3. Prepare and place the outgoing delivery to the retailer\n", " # Initially, for the first few weeks, send a fixed amount of 4 units\n", " if weekNum <= 4:\n", " self.PlaceOutgoingDelivery(4) # Fixed delivery amount in the first weeks\n", " else:\n", " # After the initial weeks, calculate the delivery based on current stock and orders\n", " self.PlaceOutgoingDelivery(self.CalcBeerToDeliver())\n", "\n", " # 4. Place an order to the upstream actor (such as a distributor)\n", " self.PlaceOutgoingOrder(weekNum)\n", "\n", " # 5. Update the wholesaler's costs for the current turn\n", " self.costsIncurred += self.CalcCostForTurn()\n", " return" ] }, { "cell_type": "markdown", "metadata": { "id": "uSoFWWQi2W7B" }, "source": [ "# Distributor" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "id": "IV1SB0gO2W7B" }, "outputs": [], "source": [ "class Distributor(SupplyChainActor):\n", "\n", " def __init__(self, incomingOrdersQueue, outgoingOrdersQueue, incomingDeliveriesQueue, outgoingDeliveriesQueue):\n", " # Initialize the distributor with supply chain queues by inheriting from SupplyChainActor\n", " super().__init__(incomingOrdersQueue, outgoingOrdersQueue, incomingDeliveriesQueue, outgoingDeliveriesQueue)\n", " return\n", "\n", " def TakeTurn(self, weekNum):\n", " # Define the steps the distributor performs each turn (weekly):\n", "\n", " # 1. Receive new delivery from the factory\n", " # This also advances the delivery queue by removing the first item in line\n", " self.ReceiveIncomingDelivery()\n", "\n", " # 2. Receive new order from the wholesaler\n", " # This also advances the orders queue by removing the first item in line\n", " self.ReceiveIncomingOrders()\n", "\n", " # 3. Prepare and place the outgoing delivery to the wholesaler\n", " # Initially, for the first few weeks, send a fixed amount of 4 units\n", " if weekNum <= 4:\n", " self.PlaceOutgoingDelivery(4) # Fixed delivery amount in the first weeks\n", " else:\n", " # After the initial weeks, calculate delivery based on current stock and orders\n", " self.PlaceOutgoingDelivery(self.CalcBeerToDeliver())\n", "\n", " # 4. Place an order to the upstream actor (e.g., factory) to replenish stock\n", " self.PlaceOutgoingOrder(weekNum)\n", "\n", " # 5. Update the distributor’s costs for the current turn\n", " self.costsIncurred += self.CalcCostForTurn()\n", " return" ] }, { "cell_type": "markdown", "metadata": { "id": "oebF0Q9h2W7B" }, "source": [ "# Factory" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "id": "J20MJ-3D2W7C" }, "outputs": [], "source": [ "class Factory(SupplyChainActor):\n", "\n", " def __init__(self, incomingOrdersQueue, outgoingOrdersQueue, incomingDeliveriesQueue, outgoingDeliveriesQueue, productionDelayWeeks):\n", " # Initialize the factory with supply chain queues and a production delay queue\n", " super().__init__(incomingOrdersQueue, outgoingOrdersQueue, incomingDeliveriesQueue, outgoingDeliveriesQueue)\n", "\n", " # Initialize a queue to handle production delays (simulating brewing/production time)\n", " self.BeerProductionDelayQueue = SupplyChainQueue(productionDelayWeeks)\n", "\n", " # Assume the factory has initial production runs in progress for stability\n", " # These initial production orders help prevent stockouts at the beginning\n", " self.BeerProductionDelayQueue.PushEnvelope(CUSTOMER_INITIAL_ORDERS)\n", " self.BeerProductionDelayQueue.PushEnvelope(CUSTOMER_INITIAL_ORDERS)\n", " return\n", "\n", " def ProduceBeer(self, weekNum):\n", " # Calculate the amount of beer to produce based on the week number\n", "\n", " if weekNum <= 4:\n", " amountToOrder = 4 # Fixed initial production amount for the first few weeks\n", " else:\n", " # After initial weeks, production amount scales with current orders and target stock\n", " amountToOrder = 0.5 * self.currentOrders # Produces enough to cover current orders\n", "\n", " # Adjust production to maintain target stock level if below target\n", " if (TARGET_STOCK - self.currentStock) > 0:\n", " amountToOrder += TARGET_STOCK - self.currentStock\n", "\n", " # Add the production order to the production delay queue (simulates delayed production)\n", " self.BeerProductionDelayQueue.PushEnvelope(amountToOrder)\n", " self.lastOrderQuantity = amountToOrder # Track the last production order quantity\n", " return\n", "\n", " def FinishProduction(self):\n", " # Complete a production run and add the produced beer to the current stock\n", " amountProduced = self.BeerProductionDelayQueue.PopEnvelope()\n", "\n", " # Add completed production amount to stock if production is finished\n", " if amountProduced > 0:\n", " self.currentStock += amountProduced\n", " return\n", "\n", " def TakeTurn(self, weekNum):\n", " # Define the steps the factory performs each turn (weekly):\n", "\n", " # 1. Complete previous production runs (if any) and add to stock\n", " self.FinishProduction()\n", "\n", " # 2. Receive new orders from the distributor\n", " # Advances the incoming orders queue by removing the first order\n", " self.ReceiveIncomingOrders()\n", "\n", " # 3. Prepare and place the outgoing delivery to the distributor\n", " # Initially, send a fixed amount of 4 units in the first few weeks\n", " if weekNum <= 4:\n", " self.PlaceOutgoingDelivery(4)\n", " else:\n", " # After initial weeks, calculate delivery based on current stock and orders\n", " self.PlaceOutgoingDelivery(self.CalcBeerToDeliver())\n", "\n", " # 4. Initiate production to fulfill future demand\n", " self.ProduceBeer(weekNum)\n", "\n", " # 5. Calculate and update the factory’s costs for the current turn\n", " self.costsIncurred += self.CalcCostForTurn()\n", " return" ] }, { "cell_type": "markdown", "metadata": { "id": "bIHIgpX72W7C" }, "source": [ "# SupplyChainStatistics" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "id": "YA5-s-A_2W7C" }, "outputs": [], "source": [ "import plotly.express as px\n", "import plotly.graph_objects as go\n", "\n", "class SupplyChainStatistics:\n", "\n", " def __init__(self):\n", " # Initialize lists to store time-series data for each actor in the supply chain\n", " # Each list will track metrics over each turn (e.g., week)\n", "\n", " # Costs over time for each supply chain actor\n", " self.retailerCostsOverTime = []\n", " self.wholesalerCostsOverTime = []\n", " self.distributorCostsOverTime = []\n", " self.factoryCostsOverTime = []\n", "\n", " # Orders over time for each actor\n", " self.retailerOrdersOverTime = []\n", " self.wholesalerOrdersOverTime = []\n", " self.distributorOrdersOverTime = []\n", " self.factoryOrdersOverTime = []\n", "\n", " # Effective inventory (current stock - current orders) over time for each actor\n", " self.retailerEffectiveInventoryOverTime = []\n", " self.wholesalerEffectiveInventoryOverTime = []\n", " self.distributorEffectiveInventoryOverTime = []\n", " self.factoryEffectiveInventoryOverTime = []\n", " return\n", "\n", " # Methods to record orders each week for each actor\n", " def RecordRetailerOrders(self, retailerOrdersThisWeek):\n", " self.retailerOrdersOverTime.append(retailerOrdersThisWeek)\n", " print('Retailer Order:', self.retailerOrdersOverTime[-1])\n", " return\n", "\n", " def RecordWholesalerOrders(self, wholesalerOrdersThisWeek):\n", " self.wholesalerOrdersOverTime.append(wholesalerOrdersThisWeek)\n", " print('Wholesaler Order:', self.wholesalerOrdersOverTime[-1])\n", " return\n", "\n", " def RecordDistributorOrders(self, distributorOrdersThisWeek):\n", " self.distributorOrdersOverTime.append(distributorOrdersThisWeek)\n", " print('Distributor Order:', self.distributorOrdersOverTime[-1])\n", " return\n", "\n", " def RecordFactoryOrders(self, factoryOrdersThisWeek):\n", " self.factoryOrdersOverTime.append(factoryOrdersThisWeek)\n", " print('Factory Order:', self.factoryOrdersOverTime[-1])\n", " return\n", "\n", " # Methods to record costs incurred each week for each actor\n", " def RecordRetailerCost(self, retailerCostsThisWeek):\n", " self.retailerCostsOverTime.append(retailerCostsThisWeek)\n", " print('Retailer Cost:', self.retailerCostsOverTime[-1])\n", " return\n", "\n", " def RecordWholesalerCost(self, wholesalerCostsThisWeek):\n", " self.wholesalerCostsOverTime.append(wholesalerCostsThisWeek)\n", " print('Wholesaler Cost:', self.wholesalerCostsOverTime[-1])\n", " return\n", "\n", " def RecordDistributorCost(self, distributorCostsThisWeek):\n", " self.distributorCostsOverTime.append(distributorCostsThisWeek)\n", " print('Distributor Cost:', self.distributorCostsOverTime[-1])\n", " return\n", "\n", " def RecordFactoryCost(self, factoryCostsThisWeek):\n", " self.factoryCostsOverTime.append(factoryCostsThisWeek)\n", " print('Factory Cost:', self.factoryCostsOverTime[-1])\n", " return\n", "\n", " # Methods to record effective inventory each week for each actor\n", " def RecordRetailerEffectiveInventory(self, retailerEffectiveInventoryThisWeek):\n", " self.retailerEffectiveInventoryOverTime.append(retailerEffectiveInventoryThisWeek)\n", " print('Retailer Effective Inventory:', self.retailerEffectiveInventoryOverTime[-1])\n", " return\n", "\n", " def RecordWholesalerEffectiveInventory(self, wholesalerEffectiveInventoryThisWeek):\n", " self.wholesalerEffectiveInventoryOverTime.append(wholesalerEffectiveInventoryThisWeek)\n", " print('Wholesaler Effective Inventory:', self.wholesalerEffectiveInventoryOverTime[-1])\n", " return\n", "\n", " def RecordDistributorEffectiveInventory(self, distributorEffectiveInventoryThisWeek):\n", " self.distributorEffectiveInventoryOverTime.append(distributorEffectiveInventoryThisWeek)\n", " print('Distributor Effective Inventory:', self.distributorEffectiveInventoryOverTime[-1])\n", " return\n", "\n", " def RecordFactoryEffectiveInventory(self, factoryEffectiveInventoryThisWeek):\n", " self.factoryEffectiveInventoryOverTime.append(factoryEffectiveInventoryThisWeek)\n", " print('Factory Effective Inventory:', self.factoryEffectiveInventoryOverTime[-1])\n", " return\n", "\n", " # Plotting methods to visualize orders, inventory, and costs over time for each actor\n", " def PlotOrders(self):\n", " # Create a plot to visualize orders placed over time by each actor\n", " fig = go.Figure()\n", " weeks = list(range(0, WEEKS_TO_PLAY + 2))\n", " fig.add_trace(go.Scatter(x=weeks, y=self.retailerOrdersOverTime, mode='lines+markers',\n", " name='Retailer Orders', marker=dict(size=5), marker_color='rgb(215,48,39)'))\n", " fig.add_trace(go.Scatter(x=weeks, y=self.wholesalerOrdersOverTime, mode='lines+markers',\n", " name='Wholesaler Orders', marker=dict(size=5), marker_color='rgb(255,186,0)'))\n", " fig.add_trace(go.Scatter(x=weeks, y=self.distributorOrdersOverTime, mode='lines+markers',\n", " name='Distributor Orders', marker=dict(size=5), marker_color='rgb(126,2,114)'))\n", " fig.add_trace(go.Scatter(x=weeks, y=self.factoryOrdersOverTime, mode='lines+markers',\n", " name='Factory Orders', marker=dict(size=5), marker_color='rgb(69,117,180)'))\n", " fig.update_layout(title_text='*Orders Placed Over Time*', xaxis_title='Weeks', yaxis_title='Orders',\n", " paper_bgcolor='rgba(0,0,0,0)', height=580)\n", " fig.update_xaxes(range=[0, WEEKS_TO_PLAY])\n", " fig.show()\n", " return\n", "\n", " def PlotEffectiveInventory(self):\n", " # Create a plot to visualize effective inventory over time for each actor\n", " fig = go.Figure()\n", " weeks = list(range(0, WEEKS_TO_PLAY + 2))\n", " fig.add_trace(go.Scatter(x=weeks, y=self.retailerEffectiveInventoryOverTime, mode='lines+markers',\n", " name='Retailer Inventory', marker=dict(size=5), marker_color='rgb(215,48,39)'))\n", " fig.add_trace(go.Scatter(x=weeks, y=self.wholesalerEffectiveInventoryOverTime, mode='lines+markers',\n", " name='Wholesaler Inventory', marker=dict(size=5), marker_color='rgb(255,186,0)'))\n", " fig.add_trace(go.Scatter(x=weeks, y=self.distributorEffectiveInventoryOverTime, mode='lines+markers',\n", " name='Distributor Inventory', marker=dict(size=5), marker_color='rgb(126,2,114)'))\n", " fig.add_trace(go.Scatter(x=weeks, y=self.factoryEffectiveInventoryOverTime, mode='lines+markers',\n", " name='Factory Inventory', marker=dict(size=5), marker_color='rgb(69,117,180)'))\n", " fig.update_layout(title_text='*Effective Inventory Over Time*', xaxis_title='Weeks', yaxis_title='Effective Inventory',\n", " paper_bgcolor='rgba(0,0,0,0)', height=580)\n", " fig.update_xaxes(range=[0, WEEKS_TO_PLAY])\n", " fig.show()\n", " return\n", "\n", " def PlotCosts(self):\n", " # Create a plot to visualize total costs incurred over time by each actor\n", " fig = go.Figure()\n", " weeks = list(range(0, WEEKS_TO_PLAY + 2))\n", " fig.add_trace(go.Scatter(x=weeks, y=self.retailerCostsOverTime, mode='lines+markers',\n", " name='Retailer Total Cost', marker=dict(size=5), marker_color='rgb(215,48,39)'))\n", " fig.add_trace(go.Scatter(x=weeks, y=self.wholesalerCostsOverTime, mode='lines+markers',\n", " name='Wholesaler Total Cost', marker=dict(size=5), marker_color='rgb(255,186,0)'))\n", " fig.add_trace(go.Scatter(x=weeks, y=self.distributorCostsOverTime, mode='lines+markers',\n", " name='Distributor Total Cost', marker=dict(size=5), marker_color='rgb(126,2,114)'))\n", " fig.add_trace(go.Scatter(x=weeks, y=self.factoryCostsOverTime, mode='lines+markers',\n", " name='Factory Total Cost', marker=dict(size=5), marker_color='rgb(69,117,180)'))\n", " fig.update_layout(title_text='*Cost Incurred Over Time*', xaxis_title='Weeks', yaxis_title='Cost ($)',\n", " paper_bgcolor='rgba(0,0,0,0)', height=580)\n", " fig.update_xaxes(range=[0, WEEKS_TO_PLAY])\n", " fig.show()\n", " return" ] }, { "cell_type": "markdown", "metadata": { "id": "45gBZ0mB2W7C" }, "source": [ "# Main" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "scrolled": true, "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "id": "0tuSSB_N2W7C", "outputId": "65f7af48-6519-480e-f24f-af1459440699" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "\n", " -------------------------------------------------\n", " Week 0\n", "-------------------------------------------------\n", "Retailer Cost: 13.5\n", "Retailer Order: 4\n", "Retailer Effective Inventory: 12\n", "Wholesaler Cost: 13.5\n", "Wholesaler Order: 4\n", "Wholesaler Effective Inventory: 12\n", "Distributor Cost: 13.5\n", "Distributor Order: 4\n", "Distributor Effective Inventory: 12\n", "Factory Cost: 13.5\n", "Factory Order: 4\n", "Factory Effective Inventory: 12\n", "\n", " -------------------------------------------------\n", " Week 1\n", "-------------------------------------------------\n", "Retailer Cost: 34.5\n", "Retailer Order: 4\n", "Retailer Effective Inventory: 12\n", "Wholesaler Cost: 34.5\n", "Wholesaler Order: 4\n", "Wholesaler Effective Inventory: 12\n", "Distributor Cost: 34.5\n", "Distributor Order: 4\n", "Distributor Effective Inventory: 12\n", "Factory Cost: 34.5\n", "Factory Order: 4\n", "Factory Effective Inventory: 12\n", "\n", " -------------------------------------------------\n", " Week 2\n", "-------------------------------------------------\n", "Retailer Cost: 62.5\n", "Retailer Order: 4\n", "Retailer Effective Inventory: 11\n", "Wholesaler Cost: 61.5\n", "Wholesaler Order: 4\n", "Wholesaler Effective Inventory: 12\n", "Distributor Cost: 61.5\n", "Distributor Order: 4\n", "Distributor Effective Inventory: 12\n", "Factory Cost: 61.5\n", "Factory Order: 4\n", "Factory Effective Inventory: 12\n", "\n", " -------------------------------------------------\n", " Week 3\n", "-------------------------------------------------\n", "Retailer Cost: 97.5\n", "Retailer Order: 4\n", "Retailer Effective Inventory: 10\n", "Wholesaler Cost: 94.5\n", "Wholesaler Order: 4\n", "Wholesaler Effective Inventory: 12\n", "Distributor Cost: 94.5\n", "Distributor Order: 4\n", "Distributor Effective Inventory: 12\n", "Factory Cost: 94.5\n", "Factory Order: 4\n", "Factory Effective Inventory: 12\n", "\n", " -------------------------------------------------\n", " Week 4\n", "-------------------------------------------------\n", "Retailer Cost: 139.5\n", "Retailer Order: 4\n", "Retailer Effective Inventory: 9\n", "Wholesaler Cost: 133.5\n", "Wholesaler Order: 4\n", "Wholesaler Effective Inventory: 12\n", "Distributor Cost: 133.5\n", "Distributor Order: 4\n", "Distributor Effective Inventory: 12\n", "Factory Cost: 133.5\n", "Factory Order: 4\n", "Factory Effective Inventory: 12\n", "\n", " -------------------------------------------------\n", " Week 5\n", "-------------------------------------------------\n", "Retailer Cost: 143.5\n", "Retailer Order: 4.0\n", "Retailer Effective Inventory: 8\n", "Wholesaler Cost: 139.5\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 12\n", "Distributor Cost: 139.5\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 12\n", "Factory Cost: 139.5\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 12\n", "\n", " -------------------------------------------------\n", " Week 6\n", "-------------------------------------------------\n", "Retailer Cost: 145.0\n", "Retailer Order: 9.0\n", "Retailer Effective Inventory: 3\n", "Wholesaler Cost: 145.5\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 12.0\n", "Distributor Cost: 147.5\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 16\n", "Factory Cost: 147.5\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 16\n", "\n", " -------------------------------------------------\n", " Week 7\n", "-------------------------------------------------\n", "Retailer Cost: 155.0\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 20\n", "Wholesaler Cost: 160.0\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 29.0\n", "Distributor Cost: 168.5\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 42\n", "Factory Cost: 155.5\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 16\n", "\n", " -------------------------------------------------\n", " Week 8\n", "-------------------------------------------------\n", "Retailer Cost: 162.5\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 15.0\n", "Wholesaler Cost: 174.5\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 29.0\n", "Distributor Cost: 189.5\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 42\n", "Factory Cost: 163.5\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 16\n", "\n", " -------------------------------------------------\n", " Week 9\n", "-------------------------------------------------\n", "Retailer Cost: 170.0\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 15.0\n", "Wholesaler Cost: 189.0\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 29.0\n", "Distributor Cost: 210.5\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 42\n", "Factory Cost: 171.5\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 16\n", "\n", " -------------------------------------------------\n", " Week 10\n", "-------------------------------------------------\n", "Retailer Cost: 173.0\n", "Retailer Order: 6.0\n", "Retailer Effective Inventory: 6.0\n", "Wholesaler Cost: 203.5\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 29.0\n", "Distributor Cost: 231.5\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 42\n", "Factory Cost: 179.5\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 16\n", "\n", " -------------------------------------------------\n", " Week 11\n", "-------------------------------------------------\n", "Retailer Cost: 176.0\n", "Retailer Order: 13.5\n", "Retailer Effective Inventory: -3.0\n", "Wholesaler Cost: 215.0\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 23.0\n", "Distributor Cost: 252.5\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 42\n", "Factory Cost: 187.5\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 16\n", "\n", " -------------------------------------------------\n", " Week 12\n", "-------------------------------------------------\n", "Retailer Cost: 188.0\n", "Retailer Order: 18.0\n", "Retailer Effective Inventory: -12.0\n", "Wholesaler Cost: 219.75\n", "Wholesaler Order: 2.5\n", "Wholesaler Effective Inventory: 9.5\n", "Distributor Cost: 273.5\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 42\n", "Factory Cost: 195.5\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 16\n", "\n", " -------------------------------------------------\n", " Week 13\n", "-------------------------------------------------\n", "Retailer Cost: 203.0\n", "Retailer Order: 19.5\n", "Retailer Effective Inventory: -15.0\n", "Wholesaler Cost: 228.25\n", "Wholesaler Order: 16.25\n", "Wholesaler Effective Inventory: -8.5\n", "Distributor Cost: 293.25\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 39.5\n", "Factory Cost: 203.5\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 16\n", "\n", " -------------------------------------------------\n", " Week 14\n", "-------------------------------------------------\n", "Retailer Cost: 213.5\n", "Retailer Order: 17.25\n", "Retailer Effective Inventory: -10.5\n", "Wholesaler Cost: 256.25\n", "Wholesaler Order: 26.0\n", "Wholesaler Effective Inventory: -28.0\n", "Distributor Cost: 304.875\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 23.25\n", "Factory Cost: 211.5\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 16\n", "\n", " -------------------------------------------------\n", " Week 15\n", "-------------------------------------------------\n", "Retailer Cost: 223.5\n", "Retailer Order: 17.0\n", "Retailer Effective Inventory: -10.0\n", "Wholesaler Cost: 299.0\n", "Wholesaler Order: 33.375\n", "Wholesaler Effective Inventory: -42.75\n", "Distributor Cost: 307.625\n", "Distributor Order: 13.375\n", "Distributor Effective Inventory: -2.75\n", "Factory Cost: 219.5\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 16\n", "\n", " -------------------------------------------------\n", " Week 16\n", "-------------------------------------------------\n", "Retailer Cost: 242.5\n", "Retailer Order: 21.5\n", "Retailer Effective Inventory: -19.0\n", "Wholesaler Cost: 342.5\n", "Wholesaler Order: 33.75\n", "Wholesaler Effective Inventory: -43.5\n", "Distributor Cost: 343.75\n", "Distributor Order: 30.0625\n", "Distributor Effective Inventory: -36.125\n", "Factory Cost: 220.8125\n", "Factory Order: 9.375\n", "Factory Effective Inventory: 2.625\n", "\n", " -------------------------------------------------\n", " Week 17\n", "-------------------------------------------------\n", "Retailer Cost: 268.0\n", "Retailer Order: 24.75\n", "Retailer Effective Inventory: -25.5\n", "Wholesaler Cost: 384.25\n", "Wholesaler Order: 32.875\n", "Wholesaler Effective Inventory: -41.75\n", "Distributor Cost: 413.625\n", "Distributor Order: 46.9375\n", "Distributor Effective Inventory: -69.875\n", "Factory Cost: 248.25\n", "Factory Order: 25.71875\n", "Factory Effective Inventory: -27.4375\n", "\n", " -------------------------------------------------\n", " Week 18\n", "-------------------------------------------------\n", "Retailer Cost: 286.25\n", "Retailer Order: 21.125\n", "Retailer Effective Inventory: -18.25\n", "Wholesaler Cost: 450.75\n", "Wholesaler Order: 45.25\n", "Wholesaler Effective Inventory: -66.5\n", "Distributor Cost: 503.0\n", "Distributor Order: 56.6875\n", "Distributor Effective Inventory: -89.375\n", "Factory Cost: 313.25\n", "Factory Order: 44.5\n", "Factory Effective Inventory: -65.0\n", "\n", " -------------------------------------------------\n", " Week 19\n", "-------------------------------------------------\n", "Retailer Cost: 290.25\n", "Retailer Order: 14.0\n", "Retailer Effective Inventory: -4.0\n", "Wholesaler Cost: 538.375\n", "Wholesaler Order: 55.8125\n", "Wholesaler Effective Inventory: -87.625\n", "Distributor Cost: 635.0\n", "Distributor Order: 78.0\n", "Distributor Effective Inventory: -132.0\n", "Factory Cost: 409.21875\n", "Factory Order: 59.984375\n", "Factory Effective Inventory: -95.96875\n", "\n", " -------------------------------------------------\n", " Week 20\n", "-------------------------------------------------\n", "Retailer Cost: 303.25\n", "Retailer Order: 18.5\n", "Retailer Effective Inventory: -13.0\n", "Wholesaler Cost: 626.625\n", "Wholesaler Order: 56.125\n", "Wholesaler Effective Inventory: -88.25\n", "Distributor Cost: 813.4375\n", "Distributor Order: 101.21875\n", "Distributor Effective Inventory: -178.4375\n", "Factory Cost: 538.6875\n", "Factory Order: 76.734375\n", "Factory Effective Inventory: -129.46875\n", "\n", " -------------------------------------------------\n", " Week 21\n", "-------------------------------------------------\n", "Retailer Cost: 325.25\n", "Retailer Order: 23.0\n", "Retailer Effective Inventory: -22.0\n", "Wholesaler Cost: 730.75\n", "Wholesaler Order: 64.0625\n", "Wholesaler Effective Inventory: -104.125\n", "Distributor Cost: 1022.28125\n", "Distributor Order: 116.421875\n", "Distributor Effective Inventory: -208.84375\n", "Factory Cost: 709.390625\n", "Factory Order: 97.3515625\n", "Factory Effective Inventory: -170.703125\n", "\n", " -------------------------------------------------\n", " Week 22\n", "-------------------------------------------------\n", "Retailer Cost: 342.875\n", "Retailer Order: 20.8125\n", "Retailer Effective Inventory: -17.625\n", "Wholesaler Cost: 848.5\n", "Wholesaler Order: 70.875\n", "Wholesaler Effective Inventory: -117.75\n", "Distributor Cost: 1250.6875\n", "Distributor Order: 126.203125\n", "Distributor Effective Inventory: -228.40625\n", "Factory Cost: 919.78125\n", "Factory Order: 117.1953125\n", "Factory Effective Inventory: -210.390625\n", "\n", " -------------------------------------------------\n", " Week 23\n", "-------------------------------------------------\n", "Retailer Cost: 366.875\n", "Retailer Order: 24.0\n", "Retailer Effective Inventory: -24.0\n", "Wholesaler Cost: 961.34375\n", "Wholesaler Order: 68.421875\n", "Wholesaler Effective Inventory: -112.84375\n", "Distributor Cost: 1489.984375\n", "Distributor Order: 131.6484375\n", "Distributor Effective Inventory: -239.296875\n", "Factory Cost: 1159.0234375\n", "Factory Order: 131.62109375\n", "Factory Effective Inventory: -239.2421875\n", "\n", " -------------------------------------------------\n", " Week 24\n", "-------------------------------------------------\n", "Retailer Cost: 390.5\n", "Retailer Order: 23.8125\n", "Retailer Effective Inventory: -23.625\n", "Wholesaler Cost: 1053.6875\n", "Wholesaler Order: 58.171875\n", "Wholesaler Effective Inventory: -92.34375\n", "Distributor Cost: 1720.96875\n", "Distributor Order: 127.4921875\n", "Distributor Effective Inventory: -230.984375\n", "Factory Cost: 1412.71875\n", "Factory Order: 138.84765625\n", "Factory Effective Inventory: -253.6953125\n", "\n", " -------------------------------------------------\n", " Week 25\n", "-------------------------------------------------\n", "Retailer Cost: 397.40625\n", "Retailer Order: 15.453125\n", "Retailer Effective Inventory: -6.90625\n", "Wholesaler Cost: 1109.859375\n", "Wholesaler Order: 40.0859375\n", "Wholesaler Effective Inventory: -56.171875\n", "Distributor Cost: 1912.7734375\n", "Distributor Order: 107.90234375\n", "Distributor Effective Inventory: -191.8046875\n", "Factory Cost: 1662.28515625\n", "Factory Order: 136.783203125\n", "Factory Effective Inventory: -249.56640625\n", "\n", " -------------------------------------------------\n", " Week 26\n", "-------------------------------------------------\n", "Retailer Cost: 411.703125\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 28.59375\n", "Wholesaler Cost: 1112.4140625\n", "Wholesaler Order: 6.890625\n", "Wholesaler Effective Inventory: 5.109375\n", "Distributor Cost: 2027.46875\n", "Distributor Order: 69.34765625\n", "Distributor Effective Inventory: -114.6953125\n", "Factory Cost: 1880.90625\n", "Factory Order: 121.310546875\n", "Factory Effective Inventory: -218.62109375\n", "\n", " -------------------------------------------------\n", " Week 27\n", "-------------------------------------------------\n", "Retailer Cost: 451.4921875\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 79.578125\n", "Wholesaler Cost: 1163.64453125\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 102.4609375\n", "Distributor Cost: 2032.486328125\n", "Distributor Order: 1.96484375\n", "Distributor Effective Inventory: 10.03515625\n", "Factory Cost: 2032.091796875\n", "Factory Order: 87.5927734375\n", "Factory Effective Inventory: -151.185546875\n", "\n", " -------------------------------------------------\n", " Week 28\n", "-------------------------------------------------\n", "Retailer Cost: 522.59375\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 142.203125\n", "Wholesaler Cost: 1273.47265625\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 219.65625\n", "Distributor Cost: 2106.927734375\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 148.8828125\n", "Factory Cost: 2063.931640625\n", "Factory Order: 27.919921875\n", "Factory Effective Inventory: -31.83984375\n", "\n", " -------------------------------------------------\n", " Week 29\n", "-------------------------------------------------\n", "Retailer Cost: 589.1953125\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 133.203125\n", "Wholesaler Cost: 1444.09375\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 341.2421875\n", "Distributor Cost: 2249.7607421875\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 285.666015625\n", "Factory Cost: 2091.80810546875\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 55.7529296875\n", "\n", " -------------------------------------------------\n", " Week 30\n", "-------------------------------------------------\n", "Retailer Cost: 651.296875\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 124.203125\n", "Wholesaler Cost: 1614.71484375\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 341.2421875\n", "Distributor Cost: 2453.2490234375\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 406.9765625\n", "Factory Cost: 2133.64453125\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 83.6728515625\n", "\n", " -------------------------------------------------\n", " Week 31\n", "-------------------------------------------------\n", "Retailer Cost: 708.8984375\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 115.203125\n", "Wholesaler Cost: 1785.3359375\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 341.2421875\n", "Distributor Cost: 2672.6572265625\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 438.81640625\n", "Factory Cost: 2175.48095703125\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 83.6728515625\n", "\n", " -------------------------------------------------\n", " Week 32\n", "-------------------------------------------------\n", "Retailer Cost: 762.0\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 106.203125\n", "Wholesaler Cost: 1955.95703125\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 341.2421875\n", "Distributor Cost: 2892.0654296875\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 438.81640625\n", "Factory Cost: 2217.3173828125\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 83.6728515625\n", "\n", " -------------------------------------------------\n", " Week 33\n", "-------------------------------------------------\n", "Retailer Cost: 810.6015625\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 97.203125\n", "Wholesaler Cost: 2126.578125\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 341.2421875\n", "Distributor Cost: 3111.4736328125\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 438.81640625\n", "Factory Cost: 2259.15380859375\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 83.6728515625\n", "\n", " -------------------------------------------------\n", " Week 34\n", "-------------------------------------------------\n", "Retailer Cost: 854.703125\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 88.203125\n", "Wholesaler Cost: 2297.19921875\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 341.2421875\n", "Distributor Cost: 3330.8818359375\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 438.81640625\n", "Factory Cost: 2300.990234375\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 83.6728515625\n", "\n", " -------------------------------------------------\n", " Week 35\n", "-------------------------------------------------\n", "Retailer Cost: 894.3046875\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 79.203125\n", "Wholesaler Cost: 2467.8203125\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 341.2421875\n", "Distributor Cost: 3550.2900390625\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 438.81640625\n", "Factory Cost: 2342.82666015625\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 83.6728515625\n", "\n", " -------------------------------------------------\n", " Week 36\n", "-------------------------------------------------\n", "Retailer Cost: 929.40625\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 70.203125\n", "Wholesaler Cost: 2638.44140625\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 341.2421875\n", "Distributor Cost: 3769.6982421875\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 438.81640625\n", "Factory Cost: 2384.6630859375\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 83.6728515625\n", "\n", " -------------------------------------------------\n", " Week 37\n", "-------------------------------------------------\n", "Retailer Cost: 960.0078125\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 61.203125\n", "Wholesaler Cost: 2809.0625\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 341.2421875\n", "Distributor Cost: 3989.1064453125\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 438.81640625\n", "Factory Cost: 2426.49951171875\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 83.6728515625\n", "\n", " -------------------------------------------------\n", " Week 38\n", "-------------------------------------------------\n", "Retailer Cost: 986.109375\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 52.203125\n", "Wholesaler Cost: 2979.68359375\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 341.2421875\n", "Distributor Cost: 4208.5146484375\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 438.81640625\n", "Factory Cost: 2468.3359375\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 83.6728515625\n", "\n", " -------------------------------------------------\n", " Week 39\n", "-------------------------------------------------\n", "Retailer Cost: 1007.7109375\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 43.203125\n", "Wholesaler Cost: 3150.3046875\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 341.2421875\n", "Distributor Cost: 4427.9228515625\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 438.81640625\n", "Factory Cost: 2510.17236328125\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 83.6728515625\n", "\n", " -------------------------------------------------\n", " Week 40\n", "-------------------------------------------------\n", "Retailer Cost: 1024.8125\n", "Retailer Order: 0.0\n", "Retailer Effective Inventory: 34.203125\n", "Wholesaler Cost: 3320.92578125\n", "Wholesaler Order: 0.0\n", "Wholesaler Effective Inventory: 341.2421875\n", "Distributor Cost: 4647.3310546875\n", "Distributor Order: 0.0\n", "Distributor Effective Inventory: 438.81640625\n", "Factory Cost: 2552.0087890625\n", "Factory Order: 0.0\n", "Factory Effective Inventory: 83.6728515625\n", "\n", "--- Final Statistics ----\n", "Beer received by customer: 365.0\n" ] }, { "output_type": "display_data", "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {} }, { "output_type": "display_data", "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {} }, { "output_type": "display_data", "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {} } ], "source": [ "# Initialize queues between each actor in the supply chain\n", "# Top and bottom queues simulate order (top) and delivery (bottom) flows between actors\n", "wholesalerRetailerTopQueue = SupplyChainQueue(QUEUE_DELAY_WEEKS)\n", "wholesalerRetailerBottomQueue = SupplyChainQueue(QUEUE_DELAY_WEEKS)\n", "\n", "distributorWholesalerTopQueue = SupplyChainQueue(QUEUE_DELAY_WEEKS)\n", "distributorWholesalerBottomQueue = SupplyChainQueue(QUEUE_DELAY_WEEKS)\n", "\n", "factoryDistributorTopQueue = SupplyChainQueue(QUEUE_DELAY_WEEKS)\n", "factoryDistributorBottomQueue = SupplyChainQueue(QUEUE_DELAY_WEEKS)\n", "\n", "# Populate queues with initial orders to stabilize early weeks of the game\n", "# Each actor's order and delivery queues are initialized with CUSTOMER_INITIAL_ORDERS\n", "for i in range(0, 2):\n", " wholesalerRetailerTopQueue.PushEnvelope(CUSTOMER_INITIAL_ORDERS)\n", " wholesalerRetailerBottomQueue.PushEnvelope(CUSTOMER_INITIAL_ORDERS)\n", " distributorWholesalerTopQueue.PushEnvelope(CUSTOMER_INITIAL_ORDERS)\n", " distributorWholesalerBottomQueue.PushEnvelope(CUSTOMER_INITIAL_ORDERS)\n", " factoryDistributorTopQueue.PushEnvelope(CUSTOMER_INITIAL_ORDERS)\n", " factoryDistributorBottomQueue.PushEnvelope(CUSTOMER_INITIAL_ORDERS)\n", "\n", "# Instantiate the customer\n", "theCustomer = Customer()\n", "\n", "# Create Retailer, connected to the customer and wholesaler via queues\n", "myRetailer = Retailer(None, wholesalerRetailerTopQueue, wholesalerRetailerBottomQueue, None, theCustomer)\n", "\n", "# Create Wholesaler, connected to the retailer and distributor via queues\n", "myWholesaler = Wholesaler(wholesalerRetailerTopQueue, distributorWholesalerTopQueue,\n", " distributorWholesalerBottomQueue, wholesalerRetailerBottomQueue)\n", "\n", "# Create Distributor, connected to the wholesaler and factory via queues\n", "myDistributor = Distributor(distributorWholesalerTopQueue, factoryDistributorTopQueue,\n", " factoryDistributorBottomQueue, distributorWholesalerBottomQueue)\n", "\n", "# Create Factory, connected to the distributor via queues, with a production delay\n", "myFactory = Factory(factoryDistributorTopQueue, None, None, factoryDistributorBottomQueue, QUEUE_DELAY_WEEKS)\n", "\n", "# Initialize an object to track and record statistics across the simulation\n", "myStats = SupplyChainStatistics()\n", "\n", "# Simulation loop: Iterate over each week\n", "for thisWeek in range(0, WEEKS_TO_PLAY):\n", "\n", " print(\"\\n\", \"-\" * 49)\n", " print(f\" Week {thisWeek}\") # Print current week number\n", " print(\"-\" * 49)\n", "\n", " # Retailer's turn: process orders, calculate costs, and update inventory and statistics\n", " myRetailer.TakeTurn(thisWeek)\n", " myStats.RecordRetailerCost(myRetailer.GetCostIncurred())\n", " myStats.RecordRetailerOrders(myRetailer.GetLastOrderQuantity())\n", " myStats.RecordRetailerEffectiveInventory(myRetailer.CalcEffectiveInventory())\n", "\n", " # Wholesaler's turn: process orders, calculate costs, and update inventory and statistics\n", " myWholesaler.TakeTurn(thisWeek)\n", " myStats.RecordWholesalerCost(myWholesaler.GetCostIncurred())\n", " myStats.RecordWholesalerOrders(myWholesaler.GetLastOrderQuantity())\n", " myStats.RecordWholesalerEffectiveInventory(myWholesaler.CalcEffectiveInventory())\n", "\n", " # Distributor's turn: process orders, calculate costs, and update inventory and statistics\n", " myDistributor.TakeTurn(thisWeek)\n", " myStats.RecordDistributorCost(myDistributor.GetCostIncurred())\n", " myStats.RecordDistributorOrders(myDistributor.GetLastOrderQuantity())\n", " myStats.RecordDistributorEffectiveInventory(myDistributor.CalcEffectiveInventory())\n", "\n", " # Factory's turn: process orders, produce beer, calculate costs, and update statistics\n", " myFactory.TakeTurn(thisWeek)\n", " myStats.RecordFactoryCost(myFactory.GetCostIncurred())\n", " myStats.RecordFactoryOrders(myFactory.GetLastOrderQuantity())\n", " myStats.RecordFactoryEffectiveInventory(myFactory.CalcEffectiveInventory())\n", "\n", "# Output final results after simulation\n", "print(\"\\n--- Final Statistics ----\")\n", "print(\"Beer received by customer: {0}\".format(theCustomer.GetBeerReceived()))\n", "\n", "# Plot time-series data for orders, inventory, and costs over the weeks of simulation\n", "myStats.PlotOrders()\n", "myStats.PlotEffectiveInventory()\n", "myStats.PlotCosts()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.3" }, "colab": { "provenance": [] } }, "nbformat": 4, "nbformat_minor": 0 }