def run_qlearning_episode(mdp, agent):
    """ Execute the Q-learning off-policy algorithm per Section 6.5.
    This is paired to an agent for the agent.run_episode() call.
    """
    # record episode path and actions
    states_visited = []
    actions_performed = []
    episode_rewards = 0

    # initialize S
    state = mdp.reset_state()
    states_visited.append(state)

    # loop for each step
    while not mdp.is_goal(state):
        # choose A from S using policy derived from Q
        action = agent.get_action(state)

        # take action A, observe R, S'
        next_state, reward = mdp.get_state_reward_transition(state, action)

        # update agent
        agent.update(state, action, reward, next_state)
        # update state
        state = next_state

        # record path
        states_visited.append(state)
        actions_performed.append(action)
        episode_rewards += reward

    return states_visited, actions_performed, episode_rewards