code: package resource_exchange_arena;
  
  import java.io.FileWriter;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Random;
  
  public class Day {
      // List of all the possible allocations that exist in the current simulation.
      private final ArrayList<Integer> availableTimeSlots = new ArrayList<>();
  
      int socPop;
      int selPop;
      double socSat;
      double selSat;
      double socSD;
      double selSD;
  
      double[] socialStatValues;
      double[] selfishStatValues;
  
      double randomAllocations;
      double optimumAllocations;
  
      /**
       * Each Simulation run consists of a number of days, each day consists of requesting and being allocated time slots,
       * exchanging those slots with other agents, and agents using social learning to learn from their experiences.
       *
       * @param demandCurves Double arrays of demand used by the agents, when multiple curves are used the agents
       *                    are split equally between the curves.
       * @param totalDemandValues Double values represeneting the sum of all values in their associated demand curves.
       * @param availabilityCurve Integer array representing the amount of energy available at each timeslot.
       * @param totalAvailability Integer value representing the total energy available throughout the day.
       * @param day Integer value representing the current day being simulated.
       * @param maxExchanges Stores the highest number of exchange rounds reached each simulation.
       * @param populationSize Integer value representing the size of the initial agent population.
       * @param uniqueTimeSlots Integer value representing the number of unique time slots available in the simulation.
       * @param slotsPerAgent Integer value representing the number of time slots each agent requires.
       * @param numberOfAgentsToEvolve Integer value representing the number of Agents who's strategy will change at the
       *                               end of each day.
       * @param uniqueAgentTypes Integer ArrayList containing each unique agent type that exists when the simulation
       *                         begins.
       * @param agents Array List of all the agents that exist in the current simulation.
       * @param allDailyDataCSVWriter Used to store data ragarding the state of the system at the end of each day.
       * @param perAgentDataCSVWriter Used to store data ragarding the state of the agent at the end of each day.
       * @param eachRoundDataCSVWriter Used to store data ragarding the state of the system at the end of each round.
       * @param run Integer value identifying the current simulation run.
       * @exception IOException On input error.
       * @see IOException
       */
      Day(
          double[][] demandCurves,
          double[] totalDemandValues,
          int [] availabilityCurve,
          int totalAvailability,
          int day,
          ArrayList<Integer> maxExchanges,
          int populationSize,
          int uniqueTimeSlots,
          int slotsPerAgent,
          int numberOfAgentsToEvolve,
          ArrayList<Integer> uniqueAgentTypes,
          ArrayList<Agent> agents,
          FileWriter dailyDataWriter,
          FileWriter perAgentDataCSVWriter,
          FileWriter eachRoundDataCSVWriter,
          int run
      ) throws IOException {
  
          if(!availableTimeSlots.isEmpty()) {
              availableTimeSlots.clear();
          }
  
          // Fill the available time slots with all the slots that exist each day.
          int requiredTimeSLots = populationSize * slotsPerAgent;
  
          for (int i = 1; i <= requiredTimeSLots; i++) {
              // Get the simulations seeded Random object.
              Random random = ResourceExchangeArena.random;
  
              // Selects a time slot based on the demand curve.
              int wheelSelector = random.nextInt(totalAvailability);
              int wheelCalculator = 0;
              int timeSlot = 0;
              while (wheelCalculator < wheelSelector) {
                  wheelCalculator = wheelCalculator + (availabilityCurve[timeSlot]);
                  timeSlot++;
              }
              availableTimeSlots.add(timeSlot);
          }
  
  
          // Agents start the day by requesting and receiving an allocation of time slots.
          Collections.shuffle(agents, ResourceExchangeArena.random);
          ArrayList<Integer> curves = new ArrayList<>();
  
          int curve = 0;
          for (int i = 0; i < agents.size(); i++) {
              curves.add(curve);
              curve++;
              if (curve >= demandCurves.length) {
                  curve = 0;
              }
          }
          Collections.shuffle(curves);
  
          for (Agent a : agents) {
              a.resetDailyTracking();
              int selector = curves.remove(0);
              ArrayList<Integer> requestedTimeSlots = a.requestTimeSlots(demandCurves[selector], totalDemandValues[selector]);
              ArrayList<Integer> allocatedTimeSlots = getRandomInitialAllocation(requestedTimeSlots);
              a.receiveAllocatedTimeSlots(allocatedTimeSlots);
          }
  
          // The random and optimum average satisfaction scores are calculated before exchanges take place.
          randomAllocations = CalculateSatisfaction.averageAgentSatisfaction(agents);
          optimumAllocations = CalculateSatisfaction.optimumAgentSatisfaction(agents);
  
          // A pre-determined number of pairwise exchanges take place, during each exchange all agents have a chance to
          // trade with another agent.
          int currentExchanges = 0;
          int timeout = 0;
          int maxTimeout = 10;
  
          while(timeout < maxTimeout) {
  
              /*
               * With each exchange all agents form pairwise exchanges and are able to consider a trade with their
               * partner for one time slot.
               *
               * @param run Integer value identifying the current simulation run.
               * @param day Integer value representing the current day being simulated.
               * @param exchange Integer value representing the current exchange being simulated.
               * @param uniqueAgentTypes Integer ArrayList containing each unique agent type that exists when the
               *                         simulation begins.
               * @param agents Array List of all the agents that exist in the current simulation.
               * @param eachRoundDataCSVWriter Used to store data ragarding the state of the system at the end of each round.
               * @exception IOException On input error.
               * @see IOException
               */ 
              Exchange current = new Exchange(
                      run,
                      day,
                      currentExchanges,
                      uniqueAgentTypes,
                      agents,
                      eachRoundDataCSVWriter
              );
  
              if (current.noExchanges == true) {
                  timeout++;
              } else {
                  timeout = 0;
              }
  
              currentExchanges++;
          }
  
          maxExchanges.add(currentExchanges);
  
          socPop = 0;
          selPop = 0;
  
          for (Agent a : agents) {
              if (a.getAgentType() == ResourceExchangeArena.SOCIAL) {
                  socPop++;
              } else if (a.getAgentType() == ResourceExchangeArena.SELFISH) {
                  selPop++;
              }
          }
  
          socSat = CalculateSatisfaction.averageAgentSatisfaction(agents, ResourceExchangeArena.SOCIAL);
          selSat = CalculateSatisfaction.averageAgentSatisfaction(agents, ResourceExchangeArena.SELFISH);
          socSD = CalculateSatisfaction.averageSatisfactionStandardDeviation(agents, ResourceExchangeArena.SOCIAL);
          selSD = CalculateSatisfaction.averageSatisfactionStandardDeviation(agents, ResourceExchangeArena.SELFISH);
  
          socialStatValues = CalculateSatisfaction.statisticalValues(agents, ResourceExchangeArena.SOCIAL);
          selfishStatValues = CalculateSatisfaction.statisticalValues(agents, ResourceExchangeArena.SELFISH);
  
          dailyDataWriter.append(String.valueOf(run));
          dailyDataWriter.append(",");
          
          dailyDataWriter.append(String.valueOf(day));
          dailyDataWriter.append(",");
          
          dailyDataWriter.append(String.valueOf(socPop));
          dailyDataWriter.append(",");
          
          dailyDataWriter.append(String.valueOf(selPop));
          dailyDataWriter.append(",");
          
          dailyDataWriter.append(String.valueOf(socSat));
          dailyDataWriter.append(",");
          
          dailyDataWriter.append(String.valueOf(selSat));
          dailyDataWriter.append(",");
  
          dailyDataWriter.append(String.valueOf(socSD));
          dailyDataWriter.append(",");
          
          dailyDataWriter.append(String.valueOf(selSD));
          dailyDataWriter.append(",");
  
          dailyDataWriter.append(String.valueOf(socialStatValues[0]));
          dailyDataWriter.append(",");
          
          dailyDataWriter.append(String.valueOf(selfishStatValues[0]));
          dailyDataWriter.append(",");
  
          dailyDataWriter.append(String.valueOf(socialStatValues[1]));
          dailyDataWriter.append(",");
          
          dailyDataWriter.append(String.valueOf(selfishStatValues[1]));
          dailyDataWriter.append(",");
  
          dailyDataWriter.append(String.valueOf(socialStatValues[2]));
          dailyDataWriter.append(",");
          
          dailyDataWriter.append(String.valueOf(selfishStatValues[2]));
          dailyDataWriter.append(",");
  
          dailyDataWriter.append(String.valueOf(socialStatValues[3]));
          dailyDataWriter.append(",");
          
          dailyDataWriter.append(String.valueOf(selfishStatValues[3]));
          dailyDataWriter.append(",");
  
          dailyDataWriter.append(String.valueOf(socialStatValues[4]));
          dailyDataWriter.append(",");
          
          dailyDataWriter.append(String.valueOf(selfishStatValues[4]));
          dailyDataWriter.append(",");
  
          dailyDataWriter.append(String.valueOf(socialStatValues[5]));
          dailyDataWriter.append(",");
          
          dailyDataWriter.append(String.valueOf(selfishStatValues[5]));
          dailyDataWriter.append(",");
          
          dailyDataWriter.append(String.valueOf(randomAllocations));
          dailyDataWriter.append(",");
  
          dailyDataWriter.append(String.valueOf(optimumAllocations));
          dailyDataWriter.append("\n");
  
  
          for (Agent a: agents) {
              perAgentDataCSVWriter.append(String.valueOf(run));
              perAgentDataCSVWriter.append(",");
      
              perAgentDataCSVWriter.append(String.valueOf(day));
              perAgentDataCSVWriter.append(",");
  
              perAgentDataCSVWriter.append(String.valueOf(a.getAgentType()));
              perAgentDataCSVWriter.append(",");
  
              perAgentDataCSVWriter.append(String.valueOf(a.calculateSatisfaction(null)));
              perAgentDataCSVWriter.append(",");
  
              perAgentDataCSVWriter.append(String.valueOf(a.getRejectedReceivedExchanges()));
              perAgentDataCSVWriter.append(",");
  
              perAgentDataCSVWriter.append(String.valueOf(a.getSocialCapitalExchanges() + a.getNoSocialCapitalExchanges()));
              perAgentDataCSVWriter.append(",");
  
              perAgentDataCSVWriter.append(String.valueOf(a.getRejectedRequestedExchanges()));
              perAgentDataCSVWriter.append(",");
  
              perAgentDataCSVWriter.append(String.valueOf(a.getAcceptedRequestedExchanges()));
              perAgentDataCSVWriter.append(",");
  
              perAgentDataCSVWriter.append(String.valueOf(a.getSocialCapitalExchanges()));
              perAgentDataCSVWriter.append(",");
  
              perAgentDataCSVWriter.append(String.valueOf(a.getNoSocialCapitalExchanges()));
              perAgentDataCSVWriter.append(",");
  
              perAgentDataCSVWriter.append(String.valueOf(a.getUnspentSocialCapital()));
              perAgentDataCSVWriter.append("\n");
          }
  
          /*
           * To facilitate social learning, for the number of the agents who are able to consider changing their strategy,
           * an Agent is selected at random, and then a second agent is selected to be observed. The first agent selected
           * checks whether their performance was weaker than the agent observed, if so they have a chance to copy the
           * strategy used by the observed agent in the previous day, with the likelihood of copying their strategy
           * proportional to the difference between their individual satisfactions.
           *
           * @param agents Array List of all the agents that exist in the current simulation.
           * @param slotsPerAgent Integer value representing the number of time slots each agent requires.
           * @param numberOfAgentsToEvolve Integer value representing the number of Agents who's strategy may change at
           *                               the end of each day.
           */
          new SocialLearning(agents, slotsPerAgent, numberOfAgentsToEvolve);
      }
  
      /**
       * Gives a random initial time slot allocation to an Agent based on the number of time slots it requests and the
       * time slots that are currently available.
       *
       * @param requestedTimeSlots The time slots that the Agent has requested.
       * @return ArrayList<Integer> Returns a list of time slots to allocated to the Agent.
       */
      private ArrayList<Integer> getRandomInitialAllocation(ArrayList<Integer> requestedTimeSlots) {
          ArrayList<Integer> timeSlots = new ArrayList<>();
  
          for (int requestedTimeSlot = 1; requestedTimeSlot <= requestedTimeSlots.size(); requestedTimeSlot++) {
              // Only allocate time slots if there are slots available to allocate.
              if (!availableTimeSlots.isEmpty()) {
                  int selector = ResourceExchangeArena.random.nextInt(availableTimeSlots.size());
                  int timeSlot = availableTimeSlots.get(selector);
  
                  timeSlots.add(timeSlot);
                  availableTimeSlots.remove(selector);
              } else {
                  System.out.println("Error: No Timeslots Available");
              }
          }
          return timeSlots;
      }
  }
  