Package swen40004.jordan.steele.rumourmill

Source Code of swen40004.jordan.steele.rumourmill.RumourMill$RunningData

package swen40004.jordan.steele.rumourmill;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;

import au.com.bytecode.opencsv.CSVWriter;

/**
* The primary class from where the model is run.
* @author jordansteele
*
*/

public class RumourMill {
 
  private World world; //the 'world' in which people and walls are contained
  private Neighbourhood neighbourhood; //the neighbourhood method in use
  private boolean wrapAround; //whether the world should wrap around or not
  private boolean rediscussRumour; //whether two people who have discussed the
                  //rumour together can rediscuss it or not
  private String outputFolder; //the folder where data should be output
 
  //Defines the potential offsets in a vonNeumann neighbourhood
  private final Coord[] vonNeumannNeighbourhood = {           
              new Coord(0,-1),  
    new Coord(-1,0),                   new Coord(1,0),
              new Coord(0,1)             };
 
  //Defines the potential offsets in a moore neighbourhood
  private final Coord[] mooreNeighbourhood = {   
      new Coord(-1,-1)new Coord(0,-1)new Coord(1,-1),
      new Coord(-1,0),                   new Coord(1,0),
      new Coord(-1,1),    new Coord(0,1),    new Coord(1,1)   };
 
  //An enum defining the two possible neighbourhoods
  public static enum Neighbourhood {
    VONNEUMANN,
    MOORE
  }
 
  /**
   * Constructor
   * @param wrapAround Whether the world should wrap around or not
   * @param xdimen The size of the world in the x dimension
   * @param ydimen The size of the world in the y dimension
   * @param rumourSources The coordinates of the people who will know the
   *             rumour at time 0
   * @param walls The coordinates of all the 'walls'
   * @param neighbourhood The method that should be used for the neighbourhood
   * @param rumourAbout The coordinate of who the rumour is about. This can
   *             be null.
   * @param rediscussRumour Whether two people who have discussed the rumour
   *               together can rediscuss it or not
   * @param outputFolder The folder where data should be output
   */
  public RumourMill(boolean wrapAround, int xdimen, int ydimen,
      Coord[] rumourSources, Coord[] walls,
      Neighbourhood neighbourhood, Coord rumourAbout,
      boolean rediscussRumour, String outputFolder) {
    this.world = new World(xdimen, ydimen, rumourSources, rumourAbout);
    this.neighbourhood = neighbourhood;
    this.wrapAround = wrapAround;
    this.rediscussRumour = rediscussRumour;
    this.outputFolder = outputFolder;
  }
 
  /**
   * Constructor
   * @param wrapAround Whether the world should wrap around or not
   * @param xdimen The size of the world in the x dimension
   * @param ydimen The size of the world in the y dimension
   * @param intialClique Fraction of people who initially know the rumour
   * @param walls The coordinates of all the 'walls'
   * @param neighbourhood The method that should be used for the neighbourhood
   * @param rumourAbout The coordinate of who the rumour is about. This can
   *             be null.
   * @param rediscussRumour Whether two people who have discussed the rumour
   *               together can rediscuss it or not
   * @param outputFolder The folder where data should be output
   */
  public RumourMill(boolean wrapAround, int xdimen, int ydimen,
      float initialClique, Coord[] walls,
      Neighbourhood neighbourhood, Coord rumourAbout,
      boolean rediscussRumour, String outputFolder) {
    this.world = new World(xdimen, ydimen, initialClique, rumourAbout);
    this.neighbourhood = neighbourhood;
    this.wrapAround = wrapAround;
    this.rediscussRumour = rediscussRumour;
    this.outputFolder = outputFolder;
  }
 
  /**
   * Runs the model for N iterations and the averages the results
   * @param iterations The number of times the model should be run for
   */
  public void runModel(int iterations) {
    //LinkedHashMap provides in order iterating
    //Stores stats for each tick in the simulation
    LinkedHashMap<Integer, RunningData> runningDataAggregates =
        new LinkedHashMap<Integer, RunningData>();
   
    //Stores stats for each person at the end of the simulation
    HashMap<Coord, Person> finalPersonDataAggregates =
        new HashMap<Coord, Person>();
   
    //Stores who has discussed the rumour together
    Graph<Person> discussedRumourGraph;
   
    int ticks;
    int prevClique;
   
    //run for n iterations
    for (int n=0; n < iterations; n++) {
      //Set the ticks to 0 and create a new graph
      ticks = 0;
      discussedRumourGraph = new Graph<Person>();
     
      //run the simulation and stop when everyone has heard the rumour
      while (this.world.getClique() < this.world.size()) { 
        ticks++;
        prevClique = this.world.getClique();
       
        //For each person in the world, if they have heard the rumour
        //pass on the rumour with their chance to pass on the rumour
        for (int i=0; i < this.world.sizeX(); i++) {
          for (int j=0; j < this.world.sizeY(); j++) {
            Cell currentCell = this.world.getCell(i, j);
            if (currentCell instanceof Person) {
              Person currentPerson = (Person) currentCell;
              if (currentPerson.getTimesHeard() > 0 &&
                  currentPerson.getChanceToPassOnRumour() >=
                  new Random().nextFloat()) {
                Person neighbour = determineNeighbour(i, j,
                    discussedRumourGraph);
                if (neighbour != null) {
                  discussedRumourGraph.addRelationship(
                      currentPerson, neighbour);
                  neighbour.setJustHeardFrom(currentPerson);
                }
              }
            }
          }
        }
       
        //Update the world to reflect who just heard the rumour
        this.world.update(ticks);
       
        //Update our running stats with the stats for this tick
        runningDataAggregates = updateRunningData(runningDataAggregates,
            ticks, prevClique);
      }
     
      //After the simulation is run, record the final person stats
      finalPersonDataAggregates = updateFinalPersonData(
          finalPersonDataAggregates);
     
      //Reset the world and inform user of the progress
      this.world.resetWorld();
      System.out.printf(Strings.COMPLETED_RUN, n + 1);
    }
   
    //Write the running and final person stats to a csv file
    writeRunningDataAggregatesAsAverageToCSV(runningDataAggregates);
    writeFinalPersonDataAggregatesAsAverageToCSV(finalPersonDataAggregates,
        iterations);
  }
 
  /**
   * Calculates the possible neighbours to a cell and chooses one to return
   * @param currXDimen The xDimen position of the cell to find a neighbour of
   * @param currYDimen The yDimen position of the cell to find a neighbour of
   * @param discussedRumourGraph The graph of which users have discussed the
   *                 rumour together
   * @return A neighbour allowed with the current settings of the model
   *       or null if no possible neighbours
   */
  private Person determineNeighbour(int currXDimen, int currYDimen,
      Graph<Person> discussedRumourGraph) {
    Person currentPerson = (Person)this.world.getCell(currXDimen,
        currYDimen);
   
    //Determine neighbourhood to use
    Coord[] neighbourhood = this.vonNeumannNeighbourhood;   
    if (this.neighbourhood == Neighbourhood.MOORE) {
      neighbourhood = this.mooreNeighbourhood;
    }
   
    //Intiate list possible neighbours to return
    ArrayList<Person> personNeighbours = new ArrayList<Person>();
    //For each neighbour in the neighbourhood
    for (Coord neighbourOffset : neighbourhood) {
     
      //Determine position of the neighbour
      int neighbourXDimen = currXDimen + neighbourOffset.x;
      int neighbourYDimen = currYDimen + neighbourOffset.y;
      //if using wrap around adjust if outside world
      if (this.wrapAround) {
        if (neighbourXDimen >= this.world.sizeX()) {
          neighbourXDimen = 0;
        }
        if (neighbourYDimen >= this.world.sizeY()) {
          neighbourYDimen = 0;
        }
        if (neighbourXDimen < 0) {
          neighbourXDimen = this.world.sizeX() - 1;
        }
        if (neighbourYDimen < 0) {
          neighbourYDimen = this.world.sizeY() - 1;
        }
      }
     
      //Try to add to possible neighbours if using wrap around or
      //inside world and cell is a person
      if (this.wrapAround || (neighbourXDimen < this.world.sizeX() &&
          neighbourYDimen < this.world.sizeY() &&
          neighbourXDimen >= 0 && neighbourYDimen >= 0)) {
        Cell neighbour = this.world.getCell(neighbourXDimen,
            neighbourYDimen);
        if (neighbour instanceof Person) {
          Person neighbourPerson = (Person) neighbour;
          //add to possible neighbours if allowed to rediscuss rumours
          //or have not discussed the rumour with this person
          if (this.rediscussRumour ||
              !discussedRumourGraph.getNeighbours(currentPerson).
              contains(neighbourPerson)) {
            personNeighbours.add(neighbourPerson);
          }
        } 
      }
    }
   
    //Choose a random neighbour from the possible neighbours and return it
    if (personNeighbours.size() != 0){
      return personNeighbours.get(
          new Random().nextInt(personNeighbours.size()));
    }

    //if no possible neighbours, return null
    return null;
  }
 
  /**
   * Updates the aggregate stats being stored for each tick
   * @param runningDataAggregates The current stats
   * @param ticks The current ticks
   * @param prevClique The size of the clique in the previous tick
   * @param peopleRehearingRumour The amount of people who reheard the rumour
   * @return The updated running data aggregate stats
   */
  private LinkedHashMap<Integer, RunningData> updateRunningData(
      LinkedHashMap<Integer, RunningData> runningDataAggregates,
      int ticks, int prevClique) {
   
    //Get the data for the current tick or init the data if no data for the
    //current tick
    RunningData currentTickData = runningDataAggregates.get(ticks);
    if (currentTickData == null) {
      currentTickData = runningDataAggregates.put(ticks,
          new RunningData(0, 0, 0));
      currentTickData = runningDataAggregates.get(ticks);
    }
   
    //Calculate the change in the clique
    int deltaClique = (this.world.getClique() - prevClique);
   
    //Update the stats for this tick
    currentTickData.setClique(currentTickData.getClique() +
        this.world.getClique());
    currentTickData.setDeltaClique(currentTickData.getDeltaClique()
        + deltaClique);
    currentTickData.setSuccessiveRatio(currentTickData.getSuccessiveRatio()
        + this.world.getClique() / (float)prevClique);
    currentTickData.setValuesAddedTogether(
        currentTickData.getValuesAddedTogether() + 1);
   
    return runningDataAggregates;
  }
 
  /**
   * Updates the aggregate stats being stored for each person
   * @param finalPersonDataAggregates The current stats
   * @return The updated aggregate stats for each person
   */
  private HashMap<Coord, Person> updateFinalPersonData(HashMap<Coord,
      Person> finalPersonDataAggregates) {
    //For each Person
    for (int i=0; i < this.world.sizeX(); i++) {
      for (int j=0; j < this.world.sizeY(); j++) {
        Cell currentCell = this.world.getCell(i, j);
        if (currentCell instanceof Person) {
          //Get current person for the simulation just run
          Person currentPerson = (Person) currentCell;
         
          //Get current aggregate person
          Coord currentCoord = new Coord(i, j);
          Person currentAggregatePerson =
              finalPersonDataAggregates.get(currentCoord);
          if (currentAggregatePerson == null) {
            currentAggregatePerson =
                finalPersonDataAggregates.put(currentCoord,
                    new Person());
            currentAggregatePerson =
                finalPersonDataAggregates.get(currentCoord);
          }
         
          //Update aggregate stats by adding the current aggregate
          //and the result after the current simulation
          currentAggregatePerson.setTimesHeard(
              currentAggregatePerson.getTimesHeard() +
              currentPerson.getTimesHeard());
          currentAggregatePerson.setFirstTimeHeard(
              currentAggregatePerson.getFirstTimeHeard() +
              currentPerson.getFirstTimeHeard());
          currentAggregatePerson.setKnowledgeOfRumour(
              currentAggregatePerson.getKnowledgeOfRumour() +
              currentPerson.getKnowledgeOfRumour());
        }
      }
    }
    return finalPersonDataAggregates;
  }
 
  /**
   * Writes the running data averages for each tick and
   * in order of ticks to csv
   * @param runningDataAggregates The aggregate data
   */
  private void writeRunningDataAggregatesAsAverageToCSV(LinkedHashMap<Integer,
      RunningData> runningDataAggregates) {
    //Open the csv file and write the header
    CSVWriter runningDataWriter = CSVWrapper.openCSV("../" +
      this.outputFolder + Strings.RUNNING_DATA_FILENAME);
    runningDataWriter.writeNext(new String[] {"Ticks", "Clique",
        "DeltaClique", "SuccessiveRatio"});
   
    //Average the stats and write them out
    for (Map.Entry<Integer, RunningData> entry :
      runningDataAggregates.entrySet()) {
      runningDataWriter.writeNext(new String[] {"" + entry.getKey(),
          "" + entry.getValue().clique /
          entry.getValue().getValuesAddedTogether(),
          "" + entry.getValue().deltaClique /
          entry.getValue().getValuesAddedTogether(),
          "" + entry.getValue().successiveRatio /
          entry.getValue().getValuesAddedTogether()});
    }
   
    //Close the csv
    CSVWrapper.closeCSV(runningDataWriter);
  }
 
  /**
   * Writes the average stats for each person to csv
   * @param finalPersonDataAggregates The aggregate data
   * @param iterations The number of iterations aggregated
   */
  private void writeFinalPersonDataAggregatesAsAverageToCSV(HashMap<Coord,
      Person> finalPersonDataAggregates, int iterations) {
    //Open the csv file and write the header
    CSVWriter personDetailsWriter = CSVWrapper.openCSV("../" +
      this.outputFolder + Strings.FINAL_PERSON_DATA_FILENAME);
    personDetailsWriter.writeNext(new String[] {"TimesHeard",
        "FirstTimeHeard", "KnowledgeOfRumour"});
   
    //Average the stats and write them out
    for (Map.Entry<Coord, Person> entry :
      finalPersonDataAggregates.entrySet()) {
      personDetailsWriter.writeNext(new String[] {
          "" +
          entry.getValue().getTimesHeard() / (float)iterations,
          ""
          + entry.getValue().getFirstTimeHeard() / (float)iterations,
          "" +
          entry.getValue().getKnowledgeOfRumour()
          / (float)iterations});
    }
   
    //Close the csv
    CSVWrapper.closeCSV(personDetailsWriter);
  }
 
  /**
   * Inner class to store the running data
   * @author jordansteele
   *
   */
  private class RunningData {
    private float clique;  //the amount of people in the clique
    private float deltaClique;  //the change in the clique
    private float successiveRatio; //the ratio of people first hearing
                    //to rehearing
    private int valuesAddedTogether; //how many values have been aggregated
   
    /**
     * Constructor
     * @param clique Initial size of clique
     * @param deltaClique Initial change in clique
     * @param successiveRatio //Initial ratio
     */
    public RunningData (float clique, float deltaClique,
        float successiveRatio) {
      this.clique = clique;
      this.deltaClique = deltaClique;
      this.successiveRatio = successiveRatio;
      this.valuesAddedTogether = 0;
    }
   
    /**
     * Getter for clique
     * @return The value for clique
     */
    public float getClique() {
      return clique;
    }
   
    /**
     * Getter for deltaClique
     * @return The value for deltaClique
     */
    public float getDeltaClique() {
      return deltaClique;
    }
 
    /**
     * Getter for successiveRatio
     * @return The value for successiveRatio
     */
    public float getSuccessiveRatio() {
      return successiveRatio;
    }
   
    /**
     * Getter for valuesAddedTogether
     * @return The value for valuesAddedTogether
     */
    public int getValuesAddedTogether() {
      return valuesAddedTogether;
    }
   
    /**
     * Setter for clique
     * @param clique The value to set clique to
     */
    public void setClique(float clique) {
      this.clique = clique;
    }
   
    /**
     * Setter for deltaClique
     * @param deltaClique The value to set deltaClique to
     */
    public void setDeltaClique(float deltaClique) {
      this.deltaClique = deltaClique;
    }
   
    /**
     * Setter for successiveRatio
     * @param successiveRatio The value to set successiveRatio to
     */
    public void setSuccessiveRatio(float successiveRatio) {
      this.successiveRatio = successiveRatio;
    }

    /**
     * Setter for valuesAddedTogether
     * @param valuesAddedTogether The value to set valuesAddedTogether to
     */
    public void setValuesAddedTogether(int valuesAddedTogether) {
      this.valuesAddedTogether = valuesAddedTogether;
    }
  }
 
}
TOP

Related Classes of swen40004.jordan.steele.rumourmill.RumourMill$RunningData

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.