HW4 Solutions Page

Return to homework page.

UML Diagram of HW4

________________  1   1___________________   1   *______________
| hw4          |<#>----|WorldPanel       |<#>-----|Creature    |
|______________|       |_________________|        |____________|
| addCreature()+---    |creatures        |        |animate()   |
|              |  |    |_________________|        |____________|
|              |  |    |+animate()       |                   | |___________
|              |  |    |+paintComponent()+----|              | |Victim    |
|              |  |    |-eatVictim()     +----|              | |__________|
|              |  |    |_________________|    |___________   | |animate() |               
| updateWorld()| _|________________________  / Both use  |   | |__________|
|_+____________|/ Calls CreatureFactory's |  | Iterator's|   |
  |             | create() method to make |  | to iterate|   |__________
  |             | a Creature. Then calls  |  | over      |   |Monster  |
  |             | WorldPanel's addCreature|  | LinkedList|   |_________|
  |             | method with this        |  | creatures |   |animate()|
  |             | Creature.               |  |___________|   |_________|
  |             |_________________________|
 _|___________________
/                    |
| Calls WorldPanel's |
| animate() method   |
| Called by a Timer  |
| on hw4 each second |
|____________________|

The Factory pattern is used in the class CreatureFactory's
create method to have one centralized place to do the creation of
all the Creature's. I didn't use an AbstractFactory class which I extended
in my example. 

The Strategy pattern was used in the animate() part of the diagram
concerning the WorldPanel and the abstract Creature class
and its two concrete subclasses Monster and Victim. The animate()
method of the WorldPanel calls the animate method of the Creature's
it has on it. 

The Iterator pattern is used in WorldPanel's paintComponent and
eatVictim methods to cycle through the LinkedList of Creature's.

Here are the two GIF files I used in writing HW4:
Victim Image monsterImage
// hw4.java 

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

/**

   A monster vs victims simulator 

   Run from the command line as:
  
   java hw4

   @author Chris Pollett
   @version 1.0
*/
public class hw4 extends JFrame 
{
 
   /**
      Creates our frame, sets up buttons, WorldPanel, listeners, sets up
      and starts animation Timer.
   */
   public hw4()
   {
      super("HW4 - Monster vs. Victims Simulation");
       
      containerSetup();
      
      timerSetup();
   }

   /*
      Sets up the contentPane of our frame for the simulation.
      Sets background to gray, initializes buttons and listeners.
      Sets up WorldPanel.
   */
   void containerSetup()
   {
      Container c = getContentPane();
      c.setLayout(new BorderLayout());
      c.setBackground(Color.gray);

      world= new WorldPanel();
      c.add(world, BorderLayout.CENTER);

      JPanel buttonPanel = new JPanel();

      JButton monsterButton = new JButton("Add Monster");
      monsterButton.addActionListener( new ActionListener()
         {
            public void actionPerformed(ActionEvent e)
            {
                 addCreature("Monster");
            }
         }
      );
      buttonPanel.add(monsterButton);

      JButton victimButton = new JButton("Add Victim");
      victimButton.addActionListener( new ActionListener()
         {
            public void actionPerformed(ActionEvent e)
            {
                 addCreature("Victim");
            }
         }
      );	
      buttonPanel.add(victimButton);

      c.add(buttonPanel, BorderLayout.SOUTH); 
      
   }

   /*
     Sets up the timer used to control the animation of the world
   */
   void timerSetup()
   {
      timer=new Timer(REFRESH_RATE, new ActionListener()
         {
            public void actionPerformed(ActionEvent e)
            {
                  updateWorld();
            }
         }
      );

      timer.start();
   }

   /*
     Called when either the Add Monster or Add Victim button
     pressed. Get x and y of where user wants creature then puts
     a new Creature there.
   */
   void addCreature(String type)
   {
       timer.stop();

       int x;
       int y;

       String xString = JOptionPane.showInputDialog("Enter x value of " 
                         + type +":");
       if( xString == null ) return;
       else x = Integer.parseInt(xString);

       String yString = JOptionPane.showInputDialog("Enter y value of " 
                         + type +":");
       if( yString == null ) return;
       else y = Integer.parseInt(yString);


       Creature creature = CreatureFactory.create(type, x, y);
       world.addCreature(creature);

       repaint();
       timer.start();
   } 

   /*
      Called by timer's ActionListener to update the animation
      and redraw the screen.
   */
   void updateWorld()
   {
       /* these two lines are the reverse of what's described in the
          HW spec, but I believe this order makes more sense.
          If you do it either way you should receive full credit.
       */
       world.animate(); 
       repaint();          
   }

   /*
      creates our application frame and sets closing behavior.
   */
   public static void main(String args[])
   {
       hw4 frame = new hw4();
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       frame.pack();
       frame.show();
   } 

  Timer timer; // used to control the animation
  WorldPanel world; // where the simulation takes place

  public static final int REFRESH_RATE = 1000; /* number of mS between
                                                  updates of animation */
}

//WorldPanel.java 

import java.awt.*;
import javax.swing.*;
import java.util.*;

/**
   Panel on which monster vs victim simulation carried out
*/
public class WorldPanel extends JPanel
{

   /**
      Initializes the list of creatures and set the background to gray
   */
   public WorldPanel()
   {
      creatures = new LinkedList();

      setBackground(Color.gray);
   }
 
   /**
      Adds a creature to the WorldPanel
   
      @param c - Creature we are adding.
   */
   public void addCreature(Creature c)
   {
        creatures.add(c);
   }

   /**
       moves each of the Creature's on our panel
   */
   public void animate()
   {
      Iterator iter = creatures.iterator();
      Creature c;
      
      while(iter.hasNext())
      {
         c = (Creature)iter.next();
         c.animate();
      }


   }

   /**
      Draws the WorldPanel. Called after repaint events.

      @param g - Graphics context on which drawing done.
   */
   public void paintComponent(Graphics g)
   {
       g.setColor(Color.blue);

       for(int i = 0; i < DEFAULT_X_TILES; i++)
       {
           for(int j = 0; j < DEFAULT_Y_TILES; j++)
           {
               g.fillRect(i*TILE_X_SIZE, j*TILE_Y_SIZE, 
                     RECT_X_SIZE, RECT_Y_SIZE);
           }
       }

       eatVictims();       

       Creature c;
       Iterator iter = creatures.iterator();
       while(iter.hasNext())
       {
           c = (Creature)iter.next();
           c.drawCreature(g, c.x*TILE_X_SIZE, c.y*TILE_Y_SIZE);
       }

   }

   /**
      This function is called by JFrame this panel lives on
      to determine the size is should set aside for this component.

      @return - preferred Dimension of this panel
   */
   public Dimension getPreferredSize()
   {
      return new Dimension(DEFAULT_X_TILES*TILE_X_SIZE,
                           DEFAULT_Y_TILES*TILE_Y_SIZE);
   }

   /*
     Used to delete any Victim that's on the same square as a Monster
   */
   void eatVictims()
   {
      Iterator iter = creatures.iterator();
      Iterator iter2;
      Creature c;
      Creature c2;

      /* first we mark Victims to be deleted
         (hard to use remove() when have more that one iterator
          active on a LinkedList.)
      */
     
      iter = creatures.iterator();
      while(iter.hasNext())
      {
          c = (Creature)iter.next();
          if(c instanceof Monster)
          {
              
              iter2 = creatures.iterator();
              while(iter2.hasNext()) 
              {
                 c2 = (Creature)iter2.next();
                 if(c2 instanceof Victim && c2.x == c.x && c2.y ==c.y)
                 {
                    c2.x = -1; // mark Victim 
                 }
              }
          }
      }

      iter = creatures.iterator();
      while(iter.hasNext())
      {
         c = (Creature)iter.next();
         if(c.x == -1) iter.remove(); // now delete
      }
   }

   LinkedList creatures; // list of creatures on WorldPanel

   public static final int DEFAULT_X_TILES = 20; //number of horizontal tiles 
   public static final int DEFAULT_Y_TILES = 20; //number of vertical tiles
   public static final int TILE_X_SIZE = 20; // x pixels set aside for a tile 
   public static final int TILE_Y_SIZE = 20; // y pixels set aside for a tile
   public static final int RECT_X_SIZE = 16; // x pixels used to draw tile 
   public static final int RECT_Y_SIZE = 16; // y pixels used to draw tile
}


//CreatureFactory.java

/**
   Factory class for Creature objects use in Monster vs Victim simulation
*/
public class CreatureFactory
{
   /**
      This function is used to make the Creature's specified by
      the parameters. 

      @param type - the type of the creature
      @param x - the x tile of the creature
      @param y - the y tile of the creature
      @return Creature - the creature we create
   */
   public static Creature create(String type, int x, int y)
   {
     if(type.equals("Monster"))
          return new Monster(x,y);

     if(type.equals("Victim"))
          return new Victim(x,y);

     return null;
   }
}

//Creature.java

import javax.swing.*;
import java.awt.*;

/**
   Encapsulates the notion of a creature that can appear in our
   Monster vs Victim simulation. i.e., a Monster or a Victim
*/
public abstract class Creature
{

   /**
      Create a Creature object at x, y with image from the file named s
      
      @param s - String of filename with image of Creature
      @param x - horizontal coordinate of tile the creature lives on
      @param y - vertital coordinate of tile the creature lives on 
   */
   public Creature(String s, int x, int y)
   {
      this.x = x;
      this.y = y;
      image = new ImageIcon(s);
   }

   /**
      Draws the current Creature to the specified graphics context
      at the specified location.

      @param g - the Graphics object on which we are drawing
      @param x - the x position in pixels at which we draw the creature
      @param y - the y position in pixels at which we draw the creature  
   */
   public void drawCreature(Graphics g, int x, int y)
   {
       image.paintIcon(null, g, x, y);
   }

   /**
      This method is overriden to update the creatures position
      according to some rule.
   */
   public abstract void animate();

   public int x; // the x tile the creature is on
   public int y; // the y tile the creature is on
   protected ImageIcon image; // the image used to draw the creature

}

//Monster.java

/**
   Used to draw, animate, and keep track of a Monster's in our
   Monsters vs. Victims simulation
*/
public class Monster extends Creature
{
   /**
      creates a monster at tile (x,y)
   */
   public Monster(int x, int y)
   {
       super("monster.gif", x, y);
   }

   /**
      In one turn a monster moves -2, -1, 0, 1, 2 horizontally
                                  -2, -1, 0, 1, 2 vertically

      If it goes off the world it wraps to the other side.
   */
   public void animate()
   {
      x += ((int)(Math.random()*X_DELTA)) - X_OFFSET;
      if(x < 0 ) x = WorldPanel.DEFAULT_X_TILES-1;
      if(x >= WorldPanel.DEFAULT_X_TILES ) x = 0;

      y += ((int)(Math.random()*Y_DELTA)) - Y_OFFSET;
      if(y < 0 ) y = WorldPanel.DEFAULT_Y_TILES-1;
      if(y >= WorldPanel.DEFAULT_Y_TILES ) y = 0;
   }

   public static int X_DELTA = 7; // in a turn x changes by at most a value < 3
   public static int X_OFFSET = 3;// subtracted from amount x changes
   public static int Y_DELTA = 7; // in a turn y changes by at most a value < 3
   public static int Y_OFFSET = 3;// subtracted from amount y changes
}

//Victim.java

/**
   Used to draw, animate, and keep track of a Victim's in our
   Monsters vs. Victims simulation
*/
public class Victim extends Creature
{
   /**
      creates a Victim at tile (x,y)
   */
   public Victim(int x, int y)
   {
       super("victim.gif", x, y);
   }

   /**
      In one turn a Victim moves -1, 0, 1 horizontally
                                 -1, 0, 1 vertically

      If it goes off the world it wraps to the other side.
   */
   public void animate()
   {
      x += ((int)(Math.random()*X_DELTA)) - X_OFFSET;
      if(x < 0 ) x = WorldPanel.DEFAULT_X_TILES-1;
      if(x >= WorldPanel.DEFAULT_X_TILES ) x = 0;

      y += ((int)(Math.random()*Y_DELTA)) - Y_OFFSET;
      if(y < 0 ) y = WorldPanel.DEFAULT_Y_TILES-1;
      if(y >= WorldPanel.DEFAULT_Y_TILES ) y = 0;

   }

   public static int X_DELTA = 3; // in a turn x changes by at most a value < 3
   public static int X_OFFSET = 1;// subtracted from amount x changes
   public static int Y_DELTA = 3; // in a turn y changes by at most a value < 3
   public static int Y_OFFSET = 1;// subtracted from amount y changes
   

}