diff --git a/scripts/main.py b/scripts/main.py index 9e671e4c40f117edb2ba22e72fe06371ea1f3b7f..25a95d9e7bac3d742ebc69b24d80fbf90d8af659 100755 --- a/scripts/main.py +++ b/scripts/main.py @@ -4,6 +4,7 @@ This is the main class for running the entire game framework ''' #import modules and classes from log import Log +from cognitive_game import Game #import from libraries import enum import random @@ -12,133 +13,6 @@ import os #import from ros import rospy -from board_state.msg import TokenMsg -from board_state.msg import BoardMsg - -user_picked = (0, 0) -user_placed = (0, 0) - - -class Game(object): - def __init__(self, task_length, n_max_attempt_per_token, timeout): - rospy.init_node('big_hero', anonymous=True) - # subscriber for getting info from the board - rospy.Subscriber("/detected_move", TokenMsg, self.get_move_event_callback) - rospy.Subscriber("/board_status", BoardMsg, self.get_board_event_callback) - #get the objective of the exercise from the launch file - self.objective = rospy.get_param("/objective") - - #we need a sleep in order to give the system the time to get the board info - self.current_board = [] - rospy.sleep(2) - self.initial_board = self.get_board_event() - self.task_length = task_length - self.n_max_attempt_per_token = n_max_attempt_per_token - self.solution = self.set_objective(self.task_length) - self.timeout = timeout - self.robot_assistance = 0 - self.avg_robot_assistance_per_move = 0 - self.outcome = 0 - #counters - self.n_attempt_per_token = 1 - self.n_attempt_per_feedback = 0 - self.n_mistakes = 0 - self.n_correct_move = 0 - #subscriber variables from detect_move - self.detected_token = [] - self.picked = False - self.placed = False - self.moved_back = False - #logger - self.react_time_per_token_spec_t0 = 0.0 - self.react_time_per_token_gen_t0 = 0.0 - self.react_time_per_token_spec_t1 = 0.0 - self.react_time_per_token_gen_t1 = 0.0 - self.elapsed_time_per_token_gen_t0 = 0.0 - self.elapsed_time_per_token_spec_t0 = 0.0 - self.elapsed_time_per_token_gen_t1 = 0.0 - self.elapsed_time_per_token_spec_t1 = 0.0 - self.total_elapsed_time = 0.0 - self.move_info_gen = {'token_id':'', 'from':'', 'to':'', 'avg_robot_assistance_per_move':'', 'cum_react_time':'', 'cum_elapsed_time':'', 'attempt':''} - self.move_info_spec = {'token_id': '', 'from': '', 'to': '', 'robot_assistance': '', 'react_time': '', - 'elapsed_time': '', 'attempt': ''} - self.move_info_summary ={'attempt':'', 'avg_lev_assistance':'', 'react_time':'', 'elapsed_time':''} - self.move_info_gen_vect = list() - self.move_info_spec_vect = list() - self.move_info_summary_vect = list() - - def get_board_event_callback(self, msg): - '''callback from the topic board_status to get the status of the current_board''' - self.current_board = msg.data - - def get_board_event(self): - '''This method returns what is listened by the subscriber''' - return self.current_board - - def get_move_event_callback(self, msg): - '''callback from the topic detected_move to get the detected move if so''' - self.detected_token = msg.detected_token - self.picked = msg.picked - self.placed = msg.placed - self.moved_back = msg.moved_back - - def get_move_event(self): - '''This method just returns what is listened by the subscriber''' - return self.detected_token, self.picked, self.placed, self.moved_back - - def set_objective(self, n_token): - '''The method return a list with the tokens ordered based on the solution of the exercise''' - board_ = self.current_board[:] - #remove the empty cells from the current_board - board_filtered = list(filter(lambda x: x!="0", board_)) - if self.objective == "ascending": - self.solution = sorted(board_filtered)[:n_token] - elif self.objective == "descending": - self.solution = sorted(board_filtered, reverse=False)[:n_token] - else: - assert("Game is not defined contact the developer for integrating it") - return self.solution - - #methods to access the variables outside the class - def get_n_attempt_per_token(self): - return self.n_attempt_per_token - - def get_n_max_attempt(self): - return self.n_max_attempt - - def get_n_mistakes(self): - return self.n_mistakes - - def get_n_solution(self): - return self.n_solution - - def get_n_correct_move(self): - return self.n_correct_move - - def reset_attempt_per_token(self): - self.n_attempt_per_token = 0 - - def set_n_attempt_per_token(self, value): - self.n_attempt_per_token = value - - def set_n_mistakes(self, value): - self.n_mistakes = value - - def set_n_correct_move(self, value): - self.n_correct_move = value - - def set_n_solution(self, value): - self.n_solution = value - - def set_n_max_attempt_per_token(self, value): - self.n_max_attempt_per_token = value - - def add_info_gen_vect(self, dict): - self.move_info_gen_vect.append(dict.copy()) - - def add_info_spec_vect(self, dict): - self.move_info_spec_vect.append(dict.copy()) - class StateMachine(enum.Enum): @@ -171,8 +45,6 @@ class StateMachine(enum.Enum): CURRENT_STATE = 1 - - def robot_provide_assistance(self, game): ''' Robot action of assistance combining speech and gesture @@ -212,18 +84,22 @@ class StateMachine(enum.Enum): :return: ''' print("R_OUTCOME") - outcome = 0 - attempt = 0 + if game.moved_back: + print("token has been moved back, DO nothing") + game.outcome = "" + self.CURRENT_STATE = self.S_ROBOT_ASSIST + # check if the user reached his max number of attempts + if game.n_attempt_per_token >= game.n_max_attempt_per_token: + print("Max attempt reached") + self.S_ROBOT_MOVE_CORRECT_TOKEN = True + self.b_user_reached_max_attempt = True + self.robot_move_correct_token(game) + #get current move and check if it is the one expeceted in the solution list - if game.detected_token[0] == game.solution[game.n_correct_move] \ + elif game.detected_token[0] == game.solution[game.n_correct_move] \ and game.detected_token[2] == str(game.solution.index(game.detected_token[0])+1): #get the values from attempt and mistakes before resetting them - attempt = game.n_attempt_per_token game.outcome = 1 - game.n_correct_move += 1 - game.n_attempt_per_token = 1 - game.set_n_correct_move(game.n_correct_move) - game.set_n_attempt_per_token(game.n_attempt_per_token) print("correct_solution ", game.get_n_correct_move()) self.CURRENT_STATE = self.S_ROBOT_ASSIST elif game.detected_token[0] == []: @@ -257,12 +133,13 @@ class StateMachine(enum.Enum): #check if the user reached his max number of attempts if game.n_attempt_per_token>=game.n_max_attempt_per_token: + print("Max attempt reached") self.S_ROBOT_MOVE_CORRECT_TOKEN = True self.b_user_reached_max_attempt = True self.robot_move_correct_token(game) self.b_robot_outcome_finished = True - return self.b_robot_outcome_finished, attempt + return self.b_robot_outcome_finished def robot_move_back(self): #user moved the token in an incorrect location @@ -297,8 +174,13 @@ class StateMachine(enum.Enum): def user_action(self, game): '''user action state has been divided in three sub methods: 1. user pick a token + 1.a check for timeout + 1.b timeout -> provide reengagement + go back to 1 2. robot provides feedback - 3. user place a token + 3. user places a token + 3.a user places a token back + 3.b user places a token in a goal location ''' print("U_ACTION") @@ -315,18 +197,12 @@ class StateMachine(enum.Enum): else: return False - def placed_back_token(sm): - '''As timeout occuccedd when the user has the token in his hand, we - ask him to move it back''' - pass - - def user_pick_token(sm, game): game.react_time_per_token_spec_t0 = time.time() print("U_PICK") sm.CURRENT_STATE = sm.S_ROBOT_FEEDBACK detected_token, picked, _, _ = game.get_move_event() - while(not picked): + while(not picked and (detected_token==[])): if check_move_timeout(game): detected_token, picked, _, _ = game.get_move_event() else: @@ -340,8 +216,14 @@ class StateMachine(enum.Enum): sm.b_user_picked_token = True return sm.b_user_picked_token - def robot_provide_feedback(sm): + def robot_provide_feedback(sm, game): print("R_FEEDBACK") + #if the + game.n_sociable_per_token += 1 + game.n_tot_sociable += 1 + # if sm.b_user_picked_token and not game.detected_token[0] == game.solution[game.n_correct_move]: + # game.n_attempt_per_token += 1 + sm.CURRENT_STATE = sm.S_USER_PLACE sm.robot_provided_feeback_finished = True return sm.robot_provided_feeback_finished @@ -356,12 +238,12 @@ class StateMachine(enum.Enum): #check where the user will place the token def user_place_token_back(sm): print("U_PLACE_BACK") - sm.CURRENT_STATE = sm.S_USER_ACTION + sm.CURRENT_STATE = sm.S_ROBOT_OUTCOME sm.b_user_placed_token_back = True game.elapsed_time_per_token_spec_t1 = time.time()-game.elapsed_time_per_token_spec_t0 game.elapsed_time_per_token_gen_t1 += game.elapsed_time_per_token_spec_t1 game.n_mistakes += 1 - game.n_attempt_per_feedback += 1 + game.n_attempt_per_token += 1 game.set_n_attempt_per_token(game.n_attempt_per_token) game.set_n_mistakes(game.n_mistakes) return sm.b_user_placed_token_back @@ -380,21 +262,30 @@ class StateMachine(enum.Enum): while (not placed): detected_token, _, placed, moved_back = game.get_move_event() if (placed and moved_back): - user_place_token_back(sm) - self.CURRENT_STATE = sm.S_USER_ACTION + self.CURRENT_STATE = sm.S_ROBOT_OUTCOME + return user_place_token_back(sm) elif (placed and not moved_back): - user_place_token_sol(sm) self.CURRENT_STATE = sm.S_ROBOT_OUTCOME + return user_place_token_sol(sm) else: assert "Unexpected state" self.CURRENT_STATE = self.S_USER_ACTION + #if the user picks a token and SOCIABLE is active if user_pick_token(self, game): - if robot_provide_feedback(self): + if game.with_SOCIABLE: + if robot_provide_feedback(self, game): + if user_place(self, game): + return True + else: + print("Something went wrong with user place in SOCIABLE") + else: + print("Something went wrong with robot feedback") + else: if user_place(self, game): return True - else: - self.CURRENT_STATE = self.S_USER_PICK_TOKEN + else: + print("Something went wrong with user place") else: self.CURRENT_STATE = self.S_ROBOT_ASSIST @@ -416,22 +307,19 @@ class StateMachine(enum.Enum): # Execute the function return func() - - - def main(): game = Game(task_length=5, n_max_attempt_per_token=4, timeout=15) - input = raw_input("please, insert the id of the user:") + user_id = raw_input("please, insert the id of the user:") path = os.path.abspath(__file__) dir_path = os.path.dirname(path) parent_dir_of_file = os.path.dirname(dir_path) - path_name = parent_dir_of_file + "/log/" + input - print(path_name) + path_name = parent_dir_of_file + "/log/" + user_id + if not os.path.exists(path_name): os.makedirs(path_name) else: - input = raw_input("The folder already exists, please remove it or create a new one:") - path_name = os.getcwd() + "/log/" + input + user_id = raw_input("The folder already exists, please remove it or create a new one:") + path_name = os.getcwd() + "/log/" + user_id if not os.path.exists(path_name): os.makedirs(path_name) @@ -447,6 +335,7 @@ def main(): game.move_info_spec['react_time'] = "react_time" game.move_info_spec['elapsed_time'] = "elapsed_time" game.move_info_spec['attempt'] = "attempt" + game.move_info_spec['sociable'] = "sociable" game.move_info_spec_vect.append(game.move_info_spec) log_spec.add_row_entry(game.move_info_spec) @@ -458,11 +347,13 @@ def main(): game.move_info_gen['cum_react_time'] = "cum_react_time" game.move_info_gen['cum_elapsed_time'] = "cum_elapsed_time" game.move_info_gen['attempt'] = "attempt" + game.move_info_gen['sociable'] = "sociable" game.move_info_gen_vect.append(game.move_info_gen) log_gen.add_row_entry(game.move_info_gen) log_summary = Log(file_summary) game.move_info_summary["attempt"] = "n_attempt" + game.move_info_summary["sociable"] = "n_sociable" game.move_info_summary["avg_lev_assistance"] = "lev_assistance" game.move_info_summary["react_time"] = "react_time" game.move_info_summary["elapsed_time"] = "elapsed_time" @@ -470,62 +361,34 @@ def main(): sm = StateMachine(1) while game.get_n_correct_move()<game.task_length: + if sm.CURRENT_STATE.value == sm.S_ROBOT_ASSIST.value: sm.robot_provide_assistance(game) game.avg_robot_assistance_per_move += game.robot_assistance + elif sm.CURRENT_STATE.value == sm.S_USER_ACTION.value: print("Expected token ", game.solution[game.get_n_correct_move()]) time_to_act = time.time() sm.user_action(game) - if game.detected_token: - print("Moved token ", game.detected_token[0]) - game.move_info_spec['token_id'] = game.detected_token[0] - game.move_info_spec['from'] = game.detected_token[1] - game.move_info_spec['to'] = game.detected_token[2] - game.move_info_spec['robot_assistance'] = game.robot_assistance - game.move_info_spec['react_time'] = round(game.react_time_per_token_spec_t1, 3) - game.move_info_spec['elapsed_time'] = round(game.elapsed_time_per_token_spec_t1, 3) - game.move_info_spec['attempt'] = game.n_attempt_per_token - game.total_elapsed_time += time.time()-time_to_act - game.add_info_spec_vect(game.move_info_spec) - log_spec.add_row_entry(game.move_info_spec) + game.total_elapsed_time += time.time() - time_to_act elif sm.CURRENT_STATE.value == sm.S_ROBOT_OUTCOME.value: #these are reported only because the variables are already reset when a correct move occurred - done, attempt = sm.robot_provide_outcome(game) + sm.robot_provide_outcome(game) + info = game.store_info_spec() + log_spec.add_row_entry(info) #{'token_id':'', 'from':'', 'to':'', 'robot_assistance':'', 'react_time':'', 'elapsed_time':'', 'attempt':''} + #if you have done the correct move or you reach the maximum number of attempts then store the data if (game.outcome == 1 or (game.outcome==0 and game.n_attempt_per_token==game.n_max_attempt_per_token) or (game.outcome==-1 and game.n_attempt_per_token==game.n_max_attempt_per_token)): - game.move_info_gen['token_id'] = game.detected_token[0] - game.move_info_gen['from'] = game.detected_token[1] - game.move_info_gen['to'] = game.detected_token[2] - game.move_info_gen['avg_robot_assistance_per_move'] = (game.avg_robot_assistance_per_move/attempt) - game.move_info_gen['cum_react_time'] = round(game.react_time_per_token_gen_t1, 3) - game.move_info_gen['cum_elapsed_time'] = round(game.elapsed_time_per_token_gen_t1, 3) - game.move_info_gen['attempt'] = attempt - game.add_info_gen_vect(game.move_info_gen) - log_gen.add_row_entry(game.move_info_gen) - game.react_time_per_token_spec_t1 = 0 - game.react_time_per_token_gen_t1 = 0 - game.react_time_per_token_spec_t0 = 0 - game.react_time_per_token_gen_t0 = 0 - game.elapsed_time_per_token_spec_t1 = 0 - game.elapsed_time_per_token_gen_t1 = 0 - game.elapsed_time_per_token_spec_t0 = 0 - game.elapsed_time_per_token_gen_t0 = 0 - game.avg_robot_assistance_per_move = 0 + log = game.store_info_gen() + log_gen.add_row_entry(log) + game.reset_counters() + game.reset_detected_token() - game.move_info_summary["attempt"] = "n_attempt" - game.move_info_summary["avg_lev_assistance"] = "lev_assistance" - game.move_info_summary["react_time"] = "react_time" - game.move_info_summary["elapsed_time"] = "elapsed_time" - - game.move_info_summary["attempt"] = sum([elem['attempt'] for elem in game.move_info_gen_vect]) - game.move_info_summary["avg_lev_assistance"] = sum([elem['avg_robot_assistance_per_move'] for elem in game.move_info_gen_vect])/game.task_length - game.move_info_summary["react_time"] = sum([elem['cum_react_time'] for elem in game.move_info_gen_vect]) - game.move_info_summary["elapsed_time"] = sum([elem['cum_elapsed_time'] for elem in game.move_info_gen_vect]) - log_summary.add_row_entry(game.move_info_summary) + log = game.store_info_summary() + log_summary.add_row_entry(log) for instance_spec in game.move_info_spec_vect: print(instance_spec)