oTree Forum >

Modifying page_sequence during app

#1 by jamiehax

I have a game where I would like to repeat a certain subset of pages with a 50% probability each time after reaching the end of that subset. I call add_interaction in an after_all_players_arrive method from the ResultsWaitPage. The function to add pages to the page_sequence seems to work fine, but the problem seems to be that oTree doesn't look at this page_sequence after __init__.py is first called. I know I could get similar functionality with a live page, but it would be nice to get it to work this way of anyone has any ideas.

class ResultsWaitPage(WaitPage):
    def after_all_players_arrive(group: Group):
        group.round_num += 1
        payout_tuple = C.PAYOUT_MATRIX[group.p1_choice][group.p2_choice]
        group.get_player_by_id(1).round_payout = payout_tuple[0]
        group.get_player_by_id(1).total_payout += payout_tuple[0]
        group.get_player_by_id(2).round_payout = payout_tuple[1]
        group.get_player_by_id(2).total_payout += payout_tuple[1]

page_sequence = [Instructions, InteractionWaitPage, Interaction, ResultsWaitPage, AfterInteraction, Results]

def add_interaction():
    r = random.randint(1, 100)
    if r <= 50:
        for index, page in reversed(list(enumerate(page_sequence))):
            if type(page) is type(AfterInteraction):
                page_sequence.insert(index + 1, InteractionWaitPage)
                page_sequence.insert(index + 2, Interaction)
                page_sequence.insert(index + 3, ResultsWaitPage)
                page_sequence.insert(index + 4, AfterInteraction)

#2 by coralio (edited )


I think you can't do it that way. The right way to do this in oTree is to use many rounds (like NUM_ROUNDS =30 in the Constants class C), and then control whether they are displayed or not in the static method is_displayed.

- Create many rounds (30). Using prob=0.5, reaching round 30 is less than 1 over a billion. Having 30 rounds is not costly.
- Aux pages InteractionWaitPage, Interaction, ResultsWaitPage and AfterInteraction ( I don't know if AfterInteraction is an actual page or some artifact you created) should only be shown if r is <= 50.
- The result of r is a random boolean "is_shown". Where "is_shown" should be stored depends on whether it's common to all players in a group, groups in a session, or to each player individually. For instance, if the r is the same for all players in the game, we compute "is_shown" and store it in the session object. The resulting code would be as follows (even though I'd rather do all the random computation in creating_session, I'll do it your way here):

class C(BaseConstants):
     NUM_ROUNDS = 30
# At the very beginning, when the session is being created, 
# all aux pages are shown for the first round:
def creating_session(subsession):
    if subsession.round_number == 1:
        subsession.is_shown = True

# We store is_shown in the Subsession class. (If r were different accross groups, do this in the Group class)
class Subsession(BaseSubsession):
    # This field determined whether each round is shown for all players
    is_shown = models.BooleanField(initial=False)

class ResultsWaitPage(WaitPage):
    # We wait for all players in the session, since "is_shown will be common to all.
    wait_for_all_groups = True
    # Note now that after_all_players_arrive is a subsession method
    def after_all_players_arrive(subsession):
        # I've passed subsession to your function.
def add_interaction(subsession):
    # In the last round, we can't decide nothing about the next round
    if subsession.round_number == C.NUM_ROUNDS:
    r = random.randint(1, 100)
    if r <= 50
        # We decide whether aux pages are shown in the NEXT ROUND.
        # This is stored in the session object, because we want the outcome to be the same for all players
        subsession.in_round(subsession.round_number + 1).is_shown = True

class Instructions(Page):
    def is_displayed(player: Player):
        return player.round_number == 1;
# Show Results only in the last round
class Results(Page):
    def is_displayed(player: Player):
        return player.round_number == C.NUM_ROUNDS; 

# Repeat the code below the rest of aux pages: Interaction, ResultsWaitPage, AfterInteraction
class InteractionWaitPage(WaitPage):
    def is_displayed(player: Player):
        return player.session.is_shown;
page_sequence = [Instructions, InteractionWaitPage, Interaction, ResultsWaitPage, AfterInteraction, Results]

Write a reply

Set forum username