def step(self): """ Take one step in the simulation. (a) Generate an additional line for the ca. (Use a copy of self.ca_lines[-1].) (b) Extend all lines in ca_lines if the new line is longer (with additional 1's) than its predecessor. (c) Trim the new line and add to self.ca_lines (d) Refresh display from values in self.ca_lines. """ # (a) new_line: str = self.generate_new_line_from_current_line( copy(self.ca_lines[-1]) ) # The new state derived from self.ca_lines[-1] # (b) # Extend lines in self.ca_lines at each end as needed. # (Don't extend for extra 0's at the ends.) # Can't do step (c) first because we would lose track of which end was extended. self.extend_ca_lines_if_needed(new_line) # (c) # Drop extraneous 0s at the end of new_line trimmed_new_line = self.drop_extraneous_0s_from_ends_of_new_line( new_line) # Add trimmed_new_line to the end of self.ca_lines self.ca_lines.append(trimmed_new_line) # (d) # Refresh the display from self.ca_lines self.set_display_from_lines() # Update the 'rows' widget. gui_set('rows', value=len(self.ca_lines))
def setup(self): Agent.id = 0 Minority_Game_World.steps_to_win = gui_get(STEPS_TO_WIN) # Adjust how far one step is based on number of steps needed to win Minority_Game_World.one_step = ( gui.PATCH_COLS - 2) * gui.BLOCK_SPACING() / Minority_Game_World.steps_to_win # For longer/shorter races, speed up/slow down frames/second SimEngine.fps = round(6 * Minority_Game_World.steps_to_win / 50) # self.done will be True if this a repeat game with the same agents. if self.done: self.reset_agents() return # This is the normal setup. Minority_Game_World.nbr_agents = gui_get(NBR_AGENTS) if Minority_Game_World.nbr_agents % 2 == 0: Minority_Game_World.nbr_agents += ( 1 if Minority_Game_World.nbr_agents < gui.WINDOW[NBR_AGENTS].Range[1] else (-1)) gui_set(NBR_AGENTS, value=Minority_Game_World.nbr_agents) Minority_Game_World.random_agent_ids = { 0, Minority_Game_World.nbr_agents - 1 } # Generate a random initial history self.history_length = gui_get(HISTORY_LENGTH) self.history = [choice([0, 1]) for _ in range(self.history_length)] self.generate_the_agents()
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # self.pos_to_switch is a dictionary that maps position values in a binary number to range(8) represented # as 3-digit binary strings: # {1: '000', 2: '001', 4: '010', 8: '011', 16: '100', 32: '101', 64: '110', 128: '111'} # The three digits are the rule components and the keys to the switches. # To see it, try: print(self.pos_to_switch) after executing the next line. # The function bin_str() is defined in utils.py # The following two lines do the same thing. Explain how both work. # self.pos_to_switch0 = {2**i: bin_str(i, 3) for i in range(8)} self.pos_to_switch = dict( zip([2**i for i in range(8)], CA_World.bin_0_to_7)) # The rule number used for this run, initially set to 110 as the default rule. # (You might also try rule 165.) # The following sets the local variable self.rule_nbr. It doesn't change the 'Rule_nbr' slider widget. self.rule_nbr = 110 # Set the switches and the binary representation of self.rule_nbr. self.set_switches_from_rule_nbr() self.set_binary_nbr_from_rule_nbr() gui_set('Rule_nbr', value=self.rule_nbr) # self.ca_lines is a list of lines, each of which is a list of 0/1. Each line represents # a state of the CA, i.e., all the cells in the line. self.ca_list contains the entire # history of the CA. # ! List of ines that is initialized to an empty list. The values from the nested list get copied to patches for each line self.ca_lines: List[List[int]] = [] # ! Sets the rows number based on how many lists are nested inside of self.ca_lines gui_set('rows', value=len(self.ca_lines))
def step(self): """ Take one step in the simulation. (a) Generate an additional line for the ca. (Use a copy of self.ca_lines[-1].) (b) Extend all lines in ca_lines if the new line is longer (with additional 1's) than its predecessor. (c) Trim the new line and add it to the end of self.ca_lines. (d) Refresh display from values in self.ca_lines. """ # (a) new_line: str = self.generate_new_line_from_current_line( self.ca_lines[-1]) # (b) # Extend lines in self.ca_lines at each end as needed. (Don't extend for extra 0's at the ends.) # If either end is '1', add '0' to that end of all strings. # Can't drop the 0's first because we would lose track of which end was extended. ... # (c) # ==> String-specific <== # (Must compare to '1'. Can't just use character as boolean. '0' is treated as True.) start = 0 if new_line[0] == '1' else 1 end = len(new_line) if new_line[-1] == '1' else len(new_line) - 1 trimmed_new_line: str = new_line[start:end] # Add trimmed_new_line to the end of self.ca_lines self.ca_lines.append(trimmed_new_line) # (d) # Refresh the display from self.ca_lines self.set_display_from_lines() # Update the 'rows' widget. gui_set('rows', value=...)
def step(self): # If we are doing animation: if Braess_World.state == 'a': if not Agent.key_step_done: Agent.run_an_animation_step() else: self.setup_2() return # We are not doing animation. We are in either state_1 or state_2. Process them normally. # If there was a change during the previous step, see if additional changes are needed. if Agent.some_agent_changed: # When a link changes length, Agent.some_agent_changed is set to True. Agent.some_agent_changed = False for lnk in self.adjustable_links: lnk.adjust_nodes() else: # Since no agent changed on the previous step, we're done with this state. # "Click" the STOP button. gui.WINDOW[GOSTOP].click() # Enable/disable the Cut-cord button depending on whether we are leaving state 1. gui_set(Braess_World.CUT_CORD, enabled=(Braess_World.state == 1))
def setup(self): """ Set up for state 1. Build the contraption piece by piece. """ self.top_spring = Braess_Link.vertical_linked_nodes(Braess_Link, self.x, Braess_World.top) self.top_cord = self.top_spring.extend_linked_nodes(Braess_Cord) self.bottom_spring = self.top_cord.extend_linked_nodes(Braess_Link) self.weight_cord = self.bottom_spring.extend_linked_nodes(Braess_Cord) # Make node_2 of the weight_cord the weight. Braess_World.weight_node = self.weight_cord.node_2 Braess_World.weight_node.shape_name = CIRCLE Braess_World.weight_node.color = Color('plum4') # ## Done with building state 1. ## # self.adjustable_links = [self.top_spring, self.top_cord, self.bottom_spring, self.weight_cord] gui_set(Braess_World.CUT_CORD, enabled=False) Braess_World.state = 1 Agent.some_agent_changed = True # In case we did the animation in slow motion and this is a second run. # Can't do this when animation ends because we want to stay # in slow motion as the springs contract and lift the weight. SimEngine.fps = 60
def set_binary_nbr_from_rule_nbr(self): """ Translate self.rule_nbr into a binary string and put it into the gui.WINDOW['bin_string'] widget. For example, if self.rule_nbr is 110, the string '(01101110)' is stored in gui.WINDOW['bin_string']. Include the parentheses around the binary number. Use gui_set('bin_string', value=new_value) to update the value of the widget. """ gui_set('bin_string', value=bin(self.rule_nbr)[2:].zfill(8))
def make_switches_and_rule_nbr_consistent(self): """ Make the Slider, the switches, and the bin number consistent: all should contain self.rule_nbr. """ current_switches_nbr = self.get_rule_nbr_from_switches() # ** switches number and rule number are not the same if current_switches_nbr != self.rule_nbr: self.rule_nbr = current_switches_nbr gui_set('Rule_nbr', value=self.rule_nbr) else: # ** the slider changed self.rule_nbr = gui_get('Rule_nbr') self.set_switches_from_rule_nbr() # ** update the binary number for any change in rule number self.set_binary_nbr_from_rule_nbr()
def handle_event(self, event): """ This is called when a GUI widget is changed and the change isn't handled by the system. The key of the widget that changed is in event. """ # Handle color change requests. super().handle_event(event) # Handle rule nbr change events, either switches or rule_nbr slider if event in ['Rule_nbr'] + CA_World.bin_0_to_7: self.make_switches_and_rule_nbr_consistent() # When the user checks the 'Random?' box, the Input line area should disappear. # When the user unchecks the 'Random?' box, the Input line area should re-appear. elif event == 'Random?': disabled = gui_get('Random?') gui_set('init_line', visible=not disabled, value=1)
def setup(self): GA_World.individual_class = Cycle_Individual GA_World.chromosome_class = Cycle_Chromosome Cycle_World.cycle_length = gui_get('cycle_length') gui_set('Max generations', value=float('inf')) gui_set('pop_size', value=100) self.pop_size = 100 gui_set('prob_random_parent', value=20) Cycle_World.cycle_length = gui_get('cycle_length') super().setup()
def set_results(self): """ Find and display the best individual. """ current_best_ind = self.get_best_individual() if self.best_ind is None or current_best_ind.discrepancy < self.best_ind.discrepancy: self.best_ind = current_best_ind gui_set('best_fitness', value=round(self.best_ind.fitness, 1)) gui_set('discrepancy', value=round(self.best_ind.discrepancy, 1)) gui_set('generations', value=self.generations) if self.best_ind.discrepancy == 0 or self.generations >= gui_get('Max generations'): self.done = True
def setup_a(self): """ Set up for the drop weight animation. """ # Move out by a small amount so that the two lines can be seen. step = 5 if gui_get('Pause?') else 0 self.top_spring.move_by_dxdy((step, 0)) self.top_spring.set_target_by_dxdy((self.x_offset-step, 0)) self.weight_cord.set_target_by_dxdy((0, Braess_World.cord_slack)) center_bar_node = self.bottom_spring.node_2 # Construct the full bar. bar_y = center_bar_node.y left_bar_node = Braess_Node(Pixel_xy( (self.x, bar_y) ) ) right_bar_node = Braess_Node(Pixel_xy( (self.x, bar_y) ) ) # By convention, the position of node_1 determines node_2's position. # In this case, since we will be pulling the bar up by its ends, # make the ends the controlling nodes. self.bar_right = Braess_Bar(right_bar_node, center_bar_node) self.bar_left = Braess_Bar(left_bar_node, center_bar_node) # Attach the bottom spring to the left end of the bar. self.bottom_spring.node_2 = left_bar_node left_bar_node.move_by_dxdy(Velocity((-step, 0))) right_bar_node.move_by_dxdy(Velocity((step, 0))) left_bar_node.set_target_by_dxdy(Velocity((-self.x_offset+step, Braess_World.cord_slack))) right_bar_node.set_target_by_dxdy(Velocity((self.x_offset-step, Braess_World.cord_slack))) # Attach the top cord to the right end of the bar rather than the center. self.top_cord.node_2 = right_bar_node # The left cord is a new element in state 2. It is offset to the left. x_coord = self.x cord_length = self.bottom_spring.node_1.y - Braess_World.top self.left_cord = Braess_Link.vertical_linked_nodes(Braess_Cord, x_coord, Braess_World.top, length=cord_length) # The left cord is offset to the left. self.left_cord.move_by_dxdy((-step, 0)) self.left_cord.node_1.set_target_by_dxdy(Velocity((-self.x_offset + step, 0))) self.left_cord.node_2.set_target_by_dxdy(Velocity((-self.x_offset + step, Braess_World.cord_slack))) self.left_cord.color = Color('yellow2') self.top_cord.color = Color('yellow2') # Make the left_cord's bottom node the top node of the bottom spring. World.agents.remove(self.bottom_spring.node_1) self.bottom_spring.node_1 = self.left_cord.node_2 # Add the new cord and bars to the adjustable links. self.adjustable_links.extend([self.left_cord, self.bar_right, self.bar_left]) Agent.key_step_done = False # ## Done with the setup for the animation. ## # gui_set(Braess_World.CUT_CORD, enabled=False) gui_set(GO_ONCE, enabled=True) gui_set(GOSTOP, enabled=True) if gui_get('Slow?'): SimEngine.fps = 15 if not gui_get('Pause?'): gui.WINDOW['GoStop'].click() Braess_World.state = 'a'