Advertisement
Guest User

Untitled

a guest
Jul 19th, 2018
58
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 12.08 KB | None | 0 0
  1. // Class CritterModel keeps track of the state of the critter simulation.
  2.  
  3. import java.util.*;
  4. import java.awt.Point;
  5. import java.awt.Color;
  6. import java.lang.reflect.*;
  7.  
  8. public class CritterModel {
  9.     // the following constant indicates how often infect should fail for
  10.     // critters who didn't hop on their previous move (0.0 means no advantage,
  11.     // 1.0 means 100% advantage)
  12.     public static final double HOP_ADVANTAGE = 0.2; // 20% advantage
  13.  
  14.     private int height;
  15.     private int width;
  16.     private Critter[][] grid;
  17.     private Map<Critter, PrivateData> info;
  18.     private SortedMap<String, Integer>critterCount;
  19.     private boolean debugView;
  20.     private int simulationCount;
  21.     private static boolean created;
  22.    
  23.     public CritterModel(int width, int height) {
  24.         // this prevents someone from trying to create their own copy of
  25.         // the GUI components
  26.         if (created)
  27.             throw new RuntimeException("Only one world allowed");
  28.         created = true;
  29.  
  30.         this.width = width;
  31.         this.height = height;
  32.         grid = new Critter[width][height];
  33.         info = new HashMap<Critter, PrivateData>();
  34.         critterCount = new TreeMap<String, Integer>();
  35.         this.debugView = false;
  36.     }
  37.  
  38.     public Iterator<Critter> iterator() {
  39.         return info.keySet().iterator();
  40.     }
  41.  
  42.     public Point getPoint(Critter c) {
  43.         return info.get(c).p;
  44.     }
  45.  
  46.     public Color getColor(Critter c) {
  47.         return info.get(c).color;
  48.     }
  49.  
  50.     public String getString(Critter c) {
  51.         return info.get(c).string;
  52.     }
  53.  
  54.     public void add(int number, Class<? extends Critter> critter) {
  55.         Random r = new Random();
  56.         Critter.Direction[] directions = Critter.Direction.values();
  57.         if (info.size() + number > width * height)
  58.             throw new RuntimeException("adding too many critters");
  59.         for (int i = 0; i < number; i++) {
  60.             Critter next;
  61.             try {
  62.                 next = makeCritter(critter);
  63.             } catch (IllegalArgumentException e) {
  64.                 System.out.println("ERROR: " + critter + " does not have" +
  65.                                    " the appropriate constructor.");
  66.                 System.exit(1);
  67.                 return;
  68.             } catch (Exception e) {
  69.                 System.out.println("ERROR: " + critter + " threw an " +
  70.                                    " exception in its constructor.");
  71.                 System.exit(1);
  72.                 return;
  73.             }
  74.             int x, y;
  75.             do {
  76.                 x = r.nextInt(width);
  77.                 y = r.nextInt(height);
  78.             } while (grid[x][y] != null);
  79.             grid[x][y] = next;
  80.            
  81.             Critter.Direction d = directions[r.nextInt(directions.length)];
  82.             info.put(next, new PrivateData(new Point(x, y), d));
  83.         }
  84.         String name = critter.getName();
  85.         if (!critterCount.containsKey(name))
  86.             critterCount.put(name, number);
  87.         else
  88.             critterCount.put(name, critterCount.get(name) + number);
  89.     }
  90.  
  91.     @SuppressWarnings("unchecked")
  92.     private Critter makeCritter(Class critter) throws Exception {
  93.         Constructor c = critter.getConstructors()[0];
  94.         if (critter.toString().equals("class Bear")) {
  95.             // flip a coin
  96.             boolean b = Math.random() < 0.5;
  97.             return (Critter) c.newInstance(new Object[] {b});
  98.         } else {
  99.             return (Critter) c.newInstance();
  100.         }
  101.     }
  102.  
  103.     public int getWidth() {
  104.         return width;
  105.     }
  106.  
  107.     public int getHeight() {
  108.         return height;
  109.     }
  110.  
  111.     public String getAppearance(Critter c) {
  112.         // Override specified toString if debug flag is true
  113.         if (!debugView)
  114.             return info.get(c).string;
  115.         else {
  116.             PrivateData data = info.get(c);
  117.             if (data.direction == Critter.Direction.NORTH) return "^";
  118.             else if (data.direction == Critter.Direction.SOUTH) return "v";
  119.             else if (data.direction == Critter.Direction.EAST) return ">";
  120.             else return "<";
  121.         }
  122.     }
  123.    
  124.     public void toggleDebug() {
  125.         this.debugView = !this.debugView;
  126.     }
  127.  
  128.     private boolean inBounds(int x, int y) {
  129.         return (x >= 0 && x < width && y >= 0 && y < height);
  130.     }
  131.  
  132.     private boolean inBounds(Point p) {
  133.         return inBounds(p.x, p.y);
  134.     }
  135.  
  136.     // returns the result of rotating the given direction clockwise
  137.     private Critter.Direction rotate(Critter.Direction d) {
  138.         if (d == Critter.Direction.NORTH) return Critter.Direction.EAST;
  139.         else if (d == Critter.Direction.SOUTH) return Critter.Direction.WEST;
  140.         else if (d == Critter.Direction.EAST) return Critter.Direction.SOUTH;
  141.         else return Critter.Direction.NORTH;
  142.     }
  143.  
  144.     private Point pointAt(Point p, Critter.Direction d) {
  145.         if (d == Critter.Direction.NORTH) return new Point(p.x, p.y - 1);
  146.         else if (d == Critter.Direction.SOUTH) return new Point(p.x, p.y + 1);
  147.         else if (d == Critter.Direction.EAST) return new Point(p.x + 1, p.y);
  148.         else return new Point(p.x - 1, p.y);
  149.     }
  150.  
  151.     private Info getInfo(PrivateData data, Class original) {
  152.         Critter.Neighbor[] neighbors = new Critter.Neighbor[4];
  153.         Critter.Direction d = data.direction;
  154.         boolean[] neighborThreats = new boolean[4];
  155.         for (int i = 0; i < 4; i++) {
  156.             neighbors[i] = getStatus(pointAt(data.p, d), original);
  157.             if (neighbors[i] == Critter.Neighbor.OTHER) {
  158.                 Point p = pointAt(data.p, d);
  159.                 PrivateData oldData = info.get(grid[p.x][p.y]);
  160.                 neighborThreats[i] = d == rotate(rotate(oldData.direction));
  161.             }
  162.             d = rotate(d);
  163.         }
  164.         return new Info(neighbors, data.direction, neighborThreats);
  165.     }
  166.  
  167.     private Critter.Neighbor getStatus(Point p, Class original) {
  168.         if (!inBounds(p))
  169.             return Critter.Neighbor.WALL;
  170.         else if (grid[p.x][p.y] == null)
  171.             return Critter.Neighbor.EMPTY;
  172.         else if (grid[p.x][p.y].getClass() == original)
  173.             return Critter.Neighbor.SAME;
  174.         else
  175.             return Critter.Neighbor.OTHER;
  176.     }
  177.  
  178.     @SuppressWarnings("unchecked")
  179.     public void update() {
  180.         simulationCount++;
  181.         Object[] list = info.keySet().toArray();
  182.         Collections.shuffle(Arrays.asList(list));
  183.  
  184.         // This keeps track of critters that are locked and cannot be
  185.         // infected this turn. The happens when:
  186.         // * a Critter is infected
  187.         // * a Critter hops
  188.         Set<Critter> locked = new HashSet<Critter>();
  189.        
  190.         for (int i = 0; i < list.length; i++) {
  191.             Critter next = (Critter)list[i];
  192.             PrivateData data = info.get(next);
  193.             if (data == null) {
  194.                 // happens when creature was infected earlier in this round
  195.                 continue;
  196.             }
  197.             // clear any prior setting for having hopped in the past
  198.             boolean hadHopped = data.justHopped;
  199.             data.justHopped = false;
  200.             Point p = data.p;
  201.             Point p2 = pointAt(p, data.direction);
  202.  
  203.            
  204.             // try to perform the critter's action
  205.             Critter.Action move = next.getMove(getInfo(data, next.getClass()));
  206.             if (move == Critter.Action.LEFT)
  207.                 data.direction = rotate(rotate(rotate(data.direction)));
  208.             else if (move == Critter.Action.RIGHT)
  209.                 data.direction = rotate(data.direction);
  210.             else if (move == Critter.Action.HOP) {
  211.                 if (inBounds(p2) && grid[p2.x][p2.y] == null) {
  212.                     grid[p2.x][p2.y] = grid[p.x][p.y];
  213.                     grid[p.x][p.y] = null;
  214.                     data.p = p2;
  215.                     locked.add(next); //successful hop locks a critter from
  216.                                       // being infected for the rest of the
  217.                                       // turn
  218.                     data.justHopped = true;  // remember a successful hop
  219.                 }
  220.             } else if (move == Critter.Action.INFECT) {
  221.                 if (inBounds(p2) && grid[p2.x][p2.y] != null
  222.                     && grid[p2.x][p2.y].getClass() != next.getClass()
  223.                     && !locked.contains(grid[p2.x][p2.y])
  224.                     && (hadHopped || Math.random() >= HOP_ADVANTAGE)) {
  225.                     Critter other = grid[p2.x][p2.y];
  226.                     // remember the old critter's private data
  227.                     PrivateData oldData = info.get(other);
  228.                     // then remove that old critter
  229.                     String c1 = other.getClass().getName();
  230.                     critterCount.put(c1, critterCount.get(c1) - 1);
  231.                     String c2 = next.getClass().getName();
  232.                     critterCount.put(c2, critterCount.get(c2) + 1);
  233.                     info.remove(other);
  234.                     // and add a new one to the grid
  235.                     try {
  236.                         grid[p2.x][p2.y] = makeCritter(next.getClass());
  237.                         // This critter has been infected and is now locked
  238.                         // for the rest of this turn
  239.                         locked.add(grid[p2.x][p2.y]);
  240.                     } catch (Exception e) {
  241.                         throw new RuntimeException("" + e);
  242.                     }
  243.                     // and add to the map
  244.                     info.put(grid[p2.x][p2.y], oldData);
  245.                     // but it's new, so it didn't just hop
  246.                     oldData.justHopped = false;
  247.                 }
  248.             }
  249.         }
  250.         updateColorString();
  251.     }
  252.  
  253.     // calling this method causes each critter to update the stored color and
  254.     // text for toString; should be called each time update is performed and
  255.     // once before the simulation begins
  256.     public void updateColorString() {
  257.         for (Critter next : info.keySet()) {
  258.             info.get(next).color = next.getColor();
  259.             info.get(next).string = next.toString();
  260.         }
  261.     }
  262.  
  263.     public Set<Map.Entry<String, Integer>> getCounts() {
  264.         return Collections.unmodifiableSet(critterCount.entrySet());
  265.     }
  266.  
  267.     public int getSimulationCount() {
  268.         return simulationCount;
  269.     }
  270.  
  271.     private class PrivateData {
  272.         public Point p;
  273.         public Critter.Direction direction;
  274.         public Color color;
  275.         public String string;
  276.         public boolean justHopped;
  277.  
  278.         public PrivateData(Point p, Critter.Direction d) {
  279.             this.p = p;
  280.             this.direction = d;
  281.         }
  282.  
  283.         public String toString() {
  284.             return p + " " + direction;
  285.         }
  286.     }
  287.  
  288.     // an object used to query a critter's state (neighbors, direction)
  289.     private static class Info implements CritterInfo {
  290.         private Critter.Neighbor[] neighbors;
  291.         private Critter.Direction direction;
  292.         private boolean[] neighborThreats;
  293.  
  294.         public Info(Critter.Neighbor[] neighbors, Critter.Direction d,
  295.                     boolean[] neighborThreats) {
  296.             this.neighbors = neighbors;
  297.             this.direction = d;
  298.             this.neighborThreats = neighborThreats;
  299.         }
  300.  
  301.         public Critter.Neighbor getFront() {
  302.             return neighbors[0];
  303.         }
  304.  
  305.         public Critter.Neighbor getBack() {
  306.             return neighbors[2];
  307.         }
  308.  
  309.         public Critter.Neighbor getLeft() {
  310.             return neighbors[3];
  311.         }
  312.  
  313.         public Critter.Neighbor getRight() {
  314.             return neighbors[1];
  315.         }
  316.  
  317.         public Critter.Direction getDirection() {
  318.             return direction;
  319.         }
  320.  
  321.         public boolean frontThreat() {
  322.             return neighborThreats[0];
  323.         }
  324.        
  325.         public boolean backThreat() {
  326.             return neighborThreats[2];
  327.         }
  328.            
  329.         public boolean leftThreat() {
  330.             return neighborThreats[3];
  331.         }
  332.        
  333.         public boolean rightThreat() {
  334.             return neighborThreats[1];
  335.         }
  336.     }
  337. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement