diff --git a/Simpy_Tutorial/environment.py b/Simpy_Tutorial/environment.py
index 78147453a4bef38af57b870656b09ffdc3885bd8..b94a844174eb4e5821b6664c55e8c6f26c5ef20d 100644
--- a/Simpy_Tutorial/environment.py
+++ b/Simpy_Tutorial/environment.py
@@ -2,6 +2,7 @@ import gymnasium as gym
 import numpy as np
 import processmodel as model
 import simpy
+import eventlog
 
 """
 Environment for the RL agent
@@ -40,6 +41,8 @@ class BusinessProcessEnv(gym.Env):
 
         self.reward = 0
 
+        self.bigeventlog = []
+
 
     def get_current_state(self, caseid):
         process, case, event = model.get_current_state(self.process, caseid)
@@ -94,6 +97,7 @@ class BusinessProcessEnv(gym.Env):
     
          
     def reset(self, seed=None, options=None):
+
         # Reset the environment to the initial state
         # Implement a function which extracts the current state from an event log / simulation model
         super().reset(seed=seed)
diff --git a/Simpy_Tutorial/eventlog.py b/Simpy_Tutorial/eventlog.py
index 27e541abb4b1feccb8a93c035a884f9ab9816c7d..8a31eaf99ca99fbcee7db494ea8598148373147e 100644
--- a/Simpy_Tutorial/eventlog.py
+++ b/Simpy_Tutorial/eventlog.py
@@ -11,21 +11,28 @@ def add_start_event(process, event_id, case_id, activity, start_timestamp):
     process.event_log[event_id] = {
         'CaseID': case_id,
         'Activity': activity,
-        'StartTimestamp': start_timestamp,
+        'StartTimestamp': float(start_timestamp),
+        'EndTimestamp': None
     }
 
 def add_end_event(process, event_id, end_timestamp):
-    if event_id in process.event_log:
-        event = process.event_log[event_id]
-        event['EndTimestamp'] = end_timestamp
-        process.event_log.append(event)
-        del process.event_log[event_id]
+    # if event_id in process.event_log:
+    event = process.event_log[event_id]
+    event['EndTimestamp'] = end_timestamp
+    # process.event_log.append(event)
+    # del process.event_log[event_id]
 
 # add functions for adding events with their attributes to the log
 
+<<<<<<<< HEAD:simpy_tutorial/eventlog.py
+def export_to_csv(env, file_path):
+    event_log_df = pd.DataFrame.from_dict(env.bigeventlog)
+    event_log_df.to_csv(file_path)
+========
 def export_to_csv(process, file_path):
     event_log_df = pd.DataFrame.from_dict(process.event_log)
     event_log_df.to_csv(file_path, index=False)
+>>>>>>>> 575b59827b5f928da4165070a4a1d51a85eed774:Simpy_Tutorial/eventlog.py
 
 def export_to_xes(process, file_path):
     # Use appropriate code to export to XES format
diff --git a/Simpy_Tutorial/processmodel.py b/Simpy_Tutorial/processmodel.py
index ea2fae666eee0a941d7ddad1394cb39ebbbe9b9f..1f158e087ba8c572f4b48a5f84c5634609e0a24a 100644
--- a/Simpy_Tutorial/processmodel.py
+++ b/Simpy_Tutorial/processmodel.py
@@ -78,12 +78,13 @@ def execute_case(env, case, process):
         case_obj.state[0] += 1
         case_obj.current = 1
         process.event_log.append(process.event_counter)
-        eventlog.add_start_event(process, process.event_counter, case, "a", env.now)
         event_counter = process.event_counter
+        eventlog.add_start_event(process, event_counter, case, "a", env.now)
         process.event_counter += 1
         print(f"Case {case}: started activity a at {env.now:.2f}")
         yield env.process(process.a(case))
-        eventlog.add_end_event(process, event_counter, env.now)
+        end = int(env.now)
+        eventlog.add_end_event(process, event_counter, end)
         print(f"Case {case}: finished activity a at {env.now:.2f}")
     
     if case_obj.agent: process.flag = False
@@ -250,8 +251,15 @@ def main():
     print(state['event'])
     print(business_env.flatten_observation(state))
     rl.q_learning(space, activities)
+<<<<<<<< HEAD:Simpy_Tutorial/processmodel.py
     path = r"C:\Users\nourm\OneDrive\Desktop\Nour\optis_app\out.csv"
     eventlog.export_to_csv(business_env.process, path)
+========
+
+    el = business_env.bigeventlog
+
+    # eventlog.export_to_csv(business_env, r'D:\test\optis.csv')
+>>>>>>>> origin/aleks2:simpy_tutorial/processmodel.py
     
 
 
diff --git a/Simpy_Tutorial/rlalgorithm.py b/Simpy_Tutorial/rlalgorithm.py
index ac44e92e4a1ebc892cfcf8bd610fb5a5d2db550a..ed3981a8307566359e6e4d46dbdbbdd9a4a60e44 100644
--- a/Simpy_Tutorial/rlalgorithm.py
+++ b/Simpy_Tutorial/rlalgorithm.py
@@ -3,6 +3,7 @@ import numpy as np
 import environment 
 import processmodel as model
 from gymnasium.wrappers import FlattenObservation
+import eventlog
 
 def q_learning(space, activities):
     # Define the business process environment
@@ -29,7 +30,7 @@ def q_learning(space, activities):
     epsilon = 0.1 # exploration rate
 
     # Train the agent using Q-learning
-    num_episodes = 10
+    num_episodes = 2
     for episode in range(num_episodes):
         state, _ = env.reset()
         state = env.flatten_observation(state)
@@ -52,4 +53,7 @@ def q_learning(space, activities):
             # Transition to the next state
             state = next_state
 
+        env.bigeventlog += env.process.event_log
+        eventlog.export_to_csv(env, r'D:\test\optis.csv')
+
         print(f"Episode {episode} ended.")
\ No newline at end of file
diff --git a/__pycache__/environment.cpython-311.pyc b/__pycache__/environment.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d4a8542792b100289cbe8d758a23e2a2279a6cc1
Binary files /dev/null and b/__pycache__/environment.cpython-311.pyc differ
diff --git a/__pycache__/eventlog.cpython-311.pyc b/__pycache__/eventlog.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..726340a5c7d89836ad99cf2cc6f04f840138e0d4
Binary files /dev/null and b/__pycache__/eventlog.cpython-311.pyc differ
diff --git a/__pycache__/processmodel.cpython-311.pyc b/__pycache__/processmodel.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..6285a50268e9a8c80f8bd8a91f005bfc3d523237
Binary files /dev/null and b/__pycache__/processmodel.cpython-311.pyc differ
diff --git a/__pycache__/rlalgorithm.cpython-311.pyc b/__pycache__/rlalgorithm.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..23d7225e816b44e1fc9cfb2a8718f19fcf3f889b
Binary files /dev/null and b/__pycache__/rlalgorithm.cpython-311.pyc differ
diff --git a/backend/__pycache__/agent.cpython-311.pyc b/backend/__pycache__/agent.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..87f701a04a31d900d91fd18b6bc837516be90163
Binary files /dev/null and b/backend/__pycache__/agent.cpython-311.pyc differ
diff --git a/backend/__pycache__/environment.cpython-311.pyc b/backend/__pycache__/environment.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..51daa198751b4836531ffe9cc564ed462aed29b8
Binary files /dev/null and b/backend/__pycache__/environment.cpython-311.pyc differ
diff --git a/backend/__pycache__/simplesimmodel.cpython-311.pyc b/backend/__pycache__/simplesimmodel.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7f8f062a2173a3f3440055bc14475b66274869c3
Binary files /dev/null and b/backend/__pycache__/simplesimmodel.cpython-311.pyc differ
diff --git a/backend/__pycache__/simulationmodel.cpython-311.pyc b/backend/__pycache__/simulationmodel.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..faf0672706b339b6fd1e7ee5c06392cb3993a759
Binary files /dev/null and b/backend/__pycache__/simulationmodel.cpython-311.pyc differ
diff --git a/backend/agent.py b/backend/agent.py
new file mode 100644
index 0000000000000000000000000000000000000000..111df067c5097be66021c5d64734dcc9f66f6117
--- /dev/null
+++ b/backend/agent.py
@@ -0,0 +1,69 @@
+import gymnasium as gym
+import numpy as np
+import environment 
+from gymnasium.wrappers import FlattenObservation
+
+"""
+RL agent
+"""
+
+def q_learning(space, activities):
+    # Define the business process environment
+    env = environment.BusinessProcessEnv(space, activities)
+
+    # Define the Q-table
+    num_states = 1
+
+    process_space = env.observation_space['process'].nvec 
+    case_space = env.observation_space['case'].nvec 
+    event_space = env.observation_space['event'].n
+    
+    for i in process_space: num_states *= i
+    for i in case_space: num_states *= i
+    num_states *= event_space
+    
+    num_actions = env.action_space.n
+
+    Q = np.zeros((num_states, num_actions), dtype=np.int16)
+
+    # Set the hyperparameters
+    alpha = 0.1   # learning rate
+    gamma = 0.99  # discount factor
+    epsilon = 0.1 # exploration rate
+
+    mean_time = 0
+
+    # Train the agent using Q-learning
+    num_episodes = 100
+    for episode in range(num_episodes):
+        state, _ = env.reset()
+        state = env.flatten_observation(state)
+        done = False
+        start = env.process.env.now
+        while not done:
+            # Choose an action based on the epsilon-greedy policy
+            if np.random.uniform(0, 1) < epsilon:
+                action = env.action_space.sample()
+            else:
+                action = np.argmax(Q[state])
+            
+            
+            # Execute the action and observe the next state and reward
+            next_state, reward, done, _ = env.step(action)
+
+            # Update the Q-value for the current state-action pair
+            Q[state][action] = Q[state][action] + alpha * (reward + gamma * np.max(Q[next_state]) - Q[state][action])
+            
+            # Transition to the next state
+            state = next_state
+
+        time = env.process.env.now - start 
+        mean_time += time
+
+        """
+        if (episode % 20 == 19):
+            mean_time /= 20 
+            print(f"Episode {episode-19} to episode {episode}: mean time = {mean_time}")
+        """
+
+        print(f"Episode {episode}: time = {time}")
\ No newline at end of file
diff --git a/backend/environment.py b/backend/environment.py
new file mode 100644
index 0000000000000000000000000000000000000000..b96b6425d7216e12269ab8b8a50577c25a72ae05
--- /dev/null
+++ b/backend/environment.py
@@ -0,0 +1,128 @@
+import gymnasium as gym
+import numpy as np
+import simpy
+import simplesimmodel as model
+
+"""
+Environment for the RL agent
+"""
+
+
+class BusinessProcessEnv(gym.Env):
+
+    def __init__(self, space, activities):
+        self.ressources = space[0]
+        self.case = space[1]
+        self.activities = activities
+        
+        self.observation_space = gym.spaces.Dict(
+            {
+                'process': gym.spaces.MultiDiscrete(self.ressources),
+                'case': gym.spaces.MultiDiscrete(self.case),
+                'event': gym.spaces.Discrete(self.activities)
+            }
+        )
+
+        self.action_space = gym.spaces.Discrete(self.activities)
+
+        self.current_state = {
+            'process': np.array(self.ressources),
+            'case': np.zeros(len(self.case), dtype=int),
+            'event': 0
+        }
+
+        self.model_env = simpy.Environment()
+        self.process = model.BusinessProcess(self.model_env, self.ressources)
+        self.model_env.process(model.run_process(self.model_env, self.process))
+        # self.done_cases = set([])
+
+        self.reward = 0
+
+
+    def get_current_state(self, caseid):
+        process, case, event = model.get_current_state(self.process, caseid)
+        state = {
+            'process': process,
+            'case': case,
+            'event': event
+        }
+        return state
+
+    def step(self, action):
+        
+        self.process.next = action
+
+        if self.process.case_id in self.process.done_cases:
+            self.process.case_id = np.random.choice(self.process.active_cases)
+        
+        case_obj = self.process.case_objects[self.process.case_id]
+        self.current_state = self.get_current_state(case_obj)
+
+        # print(f"{self.current_state['event']}")
+
+        start = self.process.env.now
+
+        self.process.flag = True
+
+        if self.process.is_valid(self.current_state['event'], action, case_obj):
+            
+            while(self.process.flag):
+                self.model_env.step()
+
+            stop = self.process.env.now
+
+            case_obj = self.process.case_objects[self.process.case_id]
+
+            print(f"Agent did case {self.process.case_id} activity {action}.")
+
+            next_state = self.get_current_state(case_obj)
+            self.current_state = next_state
+            next_state = self.flatten_observation(next_state)
+
+            self.reward += -(stop - start)
+            done = True if (len(self.process.done_cases) == 5 or len(self.process.active_cases) == 0) else False
+            return next_state, self.reward, done, None
+        
+        else: 
+            self.reward += -100
+            next_state = self.flatten_observation(self.current_state)
+            done = False
+            return next_state, self.reward, done, None
+    
+         
+    def reset(self, seed=None, options=None):
+        # Reset the environment to the initial state
+        # Implement a function which extracts the current state from an event log / simulation model
+        super().reset(seed=seed)
+
+        self.current_state = {
+            'process': np.array(self.ressources),
+            'case': np.zeros(len(self.case), dtype=int),
+            'event': 0
+        }
+        self.current_step = 0
+
+        self.model_env = simpy.Environment()
+        self.process = model.BusinessProcess(self.model_env, self.ressources)
+        self.model_env.process(model.run_process(self.model_env, self.process))
+        self.process.done_cases = set([])
+
+        self.reward = 0
+
+        return self.current_state, None
+        
+
+    def render(self, mode='human'):
+        # Render the current state of the environment
+        pass
+
+    
+    def flatten_observation(self, observation):
+        flattened = []
+        for i in observation['process']: flattened.append(i)
+        for j in observation['case']: flattened.append(j)
+        flattened.append(observation['event'])
+
+        return flattened
+
+
diff --git a/backend/eventlog.py b/backend/eventlog.py
new file mode 100644
index 0000000000000000000000000000000000000000..12c771efaaad9a950ead2e04aeba2be9f8c4cbb0
--- /dev/null
+++ b/backend/eventlog.py
@@ -0,0 +1,39 @@
+import pandas as pd
+import simplesimmodel as model
+
+"""
+Event log generator for our simulation model:
+- generate an event log
+- update an event log (adding new events)
+"""
+
+def add_start_event(process, event_id, case_id, activity, start_timestamp):
+    process.event_log[event_id] = {
+        'CaseID': case_id,
+        'Activity': activity,
+        'StartTimestamp': float(start_timestamp),
+        'EndTimestamp': None
+    }
+
+def add_end_event(process, event_id, end_timestamp):
+    # if event_id in process.event_log:
+    event = process.event_log[event_id]
+    event['EndTimestamp'] = end_timestamp
+    # process.event_log.append(event)
+    # del process.event_log[event_id]
+
+# add functions for adding events with their attributes to the log
+
+<<<<<<<< HEAD:Simpy_Tutorial/eventlog.py
+def export_to_csv(process, file_path):
+    event_log_df = pd.DataFrame.from_dict(process.event_log)
+    event_log_df.to_csv(file_path, index=False)
+========
+def export_to_csv(env, file_path):
+    event_log_df = pd.DataFrame.from_dict(env.bigeventlog)
+    event_log_df.to_csv(file_path)
+>>>>>>>> origin/aleks2:backend/eventlog.py
+
+def export_to_xes(process, file_path):
+    # Use appropriate code to export to XES format
+    pass
\ No newline at end of file
diff --git a/backend/simplesimmodel.py b/backend/simplesimmodel.py
new file mode 100644
index 0000000000000000000000000000000000000000..fe23eb4b077fbfcf15c62daa4a7e844ac1e875e0
--- /dev/null
+++ b/backend/simplesimmodel.py
@@ -0,0 +1,519 @@
+import simpy
+import random
+import numpy as np
+import environment 
+import agent
+
+"""
+Simulation model for a simpler business process, including:
+- a class representing the process
+- a function representing the process flow
+- a function to run the process
+"""
+
+
+""" 
+This class represents the process with its:
+- ressources: system, managers, stock handlers (a, b and c), manufacturers (a and b), delivery services (a, b and c)
+- activities
+"""
+class BusinessProcess(object):
+    """ Initialize the process with the number of the different types of ressources """ 
+    def __init__(self, env, ressources):
+        
+        # initialize simulation environment
+        self.env = env
+
+        # initialize ressources
+        self.system = simpy.Resource(env, ressources[0])
+        self.order_taker = simpy.Resource(env, ressources[1])
+        self.stock_handler_a = simpy.Resource(env, ressources[2])
+        self.stock_handler_b = simpy.Resource(env, ressources[3])
+        self.stock_handler_c = simpy.Resource(env, ressources[4])
+        self.manufacturer_a = simpy.Resource(env, ressources[5])
+        self.manufacturer_b = simpy.Resource(env, ressources[6])
+        self.packer_a = simpy.Resource(env, ressources[7])
+        self.packer_b = simpy.Resource(env, ressources[8])
+        self.packer_c = simpy.Resource(env, ressources[9])
+        # capacity of each delivery service instead of numbers of workers
+        self.delivery_service_a = simpy.Resource(env, ressources[10])
+        self.delivery_service_b = simpy.Resource(env, ressources[11])
+        self.delivery_service_c = simpy.Resource(env, ressources[12])
+    
+        # initialize lists with active cases and all case objects
+        self.active_cases = [0, 1, 2]
+        case_0 = Case(0)
+        case_1 = Case(1)
+        case_2 = Case(2)
+        self.case_objects = [case_0, case_1, case_2]
+        
+        # flag indicates whether the process is currently controlled by the agent and we set it on false every time the agent does an activity
+        self.flag = True
+
+        self.next = 0
+        
+        self.case_id = 0
+
+        self.done_cases = set([])
+
+        random.seed(1)
+
+    def place_order(self, case):
+        yield self.env.timeout(0)
+
+    def arrange_standard_order(self, case):
+        yield self.env.timeout(random.randint(10, 15))
+    
+    def arrange_custom_order(self, case):
+        yield self.env.timeout(random.randint(20, 30))
+    
+    def pick_from_stock_a(self, case):
+        yield self.env.timeout(random.randint(10, 40))
+    
+    def pick_from_stock_b(self, case):
+        yield self.env.timeout(random.randint(20, 60))
+
+    def pick_from_stock_c(self, case):
+        yield self.env.timeout(random.randint(30, 80))
+    
+    def manufacture_a(self, case):
+        yield self.env.timeout(random.randint(240, 360))
+    
+    def manufacture_b(self, case):
+        yield self.env.timeout(random.randint(360, 480))
+    
+    def pack_a(self, case):
+        yield self.env.timeout(random.randint(10, 20))
+    
+    def pack_b(self, case):
+        yield self.env.timeout(random.randint(15, 30))
+    
+    def pack_c(self, case):
+        yield self.env.timeout(random.randint(25, 50))
+    
+    def attempt_delivery_a(self, case):
+        yield self.env.timeout(random.randint(720, 1440))
+    
+    def attempt_delivery_b(self, case):
+        yield self.env.timeout(random.randint(1440, 2160))
+
+    def attempt_delivery_c(self, case):
+        yield self.env.timeout(random.randint(1440, 2880))
+    
+    def order_completed(self, case):
+        yield self.env.timeout(1)
+    
+    def is_valid(self, event, action, case_obj):
+        if event == 0 and action == 1:
+            return True
+        elif event == 1 and (action == 2 or action == 3):
+            return True
+        elif event == 2 and case_obj.standard_order and (action == 4 or action == 5 or action == 6):
+            return True
+        elif event == 3 and (not case_obj.standard_order) and (action == 7 or action == 8):
+            return True
+        elif (4 <= event <= 8) and (action == 9 or action == 10 or action == 11):
+            return True
+        elif (9 <= event <= 11) and (12 <= action <=14):
+            return True
+        elif (12 <= event <=14) and action == 15:
+            return True
+        else:
+            return False
+
+"""
+A class representing a single case from the process. Every case has an (unique) case id, 
+state showing which activities have been already done, current activity being executed,
+agent flag showing if the agent is currently controlling the case and 
+a flag whether the order is standard
+"""
+class Case(object):
+    def __init__(self, case):
+        self.case_id = case
+        self.state = np.zeros(15, dtype = int)
+        self.current = 0
+        self.agent = False 
+        self.standard_order = True
+
+"""
+A function, which represents the process model with it's control flow and ressource allocation.
+A customer places an order at the system and then is redirected to the order takers, where it's arranged 
+what type of order the customer wants - standard or custom. After that depending on whether the order 
+is standard or custom, respectively a stock or manufacturer is chosen. 
+Afterwards one of three packing options is chosen and executed. 
+Following the packing one of the three delivery services deliver the order. 
+With that the order is completed.
+"""
+def execute_case(env, case, process):
+    # create a case object to keep track of case attributes if not already existing
+    if len(process.case_objects) <= case:
+        case_obj = Case(case)
+
+        # add the case to the process's active cases list
+        process.case_objects.append(case_obj) 
+        process.active_cases.append(case) 
+
+    # if the case is one of the first three choose it from the list
+    if len(process.case_objects) <= 3:
+        case_obj = process.case_objects[case]
+
+    # if case chosen by agent set agent flag to true
+    if process.case_id == case: 
+        case_obj.agent = True
+
+    # place order
+    with process.system.request() as request:
+        yield request
+        case_obj.state[0] += 1
+        case_obj.current = 1
+        yield env.process(process.place_order(case))
+        if case_obj.agent: print(f"Case {case}: 'placed order' at {env.now:.2f}")
+
+
+    # if the last action was made from the agent set the process flag to be able to return to the environment's step function
+    if case_obj.agent: 
+        process.flag = False
+
+        with process.system.request() as request:
+            yield request
+
+    # before a new action is executed check if the agent is controlling the case and set the flag to true if yes
+    if process.case_id == case: case_obj.agent = True 
+
+    # standard order XOR custom order
+    choice = random.randint(2,3) if not case_obj.agent else process.next 
+    if choice == 2:
+        case_obj.standard_order = True
+        with process.order_taker.request() as request:
+            yield request
+            case_obj.state[1] += 1
+            case_obj.current = 2
+            if case_obj.agent: print(f"Case {case}: started 'arrange standard order' at {env.now:.2f}")
+            yield env.process(process.arrange_standard_order(case))
+            if case_obj.agent: print(f"Case {case}: finished 'arrange standard order' at {env.now:.2f}")
+            if case_obj.agent: 
+                process.flag = False
+
+                with process.system.request() as request:
+                        yield request
+    else:
+        case_obj.standard_order = False
+        with process.order_taker.request() as request:
+            yield request
+            case_obj.state[2] += 1
+            case_obj.current = 3
+            if case_obj.agent: print(f"Case {case}: started 'arrange custom order' at {env.now:.2f}")
+            yield env.process(process.arrange_custom_order(case))
+            if case_obj.agent: print(f"Case {case}: finished 'arrange custom order' at {env.now:.2f}")
+            if case_obj.agent: 
+                process.flag = False
+
+                with process.system.request() as request:
+                        yield request
+
+
+    if process.case_id == case: case_obj.agent = True 
+
+    if case_obj.standard_order and (not case_obj.agent):
+        choice = random.randint(4,6)
+    elif (not case_obj.standard_order) and (not case_obj.agent):
+        choice = random.randint(7,8)
+    else:
+        choice = process.next
+     
+    # choose stock or manufacturer
+    if choice == 4:
+        with process.stock_handler_a.request() as request:
+            yield request
+            case_obj.state[3] += 1
+            case_obj.current = 4
+            if case_obj.agent: print(f"Case {case}: started 'pick from stock A' at {env.now:.2f}")
+            yield env.process(process.pick_from_stock_a(case))
+            if case_obj.agent: print(f"Case {case}: finished 'pick from stock A' at {env.now:.2f}")
+            if case_obj.agent: 
+                process.flag = False
+
+                with process.system.request() as request:
+                    yield request
+    elif choice == 5:
+        with process.stock_handler_b.request() as request:
+            yield request
+            case_obj.state[4] += 1
+            case_obj.current = 5
+            if case_obj.agent: print(f"Case {case}: started 'pick from stock B' at {env.now:.2f}")
+            yield env.process(process.pick_from_stock_b(case))
+            if case_obj.agent: print(f"Case {case}: finished 'pick from stock B' at {env.now:.2f}")
+            if case_obj.agent: 
+                process.flag = False
+
+                with process.system.request() as request:
+                    yield request
+    elif choice == 6:
+        with process.stock_handler_c.request() as request:
+            yield request
+            case_obj.state[5] += 1
+            case_obj.current = 6
+            if case_obj.agent: print(f"Case {case}: started 'pick from stock C' at {env.now:.2f}")
+            yield env.process(process.pick_from_stock_c(case))
+            if case_obj.agent: print(f"Case {case}: finished 'pick from stock C' at {env.now:.2f}")
+            if case_obj.agent: 
+                process.flag = False
+
+                with process.system.request() as request:
+                    yield request
+    elif choice == 7:
+        with process.manufacturer_a.request() as request:
+            yield request
+            case_obj.state[6] += 1
+            case_obj.current = 7
+            if case_obj.agent: print(f"Case {case}: started 'manufacture A' at {env.now:.2f}")
+            yield env.process(process.manufacture_a(case))
+            if case_obj.agent: print(f"Case {case}: finished 'manufacture A' at {env.now:.2f}")
+            if case_obj.agent: 
+                process.flag = False
+
+                with process.system.request() as request:
+                    yield request
+    else:
+        with process.manufacturer_b.request() as request:
+            yield request
+            case_obj.state[7] += 1
+            case_obj.current = 8
+            if case_obj.agent: print(f"Case {case}: started 'manufacture B' at {env.now:.2f}")
+            yield env.process(process.manufacture_b(case))
+            if case_obj.agent: print(f"Case {case}: finished 'manufacture B' at {env.now:.2f}")
+            if case_obj.agent: 
+                process.flag = False
+
+                with process.system.request() as request:
+                    yield request
+
+
+    if process.case_id == case: case_obj.agent = True 
+
+    choice = random.randint(9,11) if not case_obj.agent else process.next
+
+    if choice == 9:
+        with process.packer_a.request() as request:
+            yield request
+            case_obj.state[8] += 1
+            case_obj.current = 9
+            if case_obj.agent: print(f"Case {case}: started 'pack A' at {env.now:.2f}")
+            yield env.process(process.pack_a(case))
+            if case_obj.agent: print(f"Case {case}: finished 'pack A' at {env.now:.2f}")
+            if case_obj.agent: 
+                    process.flag = False
+
+                    with process.system.request() as request:
+                        yield request
+    elif choice == 10:
+        with process.packer_b.request() as request:
+            yield request
+            case_obj.state[9] += 1
+            case_obj.current = 10
+            if case_obj.agent: print(f"Case {case}: started 'pack B' at {env.now:.2f}")
+            yield env.process(process.pack_b(case))
+            if case_obj.agent: print(f"Case {case}: finished 'pack B' at {env.now:.2f}")
+            if case_obj.agent: 
+                    process.flag = False
+
+                    with process.system.request() as request:
+                        yield request
+    else:
+        with process.packer_c.request() as request:
+            yield request
+            case_obj.state[10] += 1
+            case_obj.current = 11
+            if case_obj.agent: print(f"Case {case}: started 'pack C' at {env.now:.2f}")
+            yield env.process(process.pack_c(case))
+            if case_obj.agent: print(f"Case {case}: finished 'pack C' at {env.now:.2f}")
+            if case_obj.agent: 
+                    process.flag = False
+
+                    with process.system.request() as request:
+                        yield request
+
+    if process.case_id == case: case_obj.agent = True 
+
+    # choose delivery
+    choice = random.randint(12,14) if not case_obj.agent else process.next
+    if choice == 12:
+        with process.delivery_service_a.request() as request:
+            yield request
+            case_obj.state[11] += 1
+            case_obj.current = 12
+            if case_obj.agent: print(f"Case {case}: started 'attempt delivery A' at {env.now:.2f}")
+            yield env.process(process.attempt_delivery_a(case))
+            if case_obj.agent: print(f"Case {case}: finished 'attempt delivery A' at {env.now:.2f}")
+            if case_obj.agent: 
+                    process.flag = False
+
+                    with process.system.request() as request:
+                        yield request
+    elif choice == 13:
+        with process.delivery_service_b.request() as request:
+            yield request
+            case_obj.state[12] += 1
+            case_obj.current = 13
+            if case_obj.agent: print(f"Case {case}: started 'attempt delivery B' at {env.now:.2f}")
+            yield env.process(process.attempt_delivery_b(case))
+            if case_obj.agent: print(f"Case {case}: finished 'attempt delivery B' at {env.now:.2f}")
+            if case_obj.agent: 
+                    process.flag = False
+
+                    with process.system.request() as request:
+                        yield request
+    else: 
+        with process.delivery_service_c.request() as request:
+            yield request
+            case_obj.state[13] += 1
+            case_obj.current = 14
+            if case_obj.agent: print(f"Case {case}: started 'attempt delivery C' at {env.now:.2f}")
+            yield env.process(process.attempt_delivery_c(case))
+            if case_obj.agent: print(f"Case {case}: finished 'attempt delivery C' at {env.now:.2f}")
+            if case_obj.agent: 
+                    process.flag = False
+
+                    with process.system.request() as request:
+                        yield request
+    
+
+    if process.case_id == case: case_obj.agent = True 
+
+    # case completed
+    with process.system.request() as request:
+        yield request
+        case_obj.state[14] += 1
+        case_obj.current = 15
+        yield env.process(process.order_completed(case))
+        if case_obj.agent: print(f"Case {case}: 'completed' at {env.now:.2f}")
+
+
+    if case in process.active_cases:
+        process.active_cases.remove(case)
+    
+    if case_obj.agent: 
+        for i in process.case_objects:
+            if (i.current == 15) and (i.case_id in process.active_cases) and (i.case_id != case):
+                process.active_cases.remove(i.case_id)
+        process.done_cases.add(process.case_id)
+        process.flag = False 
+    
+def get_current_state(process, case):
+    process_state = []
+
+    num_system = process.system.capacity - process.system.count
+    process_state.append(num_system)
+
+    num_order_taker = process.order_taker.capacity - process.order_taker.count 
+    process_state.append(num_order_taker)
+
+    num_stock_handler_a = process.stock_handler_a.capacity - process.stock_handler_a.count 
+    process_state.append(num_stock_handler_a)
+
+    num_stock_handler_b = process.stock_handler_b.capacity - process.stock_handler_b.count 
+    process_state.append(num_stock_handler_b)
+
+    num_stock_handler_c = process.stock_handler_c.capacity - process.stock_handler_c.count 
+    process_state.append(num_stock_handler_c)
+
+    num_manufacturer_a = process.manufacturer_a.capacity - process.manufacturer_a.count 
+    process_state.append(num_manufacturer_a)
+
+    num_manufacturer_b = process.manufacturer_b.capacity - process.manufacturer_b.count 
+    process_state.append(num_manufacturer_b)
+
+    num_packer_a = process.packer_a.capacity - process.packer_a.count 
+    process_state.append(num_packer_a)
+
+    num_packer_b = process.packer_b.capacity - process.packer_b.count 
+    process_state.append(num_packer_b)
+
+    num_packer_c = process.packer_c.capacity - process.packer_c.count 
+    process_state.append(num_packer_c)
+
+    num_delivery_service_a = process.delivery_service_a.capacity - process.delivery_service_a.count 
+    process_state.append(num_delivery_service_a)
+
+    num_delivery_service_b = process.delivery_service_b.capacity - process.delivery_service_b.count
+    process_state.append(num_delivery_service_b) 
+
+    num_delivery_service_c = process.delivery_service_c.capacity - process.delivery_service_c.count 
+    process_state.append(num_delivery_service_c)
+
+    cur_case = case.state
+
+    event  = case.current
+
+    return process_state, cur_case, event
+
+def run_process(env, process):
+    # process = Process(env, num_ot, num_m, num_sh_a, num_sh_b, num_sh_c, num_m_a, num_m_b, num_ds_a, num_ds_b, num_ds_c)
+    
+    # the waiting orders
+    for case in range(3):
+        env.process(execute_case(env, case, process))
+
+    # the new incoming orders
+    while case < 1000:
+        waittime = random.randint(10,15)
+        yield env.timeout(waittime)  # Wait a bit before generating a new case
+
+        case += 1
+        # process.active_cases.append(case)
+        env.process(execute_case(env, case, process))
+
+def main():
+    # Setup
+    # we can use a random seed if we want to generate the same results every time (maybe useful later for the training)
+    # random.seed(42)
+    # initialize the number of resources
+
+    process = [] 
+    num_s = 1
+    process.append(num_s)
+    num_ot = 5
+    process.append(num_ot)
+    num_sh_a = 3
+    process.append(num_sh_a)
+    num_sh_b = 3
+    process.append(num_sh_b)
+    num_sh_c = 3
+    process.append(num_sh_c)
+    num_m_a = 3
+    process.append(num_m_a)
+    num_m_b = 2
+    process.append(num_m_b)
+    num_p_a = 4
+    process.append(num_p_a)
+    num_p_b = 5
+    process.append(num_p_b)
+    num_p_c = 4
+    process.append(num_p_c)
+    num_ds_a = 8
+    process.append(num_ds_a)
+    num_ds_b = 8
+    process.append(num_ds_b)
+    num_ds_c = 8
+    process.append(num_ds_c)
+
+    case = []
+    for i in range(15):
+        case.append(1)
+        
+    space = [process, case]
+    activities = 16
+ 
+    business_env = environment.BusinessProcessEnv(space, activities)
+    print(business_env.observation_space.shape)
+    print(business_env.observation_space.sample())
+
+    state, _ = business_env.reset()
+    print(state)
+    print(business_env.current_state)
+    print(state['event'])
+    print(business_env.flatten_observation(state))
+    agent.q_learning(space, activities)
+
+if __name__ == "__main__":
+    main()