class InteractiveGame(object): """ Implements an interactive debugging game. This class implements an interactive game between the computer in the role of the environment and the user in the role of the system. In every time step, the environment first gives next inputs to the system. Then the user is asked to choose the next values of the output variables. The play is won for the user if she manages to fulfill all the fairness conditions of the system infinitely often or if the environment does not fulfill all its fairness constraints. The play is won for the environment if the user looses, so if the environment manages to find inputs to the system, so that the user is not able to fulfill all fairness conditions infinitely often while the environment is able to do so. If the specification is not realizable a strategy to determine the next inputs to the system so that the system loses every play exists. This strategy is called a 'counterstrategy'. In the game implemented in this class, this counterstrategy is used to find the inputs for the system. To make it easier for the user to find outputs of the system which are feasible according to the transition relation of the system, the possible values for all output variables are calculated. The computer will also print the index of the fairness constraint of the system it tries to evade. Thus the user can concentrate on fulfilling this constraint only. A summary of all input variables and all output variables in all steps is finally written into the file 'spec_debug_results/log.txt'. @author: Robert Koenighofer <*****@*****.**> @version: 1.0.0 """ def __init__(self, utils, counterstrategy, z_array, countertrace): """ Constructor @param utils: A module containing a lot of utility-functions as well as data which is needed almost everywhere. @type utils: L{SpecDebugUtils} @param counterstrategy: A counterstrategy, i.e., a strategy for the environment to find inputs so that the system is forced to violate the specification. @type counterstrategy: L{BDD} @param z_array: z_array[a] contains the intermediate results of the fixpoint computation in the variable Z of the computation of the winning region for the environment. 'a' counts the iterations of the fixpoint computation in Z. It is used to figure out how often the value of jx might still change in the future of the play. (If the play is in z_array[a], jx might change at most a-1 times.) @type z_array: list<L{BDD}> @param countertrace: A sequence of inputs so that the system is forced to violate its specification. @type countertrace: L{InfiniteTraceOfBdds} """ #: A module containing a lot of utility-functions. #: @type: L{SpecDebugUtils} self.__utils = utils #: A winning strategy for the environment. #: @type: L{BDD} self.__counterstrategy = counterstrategy #: Intermediate results of the fixpoint computation in the variable Z. #: @type: list<L{BDD}> self.__z_array = z_array #: A sequence of inputs so that the system is forced to violate its #: specification. #: @type: L{InfiniteTraceOfBdds} self.__countertrace = countertrace #: A utility class that allows us to print the summary of the play. #: @type: L{Writer} self.__writer = Writer(utils) def interact(self): """ Implements an interactive debugging game. This method implements an interactive game between the computer in the role of the environment and the user in the role of the system. In every time step, the environment first gives next inputs to the system. Then the user is asked to choose the next values of the output variables. The play is won for the user if she manages to fulfill all the fairness conditions of the system infinitely often or if the environment does not fulfill all its fairness constraints. The play is won for the environment if the user looses, so if the environment manages to find inputs to the system, so that the user is not able to fulfill all fairness conditions infinitely often while the environment is able to do so. The counterstrategy is used to find the inputs for the system. To make it easier for the user to find outputs of the system which are feasible according to the transition relation of the system, the possible values for all output variables are calculated. The computer will also print the index of the fairness constraint of the system it tries to evade. Thus the user can concentrate on fulfilling this constraint only. A summary of all input variables and all output variables in all steps is finally written into the file 'spec_debug_results/log.txt'. """ # The first current state is the initial state: current_state = self.__utils.init_ix_jx step_count = 0 while True: # We now print the current state: # (The method _print_current_state also logs the current state to # the file 'spec_debug_results/log.txt'.) self._print_current_state(current_state) #current_state.print_minterm() # a concrete next input is chosen either according to the # counterstrategy or according to the countertrace: next_input = self.__utils.get_next_inputs(current_state, \ step_count, \ self.__counterstrategy, \ self.__countertrace) # print the next inputs: self._print_next_inputs(next_input) # Choose the next jx variables (rho2 may allow more than one): for jx_var in self.__utils.jx_variables: (val, next_input) = self.__utils.choose_val( \ next_input, jx_var, True) # computing all possible next outputs: possible_next_outputs = next_input * self.__utils.sys_trans # we read the next output variables (entered by the user): (outvar_bdd, quit) = self._read_out_vars(possible_next_outputs) if quit: return next_state = possible_next_outputs * outvar_bdd # and assign the next state to current_state: next_state = self.__utils.swap_present_next(next_state) current_state = self.__utils.keep_only_present(next_state) step_count += 1 def _print_current_state(self, current_state): """ Prints the current state. This method prints the current state in the interactive game between the environment and the system to STDOUT. In addition to that, the state is written to the log file 'spec_debug_results/log.txt' with the help of the L{Writer}. @param current_state: The state to print. @type current_state: L{BDD} """ print "current state:" # print the input variables: for in_var in self.__utils.input_vars: symb = self.__utils.get_val_as_string(current_state, in_var, False) print "current " + in_var.name + " is: " + symb self.__writer.set_chosen_input(in_var.name, symb) # print the output variables: for out_var in self.__utils.relevant_out_vars: symb = self.__utils.get_val_as_string(current_state, out_var, False) print "current " + out_var.name + " is: " + symb self.__writer.set_chosen_output(out_var.name, symb); # print the ix-variables: ix_value = self.__utils.get_decimal_ix_val(current_state, False) if ix_value != None: print "The environment tries to fulfill fairness condition nr", print str(ix_value) + " next"; self.__writer.set_chosen_aux("ix", str(ix_value)) else: print "The environment has not decided yet which fairness", print "condition it will try to reach next" self.__writer.set_chosen_aux("ix","?") # print the jx-variables: nr_changes = self.__utils.how_often_may_jx_change(current_state, \ self.__z_array) self.__writer.set_chosen_aux("jx changes", str(nr_changes)) jx_value = self.__utils.get_decimal_jx_val(current_state, False); if jx_value != None: print "I try to keep the system from fulfilling fairness", print "condition nr: " + str(jx_value) print "I reserve the right to change my opinion on that at most", print str(nr_changes) + " times from now" self.__writer.set_chosen_aux("jx", str(jx_value)) else: print "I have not decided yet which fairness condition of the", print "system I will try to evade"; self.__writer.set_chosen_aux("jx","?") self.__writer.start_next_time_step() def _print_next_inputs(self, next_inputs): """ Prints all next inputs in a bdd. @param next_inputs: A bdd containing the next inputs. @type next_inputs: L{BDD} """ for in_var in self.__utils.input_vars: symb = self.__utils.get_val_as_string(next_inputs, in_var, True) print "next " + in_var.name + " is: " + symb def _read_out_vars(self, possible_outputs): """ Reads all next output values from STDIN. This method asks the user to enter the next values of the output variables of the system. It also resolves all restrictions on the variables (due to the transition relation of the system and previously entered variables) and displays possible values for the variable in brackets: - enter next hgrant0 (1): means only 1 is allowed for the next hgrant0 - enter next hgrant0 (0): means only 0 is allowed for the next hgrant0 - enter next hgrant0 (0,1): means that you can pick whatever you want The user is only allowed to enter '0', '1' or 'Q' if she wants to quit the game. @param possible_outputs: A bdd with all possible next outputs. @type possible_outputs: L{BDD} @return: A tuple (outvar_bdd, quit) where: - outvar_bdd is a bdd with all next output variables set to the values entered by the user - quit is True if the user wants to quit and False otherwise. @rtype: (L{BDD}, bool) """ # While the user enters variable values that are not allowed by # the transition relation of the system (if valid values are entered, # we jump out of the loop with a return): while True: all_outvars_bdd = BDD.ONE(self.__utils.dd_mgr) for out_var in self.__utils.relevant_out_vars: (out_bdd, quit) = self._read_single_out_var(out_var, \ all_outvars_bdd * possible_outputs) if quit: return (None, True) all_outvars_bdd *= out_bdd # check if the entered values are possible according to the # transition relation of the system (which is already part of # $possible_outputs) if (possible_outputs * all_outvars_bdd).isNotZero(): return (all_outvars_bdd, False) print "this is not possible according to the transition relation" print "try again" def _read_single_out_var(self, out_var, possible_next_outputs): """ Reads one single next output value from STDIN. This method asks the user to enter the next value of one single output variables of the system. It also resolves all restrictions on the variable (due to the transition relation of the system and previously entered variables) and displays possible values for the variable in brackets: - enter next hgrant0 (1): means only 1 is allowed for the next hgrant0 - enter next hgrant0 (0): means only 0 is allowed for the next hgrant0 - enter next hgrant0 (0,1): means that you can pick whatever you want The user is only allowed to enter '0', '1' or 'Q' if she wants to quit the game. @param out_var: The output variable to choose. @type out_var: L{Variable} @param possible_next_outputs: A bdd with all possible next outputs. @type possible_next_outputs: L{BDD} @return: A tuple (outvar_bdd, quit) where: - outvar_bdd is a bdd with all next output variables set to the values entered by the user - quit is True if the user wants to quit and False otherwise. @rtype: (L{BDD}, int) """ # while the user enteres invalid values (otherwise we jump out of # the loop with the return): while True: print "enter next " + out_var.name, self._print_possible_values_for(out_var, possible_next_outputs) input_line = sys.stdin.readline() if input_line == "Q\n" or input_line == "q\n": print "quit by user"; self.__writer.write_to_file("./spec_debug_results/log.txt") self.__writer.clear() return (None, True) if input_line == "1\n": return (out_var.ns, False) elif input_line == "0\n": return (~out_var.ns, False) print "enter '1' or '0' or 'Q' to quit" def _print_possible_values_for(self, out_var, restrictions): """ Prints all values which are possible for a certain output. This method prints all possible values for a certain output variable to STDOUT and also writes the values to a file 'spec_debug_results/log.txt'. Writing to the file is done with the L{Writer} which formats the output in a nice way. @param out_var: The variable to print the possible values for. @type out_var: L{Variable} @param restrictions: A bdd with all restrictions on the next output variables. @type restrictions: L{BDD} """ (can_be_1, can_be_0) = self.__utils.get_val(restrictions, out_var, True) if can_be_1 and can_be_0: sys.stdout.write(" (0,1): ") self.__writer.set_possibilities(out_var.name, "(0,1)") elif can_be_1: sys.stdout.write(" (1): ") self.__writer.set_possibilities(out_var.name, "( 1 )") elif can_be_0: sys.stdout.write(" (0): ") self.__writer.set_possibilities(out_var.name, "( 0 )") else: sys.stdout.write(" (): ") self.__writer.set_possibilities(out_var.name, "( )")