/** * @brief Update the episodic memories for a given ape. * This is based upon a fading memory model in which older memories * are replaced by newer ones. Each memory has an associated affect * value indicating its emotional impact, and this fades over time. * * The rate of fading is genetically regulated, with different rates * for memories with positive and negative affect. * This facilitates optimistic/pessimistic and forgetful/memorable * type personalities. * * The fading memory model may not be strictly realistic, and might * be replaced by something else in future. * @param local_being pointer to the ape */ void episodic_cycle_no_sim(noble_being * local_being, void * data) { if (local_being->delta.awake == 0) return; { n_int i; noble_episodic * local_episodic = being_episodic(local_being); n_genetics * genetics = being_genetics(local_being); if (!local_episodic) return; for (i=0; i<EPISODIC_SIZE; i++) { if (local_episodic[i].event == 0) continue; /** remove intentions which are outdated */ if (local_episodic[i].event >= EVENT_INTENTION) { /** is this my intention, or someone else's? */ if (being_name_comparison(local_being, local_episodic[i].first_name[BEING_MEETER], local_episodic[i].family_name[BEING_MEETER])) { if (spacetime_before_now(&local_episodic[i].space_time)) { local_episodic[i].event = 0; continue; } } episodic_intention_update(local_being, i); } /** fade towards EPISODIC_AFFECT_ZERO */ if (local_episodic[i].affect < EPISODIC_AFFECT_ZERO) { /** negative memories fade */ if (EPISODIC_AFFECT_ZERO - local_episodic[i].affect > 16) { local_episodic[i].affect+=(1+GENE_NEGATIVE_AFFECT_FADE(genetics)); } else { local_episodic[i].affect++; } } else { if (local_episodic[i].affect > EPISODIC_AFFECT_ZERO) { /** positive memories fade */ if (local_episodic[i].affect - EPISODIC_AFFECT_ZERO > 16) { local_episodic[i].affect-=(1+GENE_POSITIVE_AFFECT_FADE(genetics)); } else { local_episodic[i].affect--; } } } } } }
static void transfer_being(n_file * tranfer_out, noble_simulation * value, n_int being, noble_file_entry * format) { #ifdef USE_FIL_SOE n_int loop = (SOCIAL_SIZE * being); n_int loop_end = loop + SOCIAL_SIZE; #endif #ifdef USE_FIL_EPI n_int loop_episodic = (EPISODIC_SIZE * being); n_int loop_episodic_end = loop + EPISODIC_SIZE; #endif #ifdef USE_FIL_BEI io_write_buff(tranfer_out, &(value->beings[being]), format, FIL_BEI, 0L); #endif #ifdef USE_FIL_SOE while (loop < loop_end) { io_write_buff(tranfer_out, being_social(&(value->beings[being])), format, FIL_SOE, &brain_three_byte_command); loop++; } #endif #ifdef USE_FIL_EPI while (loop_episodic < loop_episodic_end) { io_write_buff(tranfer_out, being_episodic(&(value->beings[being])), format, FIL_EPI, 0L); loop_episodic++; } #endif }
/** * @brief If the given episodic memory is an intention, as defined by the event type, * then update the learned preferences based upon the intention type. * For example, if the ape intends to chat then the chatting preference * may be increased which makes chatting more likely. * @param local pointer to the particular ape * @param episode_index array index of the episodic memory representing the intention */ static void episodic_intention_update(noble_being * local, n_int episode_index) { noble_episodic * local_episodic = being_episodic(local); n_byte event; n_int learned_preference_index=-1; if (local_episodic == 0L) { return; } event = local_episodic[episode_index].event - EVENT_INTENTION; switch(event) { case EVENT_CHAT: { learned_preference_index = PREFERENCE_CHAT; break; } case EVENT_GROOM: { if ((local_episodic[episode_index].arg&2)!=0) { learned_preference_index = PREFERENCE_GROOM_MALE; } else { learned_preference_index = PREFERENCE_GROOM_FEMALE; } break; } } /** alter preferences */ if (learned_preference_index>-1) { if ((local_episodic[episode_index].arg&1)!=0) { if (local->changes.learned_preference[learned_preference_index]<255) { local->changes.learned_preference[learned_preference_index]++; } } else { if (local->changes.learned_preference[learned_preference_index]>0) { local->changes.learned_preference[learned_preference_index]--; } } } }
/** * @brief This returns the percentage of episodic memories or intentions which are first person. * Some memories originate from the self and others are acquired from others via chatting. * @param local_sim pointer to the simulation * @param local pointer to the ape * @param intention 0=episodic memories, 1=intentions * @return percentage in the range 0-100 */ n_int episodic_first_person_memories_percent( noble_being * local, n_byte intention) { n_int i,hits=0,memories=0; noble_episodic * local_episodic = being_episodic(local); if (local_episodic == 0L) { return 0; } /** examine all memories */ for (i=0; i<EPISODIC_SIZE; i++) { if (local_episodic[i].event>0) { if (intention!=0) { /** ratio of intentions to other memories */ if (local_episodic[i].event >= EVENT_INTENTION) { hits++; } } else { /** ratio of first person memories to other memories */ if (being_name_comparison(local, local_episodic[i].first_name[BEING_MEETER], local_episodic[i].family_name[BEING_MEETER])) { hits++; } } memories++; } } if (memories>0) { return hits*100/memories; } else { if (intention!=0) { return 0; } return 100; } }
/** * @brief Returns a celebrity factor based upon how many apes within * the episodic memory of the given ape have a similar name to the * met ape, and their friend or foe values. * This means that initial beliefs about other apes are partly * a form of stereotyping * @param local_sim pointer to the simulation * @param meeter_being pointer to the ape * @param met_being pointer to another ape * @return celebrity value of the met ape */ n_int episodic_met_being_celebrity( noble_being * meeter_being, noble_being * met_being) { n_int i,j,celebrity=0,ctr,aff; noble_episodic * meeter_episodic = being_episodic(meeter_being); n_byte2 first_name = being_gender_name(met_being); n_byte2 family_name = being_family_name(met_being); if (!meeter_episodic) return 0; /** check all episodic memories of the meeter */ for (i=0; i<EPISODIC_SIZE; i++) { aff = (n_int)(meeter_episodic[i].affect) - EPISODIC_AFFECT_ZERO; if (aff>1) aff=1; if (aff<-1) aff=-1; /** check both the meeter and the met ape for each memory */ for (j=BEING_MEETER; j<=BEING_MET; j++) { ctr=0; /** same first name */ if (meeter_episodic[i].first_name[j]==first_name) { celebrity+=aff; ctr++; } /** same family name */ if (meeter_episodic[i].family_name[j]==family_name) { celebrity+=aff; ctr++; } /** if both first name and family name match then increase the celebrity value further */ if (ctr==2) { celebrity+=aff*2; } } } /** limit within range */ if (celebrity>16) celebrity=16; if (celebrity<-16) celebrity=-16; return celebrity; }
n_int tranfer_in(n_file * input_file) { n_int ret_val; n_byte *temp_store = 0L; n_uint ape_count = 0; n_uint social_count = 0; n_uint episodic_count = 0; noble_simulation * local_sim = sim_sim(); n_uint size_buffer = io_find_size_data((noble_file_entry *)noble_file_format); temp_store = (n_byte *)memory_new(size_buffer); if (temp_store == 0L) { return SHOW_ERROR("No temporary storage memory available"); } io_whitespace(input_file); input_file->location = 0; ret_val = io_read_buff(input_file, temp_store, noble_file_format); if(ret_val != FIL_VER) /* signature must be first */ return SHOW_ERROR("Signature not first in file"); { n_byte2 *signature = (n_byte2 *)temp_store; if(signature[0] != NOBLE_APE_SIGNATURE) /* not a Noble Ape file */ return SHOW_ERROR("Not a Noble Ape File"); if(signature[1] > VERSION_NUMBER) /* file version greater than this version */ return SHOW_ERROR("File newer than Simulation"); } do { n_byte *temp = 0L; ret_val = io_read_buff(input_file, temp_store, noble_file_format); if (ret_val == -1) SHOW_ERROR("Failure in file load"); if (ret_val < FILE_EOF) { n_uint loop_end = 0; switch (ret_val) { case FIL_LAN: temp = (n_byte*)land_ptr(); loop_end = 11; /* Needs to be fixed */ break; case FIL_BEI: temp = (n_byte*) &(local_sim->beings[ape_count]); loop_end = sizeof(noble_being); break; case FIL_SOE: { noble_social * local_social = being_social(&(local_sim->beings[ape_count])); temp = (n_byte*)(&local_social[social_count]); loop_end = sizeof(noble_social); } break; case FIL_EPI: { noble_episodic * local_episodic = being_episodic(&(local_sim->beings[ape_count])); temp = (n_byte*)(&local_episodic[episodic_count]); loop_end = sizeof(noble_episodic); } break; default: { return SHOW_ERROR("Unknown file kind"); /*unknown kind*/ } break; } if(temp != 0L) { memory_copy(temp_store, temp, loop_end); } if (ret_val == FIL_BEI) { ape_count ++; if (ape_count == local_sim->max) { local_sim->num = ape_count; return SHOW_ERROR("Too many apes for memory"); } } if (ret_val == FIL_SOE) { social_count ++; if (social_count == (local_sim->max * SOCIAL_SIZE)) { local_sim->num = ape_count; return SHOW_ERROR("Too many social graph events for memory"); } } if (ret_val == FIL_EPI) { episodic_count ++; if (episodic_count == (local_sim->max * EPISODIC_SIZE)) { local_sim->num = ape_count; return SHOW_ERROR("Too many episodic events for memory"); } } } } while (ret_val < FILE_EOF); if (ret_val == FILE_EOF) { local_sim->num = ape_count; return 0; } return SHOW_ERROR("Process file failed"); }
/** * @brief Copy an episodic memory (an anecdote) from one ape to another during chat. * @param local_sim Pointer to the simulation * @param local Pointer to the ape conveying the anecdote * @param other Pointer to the ape to which the anecdote will be copied * @return Returns 1 if the copy was successful, 0 otherwise */ n_byte episodic_anecdote( noble_being * local, noble_being * other) { noble_episodic * local_episodic = being_episodic(local); noble_episodic * other_episodic = being_episodic(other); n_int affect; n_byte event; n_int replace,mult=1; if (local_episodic == 0L || other_episodic == 0L || local == other) { return 0; } affect = (n_int)(local_episodic[being_attention(local,ATTENTION_EPISODE)].affect)-EPISODIC_AFFECT_ZERO; event = local_episodic[being_attention(local,ATTENTION_EPISODE)].event; /** both protagonists must be awake */ if ((event==0) || (local->delta.awake == FULLY_ASLEEP) || (other->delta.awake == FULLY_ASLEEP)) { return 0; } if (local->delta.awake != FULLY_AWAKE) { /** more likely to make errors while drowsy */ mult=2; } /** mutate with some probability */ if (being_random(local) < (ANECDOTE_EVENT_MUTATION_RATE+ (local->changes.learned_preference[PREFERENCE_ANECDOTE_EVENT_MUTATION])*100)*mult) { event = (n_byte)(being_random(local) % EVENTS); } if (being_random(local) < (ANECDOTE_AFFECT_MUTATION_RATE+ (local->changes.learned_preference[PREFERENCE_ANECDOTE_AFFECT_MUTATION])*100)*mult) { /** affect gets exaggerated or downplayed */ affect = (affect * (64 + (n_int)(being_random(local) & 127))) / 128; /** keep affect within range */ if (affect<-32000) affect=-32000; if (affect>32000) affect=32000; } /** find an index within the other episodic memory in which to insert */ replace = noble_episodic_replace_index( event,affect, local_episodic[being_attention(local,ATTENTION_EPISODE)].first_name[BEING_MEETER], local_episodic[being_attention(local,ATTENTION_EPISODE)].family_name[BEING_MEETER], local_episodic[being_attention(local,ATTENTION_EPISODE)].first_name[BEING_MET], local_episodic[being_attention(local,ATTENTION_EPISODE)].family_name[BEING_MET], local); if (replace==-1) return 0; other_episodic[replace] = local_episodic[being_attention(local,ATTENTION_EPISODE)]; other_episodic[replace].event = event; other_episodic[replace].affect = (n_byte2)(affect+EPISODIC_AFFECT_ZERO); /** other ape pays attention to the incoming anecdote */ being_set_attention(local, ATTENTION_EPISODE, replace); return 1; }
/** * @brief Generate an intention. * Note that intentions are stored together with episodic memories, * with the event type making the difference between a memory about * the past and an intention about the future. * @param local_sim Pointer to the simulation * @param local Pointer to the ape * @param episode_index Episodic memory array index to use. * @param mins_ahead The number of minutes into the future for which the intention will last. * @param args Any additional arguments * @return Returns 1 if the update was successful, or 0 otherwise. */ n_byte episodic_intention( noble_being * local, n_int episode_index, n_byte2 mins_ahead, n_byte args) { n_byte4 date; n_byte4 time; n_int replace; n_byte event; noble_episodic * local_episodic = being_episodic(local); if (local_episodic == 0L) { return 0; } event = local_episodic[episode_index].event; if (event==0) return 0; time = land_time(); date = local_episodic[episode_index].space_time.date; if (time >= TIME_DAY_MINUTES) { /** increment date by one day */ time %= TIME_DAY_MINUTES; date++; } if (event >= EVENT_INTENTION) { /** extend the time of an existing intention */ local_episodic[episode_index].space_time.time = time; local_episodic[episode_index].space_time.date = date; local_episodic[episode_index].arg = args; /** if this was someone else's intention it now becomes yours */ local_episodic[episode_index].first_name[BEING_MEETER] = being_gender_name(local); local_episodic[episode_index].family_name[BEING_MEETER] = being_family_name(local); return 1; } /** only certain types of events become intentions */ if (!((event==EVENT_GROOM) || (event==EVENT_CHAT))) { return 0; } /** find a memory index to replace */ replace = noble_episodic_replace_index( EVENT_INTENTION + event, (n_int)(local_episodic[episode_index].affect)-EPISODIC_AFFECT_ZERO, being_gender_name(local), being_family_name(local), local_episodic[episode_index].first_name[BEING_MET], local_episodic[episode_index].family_name[BEING_MET], local); if (replace == -1) { return 0; } if (replace == episode_index) { return 0; } memory_copy((n_byte*)&local_episodic[episode_index], (n_byte*)&local_episodic[replace], sizeof(noble_episodic)); local_episodic[replace].event = EVENT_INTENTION + event; local_episodic[replace].space_time.time = time; local_episodic[replace].space_time.date = date; local_episodic[replace].first_name[BEING_MEETER] = being_gender_name(local); local_episodic[replace].family_name[BEING_MEETER] = being_family_name(local); local_episodic[replace].arg = args; return 1; }
/** * @brief Stores an episodic memory * @param local Pointer to the being * @param event The type of event * @param affect Affect value associated with the event * @param local_sim Pointer to the simulation * @param name1 First name of a being participating in the event * @param family1 Family names of a being participating in the event * @param name2 First name of a second being participating in the event * @param family2 Family names of a second being participating in the event * @param arg Any additional argument * @param food Type of food */ static void episodic_store_full( noble_being * local, being_episodic_event_type event, n_int affect, n_byte2 name1, n_byte2 family1, n_byte2 name2, n_byte2 family2, n_byte2 arg, n_byte food) { noble_episodic * local_episodic = being_episodic(local); n_int replace; n_byte old_event; n_byte4 old_time; n_byte4 new_time; if (local_episodic == 0L) { return; } if (local->delta.awake == FULLY_ASLEEP) return; replace = noble_episodic_replace_index(event,affect,name1,family1,name2,family2,local); if (replace == -1) return; old_event = local_episodic[replace].event; old_time = local_episodic[replace].space_time.time; /** insert the current event into the episodic memory */ local_episodic[replace].event = event; local_episodic[replace].affect = (n_byte2)(affect+EPISODIC_AFFECT_ZERO); spacetime_set(&local_episodic[replace].space_time, being_location(local)); new_time = local_episodic[replace].space_time.time; local_episodic[replace].first_name[BEING_MEETER]=name1; local_episodic[replace].family_name[BEING_MEETER]=family1; local_episodic[replace].first_name[BEING_MET]=name2; local_episodic[replace].family_name[BEING_MET]=family2; local_episodic[replace].food=food; local_episodic[replace].arg=arg; if ((event == 0) || (event>=EVENTS)) { (void)SHOW_ERROR("Event outside scope"); } if (local_logging) { if ((old_event != event) /*|| ((old_time+10) < (new_time))*/) /**< TODO this may need to be changed */ { n_string_block description = {0}; n_string_block str = {0}; n_string_block time = {0}; n_string_block combination = {0}; n_int social_event; social_event = episode_description(local, replace, description); if ((local_social == 1) && (social_event == 0)) { return; } being_name_simple(local, str); io_time_to_string(time); io_three_string_combination(combination, time, str, description, 35); (*local_logging)(combination); } } }
/** * @brief Returns the index of the the episodic memory which can be overwritten with a new one. * @param event The type of event * @param affect The affect value associated with the event * @param name1 Name of the first ape in the memory (meeter) * @param family1 Family name of the first ape in the memory (meeter) * @param name2 Name of the second ape in the memory (met) * @param family2 Family name of the second ape in the memory (met) * @param local Pointer to the ape * @param local_sim Pointer to the simulation * @return array index of the episodic memory which can be replaced. */ static n_int noble_episodic_replace_index( being_episodic_event_type event, n_int affect, n_byte2 name1, n_byte2 family1, n_byte2 name2, n_byte2 family2, noble_being * local) { /** absolute affect value */ n_int abs_aff = affect; n_int i; n_int replace=-1; n_int min; n_byte event_exists=0; noble_episodic * local_episodic = being_episodic(local); if (!local_episodic) return -1; /** replace only events with an affect lower then the current */ abs_aff = ABS(abs_aff); min = abs_aff; for (i=0; i<EPISODIC_SIZE; i++) { /** is this the same type of event */ if (local_episodic[i].event == event) { /** is this the same being? */ if ((local_episodic[i].first_name[BEING_MEETER]==name1) && (local_episodic[i].family_name[BEING_MEETER]==family1)) { /** get absolute affect value */ n_int aff1 = ABS((n_int)(local_episodic[i].affect)-EPISODIC_AFFECT_ZERO); /** does this have the least affect (most forgettable) */ event_exists = 1; if (aff1 <= min) { min = aff1; replace = i; } } } } if (event_exists==0) { /** Use any empty memory slots */ for (i=0; i<EPISODIC_SIZE; i++) { if (local_episodic[i].event == 0) { return i; } } /** no event of this type was found, so search for any event with the lowest affect */ min = abs_aff; for (i=0; i<EPISODIC_SIZE; i++) { /** get absolute affect value */ n_int aff1 = ABS((n_int)(local_episodic[i].affect)-EPISODIC_AFFECT_ZERO); /** does this have the least affect (most forgettable) */ if (aff1 < min) { min = aff1; replace = i; } } } return replace; }
/** * @brief Updates the sex drive * @param local Pointer to the ape * @param awake whether the ape is awake * @param local_sim Pointer to the simulation */ static void drives_sex( noble_being * local, n_int awake, noble_simulation * local_sim) { n_int i,max; noble_social * local_social_graph = being_social(local); n_int age_in_days = AGE_IN_DAYS(local); #ifdef EPISODIC_ON noble_episodic * local_episodic = being_episodic(local); #endif /** is the being mature */ if (age_in_days > AGE_OF_MATURITY) { /** is the being awake and its sex drive not saturated */ if (awake) { /** increase the sex drive */ being_inc_drive(local, DRIVE_SEX); /** if sex drive is above a mate seeking threshold and the being has no current goal */ if ((being_drive(local, DRIVE_SEX) > THRESHOLD_SEEK_MATE) && being_check_goal(local, GOAL_NONE)) { /** either search for a preferred mate, or mate randomly */ if (GENE_MATE_SEEK(being_genetics(local))&1) { /** look for a mate */ #ifdef EPISODIC_ON if (!local_episodic) return; /** does the being remember mating in the recent past */ for(i=0; i<EPISODIC_SIZE; i++) { if (local_episodic[i].event == EVENT_MATE) { /** not someone else's mate */ if (being_name_comparison(local, local_episodic[i].first_name[BEING_MEETER], local_episodic[i].family_name[BEING_MEETER])) { /** set a goal to seek the remembered mate */ being_set_goal_mate(local, local_episodic[i].first_name[BEING_MET], local_episodic[i].family_name[BEING_MET]); /** remember seeking a mate */ episodic_store_memory( local_sim, local, EVENT_SEEK_MATE, AFFECT_SEEK_MATE, being_gender_name(local), being_family_name(local), local->delta.goal[1], local->delta.goal[2],0); break; } } } #endif /** if the being is not seeking a remembered mate then examine the social graph for attractive prospects */ if (being_check_goal(local, GOAL_MATE) == 0) { max = 0; if (!local_social_graph) return; for(i=1; i<SOCIAL_SIZE_BEINGS; i++) { if (!SOCIAL_GRAPH_ENTRY_EMPTY(local_social_graph,i)) { if ((local_social_graph[i].attraction) > max) { /** who are we most attracted to? */ max=local_social_graph[i].attraction; being_set_goal_mate(local, local_social_graph[i].first_name[BEING_MET], local_social_graph[i].family_name[BEING_MET]); } } } /** if an attractive mate was found then remember this event */ if (being_check_goal(local, GOAL_MATE)) { episodic_store_memory( local_sim, local, EVENT_SEEK_MATE, AFFECT_SEEK_MATE, being_gender_name(local), being_family_name(local), local->delta.goal[1], local->delta.goal[2],0); } } } } /** during gestation reduce the sex drive */ if (being_pregnant(local) != 0) { if (being_drive(local, DRIVE_SEX) >= GESTATION_SEX_DRIVE_DECREMENT) { being_dec_drive(local, DRIVE_SEX); } } } else { /** while sleeping reduce sex drive */ being_dec_drive(local, DRIVE_SEX); } /** if sex drive falls below the mate seeking threshold and the being is seeking a mate, then stop seeking a mate */ if ((being_drive(local, DRIVE_SEX) < THRESHOLD_SEEK_MATE) && being_check_goal(local, GOAL_MATE)) { being_set_goal_none(local); } } }