####################
# MODEL PARAMETERS #
####################

# Sets the size of the team; stored in the variable "n_members"
n_members <- 5

# Sets the size of the possible knowledge pool; stored in the variable "n_info"
# Value represents the total number of relevant unique pieces of knowledge
# that could be known
n_info <- 30

###################
# MODEL FUNCTIONS #
###################
# Function to determine whether an agent attends to a piece of information shared by another agent
# Function is named "attend_info" and requires two user-provided inputs to run:
# (1) "rcvr": single integer between 1:n_members identifying which agent is attempting to attend to the shared information during this attempt
# (2) "spkr": single integer between 1:n_members identifying which agent is attempting to share the information during this attempt
# (3) "lrnr_cons": value between 0:1 representing the conscientiousness level of the learner agent
# Output of the function is a single Boolean value (TRUE/FALSE) indicating whether agent attended to the information
attend_info <- function(rcvr, spkr, lrnr_cons) {

  # Checks to see if the agent is NOT the speaker since the speaker cannot learn
  # If the speaker is NOT the agent specified as "rcvr" then the following code is executed
  if(rcvr != spkr) {

    # Sets the two possible outcomes for whether an agent attends to a speaker (T) or does not attend (F)
    att_outcomes <- c(TRUE, FALSE)

    # Sets the probability ("att_probs") of whether an agent attends to a speaker (lrnr_cons) or not to attend (1 - lrnr_cons)
    att_probs <- c(lrnr_cons, (1 - lrnr_cons))

    # Uses the sample function to randomly determine whether the agent attends to the speaker using the designated probabilities
    att <- sample(att_outcomes, size = 1, prob = att_probs)

  # If the speaker is the same as the agent specified as "rcvr" then the following code is executed
  } else {

    # Because speakers do not attempt to learn the information they share, set their attend value to FALSE
    att <- FALSE
  }

  # Identifies what is to be output from the "attend" function.
  # In this case, a single TRUE/FALSE value is returned indicating whether the agent chose to attend to the shared information during this attempt
  return(att)

#	Ends the user-defined function, "attend" to execute the attending to information action/event
}

# Function to determine how much knowledge an agent learns about a shared piece of information during a learning attempt
# Function is named "learn" and requires three user-provided inputs to run:
# (1) "info": integer value between 1:n_info identifying which piece of knowledge is the focus of learning during the learning attempt
# (2) "lrnr_know": vector of length n_info that indicates what the agents attempting to learn already knows (i.e., know_pool[lrnr,])
# (3) "lrnr_cog": value between 0:1 representing the cognitive ability level of the learner agent
# Output of the function is a vector of length n_info containing what the agent knows after this learning attempt
learn <- function(info, lrnr_know, lrnr_cog) {

  # Update agent's knowledge pool based on learning
  # Add how much the agent is able to learn about the information ("lrn_cog") to how much is already know about the information (contained in "lrnr_know")
  lrnr_know[info] <- lrnr_know[info] + lrnr_cog

  # Sets any values in the agent's knowledge pool greater than 1 to the maximum allowable value (1)
  # This can happen if the agent's current learning attempt pushes its knowledge of the information pieced being learned over the maximum allowable value.
  lrnr_know[lrnr_know > 1] <- 1

  # Identifies what is to be output from the "learn" function
  # In this case, the agent's entire knowledge pool ("lrnr_know") is returned.
  # If the agent learned, this object includes updates to the agent's knowledge pool based on what it learned during this attempt
  # If the agent did not learn, this object simply returns what the agent's knowledge pool looked like prior to the attempt
  return(lrnr_know)

#	Ends the user-defined function "learn" to execute the learning action/event
}

##################
# INITIALIZATION #
##################
# Creates a vector of agent names called "agents"
# Will name agents 1,2,3, ... n_members
agents <- 1:n_members

### PSEUDOCODE STEP 1 ###
### Assign the extraversion, conscientiousness, and cognitive ability of agents ###

# Extraversion / speaking rates of agents (scaled within the team):
## Select random values for each agent (n_members) using a uniform distribution (runif)
## The values (.3, .7) indicate that an agent will be between 30% (.3) and 70% (.7)
## likely to want to speak at any given time
## Result will be a vector ("ext"), containing all agents' raw extraversion scores
ext <- runif(n_members, min = .3, max = .7)

## Convert extraversion to a relative value scaled within-team
## sum(ext) calculates the total amount of extraversion in the team
## Result will divide each agents' raw extraversion level (stored in "ext") by the team total
ext <- ext / sum(ext)

# Conscientiousness / attention probability of agents:
## Select random value for each agent ("n_members") using a uniform distribution ("runif")
## The chosen min and max values indicate that an agent will be between 30% (".3") and 100% ("1")
## likely to attend to a speaker at any given time
## Result will be a vector ("cons"), containing all agents' raw conscientiousness scores
cons <- runif(n_members, min = .3, max = 1)

# Cognitive ability of agents (proportion of knowledge learned per time hearing it):
## Select random value for each agent ("n_members") using a uniform distribution ("runif")
## The chosen min and max values indicate that agents will be assigned a cognitive ability level
## that allows them to increase how much they know about a given piece of by a value
## between ".1" and ".33" following a single learning attempt.
cog <- runif(n_members, min = .1, max = .33)


### PSEUDOCODE STEP 2 ###
### Assign agents an initial knowledge pool indicating what information they already know ###

# Proportion of knowledge pool initially known per agent; stored in the variable "prop_info"
# Currently sets all agents to having approximately equal amounts of knowledge
# at the start of the simulation by dividing 100% (1) by the number of agents (n_members)
prop_info <- 1 / n_members

# For this example, which pieces of information each agent knows initially will be probabilistic.
## First, identify the values indicating whether a given piece of information is
## initially known: know it initially ("1") or not ("0")
initial_info_state <- c(1, 0)

## Next, set the probability of knowing any given piece of information initially ("prop_info") or not (1-"prop_info")
initial_info_probs <- c(prop_info, (1 - prop_info))

## Next, initialize a matrix object to hold agents' knowledge pool ("know_pool") with number of rows equal to number of
## agents ("n_members") and number of columns equal to number of information pieces ("n_info").
## Fill the matrix with all 0's initially
know_pool <- matrix(0, nrow = n_members, ncol = n_info)

## Finally, populate the agent knowledge pool with initial knowledge of information by looping through each row
## of the knowledge pool object ("know_pool") and randomly sampling "n_info" values with replacement from the
## "initial_info_state" vector using the probabilities provided in "inital_info_probs".
## This procedure effectively populates each row of the "know_pool" object with a new vector indicating
## whether a piece of information is not initially known (0) or is known (1)
## Because the procedure used to determine whether an agent knows a given piece of information is random,
## there is a possibility that an agent could be initialized who knows nothing. For present purposes, we do not
## want to allow this possibility to occur. Thus, after populating the agent's knowledge pool, we check whether
## its knowledge pool contains no initially known information (sum(know_pool[i, ]) == 0). If this this is true,
## we randomly pick one piece of information in its knowledge pool and give that agent knowledge of it
## (know_pool[i, sample(1:n_info, size = 1) <- 1]).
for(i in agents) {

  know_pool[i, ] <- sample(initial_info_state, size = n_info, prob = initial_info_probs, replace = TRUE)

  if(sum(know_pool[i, ]) == 0) {
    know_pool[i, sample(1:n_info, size = 1)] <- 1
  }

}

# IMPORTANT NOTE! The above procedure for creating the initial knowledge pool means that
# some pieces of information may not be initially known by any agents on the team. In the present
# model, this means that those pieces of information could NEVER be learned by agents. Thus
# we need to use the initial knowledge pool object to establish the criterion for determining when
# the team has learned all possible knowledge and the model should stop running using the following
# steps:

## Calculate the total number of information pieces the team initially knows
total_know <- sum(know_pool)

## Identify which pieces of information are initially known by at least one agent and therefore could
## be shared with/learned by other agents on the team.
## Initialize a vector of length n_info that will store the number of agents that know each piece of information.
## Initially populate vector with all 0's
n_info_known <- rep(0, n_info)

## Next, determine the total number of agents that know each piece of information by computing the sum of each
## column in the knowledge pool matrix. This can be accomplished by using 1:n_info as the iterator in a for-loop,
## using the iterator to grab the data stored in each column of the know_pool matrix, and then summing
for(i in 1:n_info) {

  n_info_known[i] <- sum(know_pool[, i])

}

## Check whether each piece of knowledge is known by at least one person.
## This vector indicates the total number of information pieces that could be known/learned by
## any single agent.
n_agents_know <- sum(n_info_known >= 1)

## Multiply the total number of information pieces that could be known/learned ("n_agents_know")
## by the number of agents ("n_members") to set the criteria for stopping the simulation when
## all agents know every information piece that is possible to learn
possible_know <- n_members * n_agents_know

## Initialize the stopping criterion variable by comparing how much the team currently knows to
## how much they could possibly learn
still_learning <- total_know < possible_know

# Create a object to hold speaking data (who spoke and which piece of knowledge).
## The data will be structured as a matrix such that each row records the data generated
## during a given time point and columns will hold the identity of the speaker
## and which piece of knowledge was spoken.
## Because the number of time points required for the team to fully learn all pieces of
## information is unknown, the matrix object is intentionally created to be much larger
## than is likely to be needed (5000 rows).
## The entire matrix will initially be populated with "NA" values.
spoke <- matrix(NA, nrow = 5000, ncol = 2)

# Create a matrix to hold how many total pieces of information each agent knows over time.
## The data will be structured as a matrix such that each row records the data generated
## during a given time point and each column corresponds to how many pieces of information
## each agent fully knows at that time point.
## Because the number of time points required for the team to fully learn all pieces of
## information is unknown, the matrix object is intentionally created to be much larger
## than is likely to be needed (5000 rows).
## The entire matrix will initially be populated with "NA" values.
agent_know <- matrix(NA, nrow = 5000, ncol = n_members)

### PSEUDOCODE STEP 3 ###
# Initialize time index ("t") to record the number of time points
# (i.e., initially set it to zero)
t <- 0

###################
# MODEL ALGORITHM #
###################

### PSEUDOCODE STEP 4 ###
### WHILE total knowledge in the team < total possible knowledge: ###

# Beginning of while-loop that determines whether the team still has information to learn (still_learning)
# When still_learning = TRUE (i.e., total know < possible_know), the code in the while-loop will run.
# When still_learning = FALSE (i.e., total know == possible_know), the while-loop will stop running.
while(still_learning) {

### PSEUDOCODE STEP 5 ###
### Iterate counter to t = t + 1 ###
  t <- t + 1

### PSEUDOCODE STEP 6 ###
### Select an agent to share knowledge based on extraversion ###

  # Randomly sample an agent to share by sampling one value ("size = 1") from the "agents" object
  # using the relative extraversion levels in the team ("ext")
  current_speaker <- sample(agents, size = 1, prob = ext)

### PSEUDOCODE STEP 7 ###
### Select an agent to share a piece of information based on extraversion ###

  # Identify the knowledge pool of the selected speaker by extracting the row ("current_speaker") containing
  # the speaker's knowledge pool from the overall knowledge pool object ("know_pool")
  speaker_know <- know_pool[current_speaker, ]

  # Identify which pieces of information the speaker is able to share (i.e., which pieces of information
  # they have fully learned).
  # This is accomplished by identifying which pieces of information in the speaker's knowledge pool
  # ("speakers_know") have a value equal to "1"
  speaker_possible_info <- which(speaker_know == 1)

  # Select one piece of information from the speaker's knowledge pool that is fully known to share
  info_shared <- sample(speaker_possible_info, size = 1)

### PSEUDOCODE STEP 8 ###
### FOR each agent on the team, attempt to learn shared information: ###

  # Beginning of for-loop that determines whether agents on the team learn the information that was shared.
  # The for-loop will perform the code below for each agent ("agents") one at a time
  for (i in agents) {

### PSEUDOCODE STEP 9 ###
### Determine whether agent attends to the shared information based on conscientiousness ###

    # Use attend() function to determine whether the current agent ("i") attends to the information shared
    # by the speaker ("current_speaker") using the agent's conscientiousness level ("cons")
    attend <- attend_info(rcvr = i, spkr = current_speaker, lrnr_cons = cons[i])

### PSEUDOCODE STEP 10 ###
### IF agent attends to speaker and has not yet fully learned the shared information, ###
### learn shared piece of information at a rate proportional to cognitive ability ###

    # Beginning of if-statement that determines whether agents learns the shared information.
    # The conditional expression evaluates TRUE if the agent attended to the shared information
    # ("attend == TRUE") and they do not yet fully know the shared information ("know_pool[i, info_shared] < 1")
    if(attend == TRUE && know_pool[i, info_shared] < 1) {

      # If the conditional expression evaluates TRUE, use the learn() function to update how much the
      # learner knows about the share piece of information ("info_shared) in the agent's knowledge pool
      # ("know_pool[i, ]") based on the agent's cognitive ability ("cog[i]")
      know_pool[i, ] <- learn(info = info_shared, lrnr_know = know_pool[i, ], lrnr_cog = cog[i])

    ## End of if-statement determining if agent attend to information
    }
  ## End of the for-loop performing learning for agents
  }

  # Record data generated during current model iteration.
  ## Speaking data
  ### Record which agent shared information into the row corresponding to the current
  ### time point ("t") and the first column ("1") of the the "spoke" object
  spoke[t, 1] <- current_speaker

  ### Record which piece of information was shared into the row corresponding to the current
  ### time point ("t") and the second column ("2") of the the "spoke" object
	spoke[t, 2] <- info_shared

	## Learning data
	### Record the total number of pieces of information fully learned by each agent at the current time point
	for (i in agents) {

	  agent_know[t, i] <- sum(know_pool[i, ] == 1)

	}


### PSEUDOCODE STEP 11 ###
### Determine if team has fully learned all possible information. If so, go to Step 10; if not return to Step 5 ###

  # Count the total amount of knowledge known across the entire team
	total_know <- sum(know_pool)

	# Check if total knowledge learned is less than the possible knowledge that could be learned.
	# The result of this check is saved into the "still_learning" object and then reevaluted
	# to determine if the while-loop should continue
	still_learning <- total_know < possible_know

# End of the while-loop performing model iterations
}

###############
# DATA OUTPUT #
###############
# NOTE: The code below is not part of the main model operations and are provided as examples of
# cleaning and outputting data generated by the model

# Cleaning data
## Remove all unnecessary rows from the "spoke" and "agent_know" objects by only saving the time points
## in which the team was learning and sharing ("1:t")
spoke <- spoke[1:t, ]
agent_know <- agent_know[1:t, ]


# Speaking data
## Number of times each agent spoke using the table function
agent_speaking <- table(spoke[, 1])

## Converts number of times agents spoke to speaking rates by dividing by total simulation time.
## This can be checked against the extraversion levels of the agents ("ext") to ensure
## the simulation accurately represented agent extraversion levels as these values should be similar
sharing_rates <- agent_speaking / t


# Learning data
## Identify the number of chances each agent had to learn.
## Calculated as the total amount of time the team was learning/sharing minus the amount of time
## that each agent spoke, since the agents can't learn while they are sharing
learning_chances <- t - agent_speaking

## Plot development of agent knowledge pools
matplot(x = 1:t, y = agent_know / n_agents_know, type = "l", lty = 1, lwd = 1.5, xlab = "Time", ylab = "% of info fully known")
lines(x = 1:t, y = (rowSums(agent_know) / possible_know), type = "l", lty = 1, lwd = 2.5, col = "purple")
legend(x = "bottomright", legend = c(paste("Agent", agents, sep = " "), "Team"), col = c(1:5, "purple"), lty = 1, lwd = 2)
