import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import java.text.*;
 
public class Sweeps extends Applet implements MouseListener, 							KeyListener, Runnable
{
	// Screen constants	
	static final int SCRWIDTH = 540;
	static final int SCRHEIGHT = 380;
	static final int BUTTONWIDTH = 65;
	static final int BUTTONHEIGHT = 30;
	static final int TinyOFFSET = 5; 
	static final int SmOFFSET = 10; 
	static final int MedOFFSET = 15;
	static final int LgOFFSET = 20; 
	static final int MAZEOFFSET = 120; 
	static final int SIZEWIDTH = 210; 
	static final int SIZEHEIGHT = 100; 
	static final int SIZESTART = 130; 
	static final int BULLPEN = 35; 

	// Room constants
	static final int LEFTEDGE = 4;
	static final int RIGHTEDGE = 2;
	static final int BOTTOMEDGE = 3;
	static final int TOPEDGE = 1;
	static final int INSIDE = 10;
	static final int TLC = 5;
	static final int TRC = 6;
	static final int BLC= 7;
	static final int BRC = 8;
	static final int FRONTDOOR = 9;
	Color dirty = new Color(255,120, 100);

	// Maze variables
	Hashtable DirtyRoom = new Hashtable();
	Room NewMaze[] = new Room[196];
	Room FrontDoor; 
	int MazeType = 1;
	int MazeHeight = 8;
	int MazeWidth = 8;
	int RoomSize;
	int MaxRooms;
	int Wall;
	int TopX;
	int TopY;
	int Trys;
	int MoveCounter = 0;
	int Goal;

	// Sweeper variables
	Hashtable SweepHere = new Hashtable();
	static final int SWEEPER = 18;
	int ActiveSweeper = 1;
	int NextSweeper = 3;
	// Marks where the sweepers start at the front door
	int SweeperX;
	int SweeperY;

	boolean SizeSw = false;	

	// Game Over display variables
	Thread blinker = null;
	boolean threadSuspended = true;
	String GameOver1;
	String GameOver2;
	int delay = 400;
	int OnOff = 0;

	Random rand = new Random();
	
	// Set up clicking areas
	Rectangle BuildIt = new Rectangle(SmOFFSET, SmOFFSET, BUTTONWIDTH, BUTTONHEIGHT); 
	Rectangle Clear = new Rectangle(SmOFFSET, LgOFFSET + BUTTONHEIGHT, BUTTONWIDTH, BUTTONHEIGHT);
	Rectangle PickSize = new Rectangle(SmOFFSET, BUTTONHEIGHT * 3, BUTTONWIDTH, BUTTONHEIGHT);
	Rectangle Bullpen = new Rectangle(0, SCRHEIGHT - BULLPEN, 105, BUTTONHEIGHT);
	Rectangle Type = new Rectangle(SmOFFSET, 250, BUTTONWIDTH, BUTTONHEIGHT);
	Rectangle Harder = new Rectangle(SmOFFSET, 290, BUTTONWIDTH, BUTTONHEIGHT);

      Font smallFont = new Font("SansSerif", Font.BOLD, 12);
      Font RegFont = new Font("SansSerif", Font.BOLD, 16);
      Font BigFont = new Font("SansSerif", Font.BOLD, 24);
      Font SuperFont = new Font("SansSerif", Font.BOLD, 48);
	Font SuperFont2 = new Font("SansSerif", Font.BOLD, 56);

   public void init() 
   {
	setBackground(Color.white);
	addMouseListener(this);
	addKeyListener(this);
   }

   public void update(Graphics g)
   {
	paint(g);
   }
	
   public void paint(Graphics g)
   {
	// Add a Start command button
	g.setFont(smallFont);
	g.setColor(Color.lightGray);
	g.fill3DRect(SmOFFSET, SmOFFSET, BUTTONWIDTH, BUTTONHEIGHT, true);
	g.setColor(Color.black);
	g.drawRect(SmOFFSET - 2, SmOFFSET - 2, BUTTONWIDTH, BUTTONHEIGHT);
	g.drawString("Start", LgOFFSET, BUTTONHEIGHT); 

	// Add a Clear command button
	g.setFont(smallFont);
	g.setColor(Color.lightGray);
	g.fill3DRect(SmOFFSET, LgOFFSET + BUTTONHEIGHT, BUTTONWIDTH, BUTTONHEIGHT, true);
	g.setColor(Color.black);
	g.drawRect(SmOFFSET - 2, LgOFFSET + BUTTONHEIGHT - 2, BUTTONWIDTH, BUTTONHEIGHT);
	g.drawString("ReStart", LgOFFSET, 70); 

	// Add a Resize command button
	g.setFont(smallFont);
	g.setColor(Color.lightGray);
	g.fill3DRect(SmOFFSET, BUTTONHEIGHT * 3, BUTTONWIDTH, BUTTONHEIGHT, true);
	g.setColor(Color.black);
	g.drawRect(SmOFFSET - 2, (BUTTONHEIGHT * 3)- 2, BUTTONWIDTH, BUTTONHEIGHT);
	g.drawString("Set Size", LgOFFSET, 110); 

	// Add a Type command button
	g.setFont(smallFont);
	g.setColor(Color.lightGray);
	g.fill3DRect(SmOFFSET, 250, BUTTONWIDTH, BUTTONHEIGHT, true);
	g.setColor(Color.black);
	g.drawRect(SmOFFSET - 2, 248, BUTTONWIDTH, BUTTONHEIGHT);
	g.drawString("Set Type", LgOFFSET, 270); 

	// Add a Harder command button
	g.setFont(smallFont);
	g.setColor(Color.lightGray);
	g.fill3DRect(SmOFFSET, 290, BUTTONWIDTH, BUTTONHEIGHT, true);
	g.setColor(Color.black);
	g.drawRect(SmOFFSET - 2, 288, BUTTONWIDTH, BUTTONHEIGHT);
	g.drawString("Harder", LgOFFSET, 310); 

	// Add a bullpen structure
	g.drawString("Reinforcements", SmOFFSET, SCRHEIGHT - 40);
	g.setColor(Color.red);
	g.fillRect(1, SCRHEIGHT - BULLPEN, LgOFFSET, TinyOFFSET);
	g.fillRect(23, SCRHEIGHT - BULLPEN, LgOFFSET, TinyOFFSET);
	g.fillRect(45, SCRHEIGHT - BULLPEN, LgOFFSET, TinyOFFSET);
	g.fillRect(67, SCRHEIGHT - BULLPEN, LgOFFSET, TinyOFFSET);
	g.fillRect(89, SCRHEIGHT - BULLPEN, LgOFFSET, TinyOFFSET);
	g.fillRect(104, SCRHEIGHT - 29, TinyOFFSET, 28);
	g.setColor(Color.black);
	g.drawRect(0, SCRHEIGHT - BULLPEN, 109, TinyOFFSET);
	g.drawRect(104, SCRHEIGHT - 30, TinyOFFSET, 28);
	PaintBullpen(g);
	
	if (MazeType == 1)
	   PaintSquareMaze(g);
	else
	   PaintRoundMaze(g);
	AddSweepers(g);
	
	//Goal Box
	g.setColor(Color.red);
	g.fillRect(MedOFFSET, SCRHEIGHT/2, 50, 35);
	g.setColor(Color.black);
	g.drawString(" Goal", LgOFFSET, SCRHEIGHT/2 - TinyOFFSET);
	g.fillRect(MedOFFSET, SCRHEIGHT/2, 50, 3);
	g.fillRect(MedOFFSET, SCRHEIGHT/2, 3, 35);
	g.fillRect(MedOFFSET + 50, SCRHEIGHT/2, 3, 35);
	g.fillRect(MedOFFSET, SCRHEIGHT/2 + 35, 53, 3);
	g.setFont(BigFont);
	g.drawString(String.valueOf(Goal),22,SCRHEIGHT/2 + 27);
	g.setColor(Color.white);
	g.drawString(String.valueOf(Goal),23,SCRHEIGHT/2 + 28);
	
	//Move Box
	g.setFont(smallFont);
	g.setColor(Color.red);
	g.fillRect(80, SCRHEIGHT/2, 50, 35);
	g.setColor(Color.black);
	g.drawString(" Moves", 85, SCRHEIGHT/2 - TinyOFFSET);
	g.fillRect(80, SCRHEIGHT/2, 50, 3);
	g.fillRect(80, SCRHEIGHT/2, 3, 35);
	g.fillRect(80 + 50, SCRHEIGHT/2, 3, 35);
	g.fillRect(80, SCRHEIGHT/2 + 35, 53, 3);
	g.setFont(BigFont);
	g.drawString(String.valueOf(MoveCounter),88,SCRHEIGHT/2 + 27);
	g.setColor(Color.white);
	g.drawString(String.valueOf(MoveCounter),89,SCRHEIGHT/2 + 28);
	if(GameOver1.length() > 1)
	{
	   GameOverDisplay();
	}
   }

   private void GameOverDisplay()
   {
	OnOff += 1;
	Graphics g = getGraphics();
	Date now = new Date();
	rand = new Random(now.getTime());   
	int red =   Math.abs(rand.nextInt() % 256); 
	int green = Math.abs(rand.nextInt() % 256); 
	int blue =  Math.abs(rand.nextInt() % 256); 

	g.setColor(Color.white);
	g.fillRect(SIZESTART, 110, 370, 170);
	g.setColor(Color.black);
	g.fillRect(SIZESTART, 110, 370, 6);
	g.fillRect(SIZESTART, 110, 6, 170);
	g.fillRect(SIZESTART + 370, 110, 6, 173);
	g.fillRect(SIZESTART, 280, 370, 6);
	g.setFont(SuperFont);
	g.drawString(GameOver1, SIZESTART + 5, 180);

	g.setColor(Color.white);
	g.fillRect(SIZESTART + 10, 190, 360, 55);
	if(OnOff % 2 == 0)
	   g.setFont(SuperFont);
	else
	   g.setFont(SuperFont2);
	g.setColor(new Color(red,green,blue));
	g.drawString(GameOver2, SIZESTART + 10, 240);
	if(blinker == null)
	{
	   blinker = new Thread(this);
	   blinker.start();
	}
	if(threadSuspended == true)
	   blinker.resume();
   }	 
	
   public void run()
   {
	while(blinker != null)
	{
	   try
	   {
		Thread.sleep(delay);
	   }
	   catch(InterruptedException e){}
   	   GameOverDisplay();
	}
   }
	
   public void stop()
   {
	blinker = null;
   }	

   // Paint the Maze
   private void PaintSquareMaze(Graphics g)
   {
	// 0 = NO/NO DOOR/OUTSIDE   1 = YES/DOOR/INSIDE
	int TempX = TopX;
	int TempY = TopY;
	int a = 0;
	for (int y = 0; y < MazeHeight; y++)
 	{
	   for (int x = 0; x < MazeWidth; x++, a++)
 	   {
		if (NewMaze[a].Clean == 1)
		   g.setColor(Color.green);
		else
		   g.setColor(dirty);
		g.fillRect(TempX, TempY, RoomSize, RoomSize);
	      g.setColor(Color.black);
		if (NewMaze[a].Up == 0)
		   g.fillRect(TempX, TempY, RoomSize, 3);	
		else
		{
		   g.fillRect(TempX, TempY, Wall + 2, 2);	
		   g.fillRect(TempX + Wall * 3, TempY, Wall + 2, 2);
	      }
		if (NewMaze[a].Down == 0)
		   g.fillRect(TempX, TempY + RoomSize, RoomSize + 1, 3);
	      else
		{
		   g.fillRect(TempX, TempY + RoomSize, Wall + 2, 2);	
		   g.fillRect(TempX + Wall * 3,TempY+RoomSize,Wall + 2, 2);
	      }
		if (NewMaze[a].Left == 0)
		   g.fillRect(TempX, TempY, 3, RoomSize);
	      else
		{
		   g.fillRect(TempX, TempY, 2, Wall + 2);	
		   g.fillRect(TempX, TempY + Wall * 3, 2, Wall + 2);
	      }
		if (NewMaze[a].Right == 0)
		   g.fillRect(TempX + RoomSize, TempY, 3, RoomSize);
	      else
		{
		   g.fillRect(TempX + RoomSize,TempY, 2, Wall + 2);	
		   g.fillRect(TempX + RoomSize,TempY + Wall * 3,2,Wall + 2);
	      }
	   TempX += RoomSize;
	   }
	TempX = TopX;
	TempY += RoomSize;
	}
   }	

   private void PaintRoundMaze(Graphics g)
   {
	// 0 = NO/NO DOOR/OUTSIDE   1 = YES/DOOR/INSIDE
	int TempX = TopX;
	int TempY = TopY;
	int a = 0;
	for (int y = 0; y < MazeHeight; y++)
 	{
	   for (int x = 0; x < MazeWidth; x++, a++)
 	   {
		if (NewMaze[a].Clean == 1)
		   g.setColor(Color.green);
		else
		   g.setColor(dirty);
		g.fillOval(TempX, TempY, RoomSize - 3, RoomSize - 3);
		g.setColor(Color.black);
		g.drawOval(TempX, TempY, RoomSize - 3, RoomSize - 3);
		if(NewMaze[a].Down == 1)
		   g.fillRect(TempX + Wall*2 - 2, TempY + RoomSize - 3, 6, 8);
 		if(NewMaze[a].Right == 1)
		   g.fillRect(TempX + RoomSize - 3, TempY + Wall*2 - 2, 8, 6);
		if(NewMaze[a].Up == 1)
		   g.fillRect(TempX + Wall*2 - 2, TempY - 3, 6, 8);
 		if(NewMaze[a].Left == 1)
		   g.fillRect(TempX - 3, TempY + Wall*2 - 2, 8, 6);
		if(a == 0)
		   g.fillRect(TempX - 12, TempY + Wall*2 - 2, 8, 6);
	      TempX += RoomSize;
	   }
	TempX = TopX;
	TempY += RoomSize;
	}
   }	
 	
   private void AddSweepers(Graphics g)
   {
	g.clearRect(SweeperX, SweeperY, SWEEPER + 1, SWEEPER + 1);	
	g.setFont(smallFont);
	Enumeration enum = SweepHere.elements();
	while(enum.hasMoreElements())
	{
	   SweepGuy	Find = (SweepGuy)enum.nextElement();
	   if (Find.Nbr == ActiveSweeper)
		g.setColor(Color.yellow);
	   else
      	g.setColor(Color.cyan);
         g.fillRect(Find.X1, Find.Y1, SWEEPER, SWEEPER);	
	   g.setColor(Color.black);
	   g.drawRect(Find.X1, Find.Y1, SWEEPER, SWEEPER);
	   g.drawString(String.valueOf(Find.Nbr),Find.X1 + 8,Find.Y1 + 12);
	}
   }

   private void ActivateBullpen()
   {
	Graphics g = getGraphics();

	// Put the next sweeper from the bullpen to the front door    
	ActiveSweeper = NextSweeper;
	FrontDoor.Occupied = NextSweeper;
      NextSweeper = (NextSweeper < 6) ? NextSweeper + 1 : 0;		
	PaintBullpen(g);

	// Store the activated sweeper in the hashtable 
	SweepHere.put(String.valueOf(NextSweeper -1), new SweepGuy(SweeperX, SweeperY, NextSweeper -1));
	AddSweepers(g);
  }
		
   // Paint the set size area
   private void PaintSetSize()
   {
	Graphics g = getGraphics();
	g.setFont(smallFont);
	g.setColor(Color.lightGray);
	g.fillRect(0, SIZESTART, SIZEWIDTH, SIZEHEIGHT);
	g.setColor(Color.black);
	g.fillRect(0, SIZESTART, SIZEWIDTH, 3);
	g.fillRect(0, SIZESTART, 3, SIZEHEIGHT);
	g.fillRect(SIZEWIDTH, SIZESTART, 3, SIZEHEIGHT + 3);
	g.fillRect(0, SIZESTART + SIZEHEIGHT, SIZEWIDTH, 3);
	g.drawString("How many rooms wide?", SmOFFSET, 150);
	g.drawString("How many rooms high?", SmOFFSET, 200);
	// Paint both of the scroll bars / 2 = loop twice
	PaintSizeBars(2, 157);
   }
	
   private void PaintSizeBars(int Loops, int TempY)
   {
	Graphics g = getGraphics();
	g.setColor(Color.black);
	g.setFont(smallFont);
	int TempX = TinyOFFSET; 
	int BoxWidth;
	for(int i = 0; i < Loops; i++)
	{
	   for(int r = 3; r < 15; r++) 
	   { 	
		g.setColor(Color.white);
		BoxWidth = (r < 10)? 12 : 18;
		g.fillRect(TempX, TempY, BoxWidth, MedOFFSET);
		g.setColor(Color.black);
		g.drawRect(TempX, TempY, BoxWidth, MedOFFSET);
		g.drawString(String.valueOf(r), TempX + 2, TempY + 12);
            TempX += BoxWidth + 2;
  	   }	
	   TempY += 50;	
	   TempX = TinyOFFSET;			
	}
   }		

   private void PaintBullpen(Graphics g)
   {
	int TempX = TinyOFFSET;
	g.setFont(smallFont);

	// Clear the old bullpen
	g.setColor(Color.white);
	g.fillRect(TempX, SCRHEIGHT - 29, SIZEHEIGHT - 1, BULLPEN - 1);

	for(int i = NextSweeper; i < 7; i++)
	{
	   g.setColor(Color.cyan);
	   g.fillRect(TempX, SCRHEIGHT - 27, SWEEPER, SWEEPER);
	   g.setColor(Color.black);
	   g.drawRect(TempX, SCRHEIGHT - 27, SWEEPER, SWEEPER);
	   g.drawString(String.valueOf(i), TempX + 8, SCRHEIGHT - 13);
	   TempX += 25;
 	}
   }

   private void AddFrontDoor()
   {
	// Get Top left corner of the maze
	SweeperX = TopX -(SWEEPER + 12);
	SweeperY = TopY + Wall;
	int StartX = TopX - 1 + Wall;
	int StartY = TopY -1 + Wall;

	//Create the space outside the front door	
	FrontDoor.Clean = 1;
	FrontDoor.Occupied = 2;
	FrontDoor.Right = 1;

	//Put the first sweeper in the first room
	NewMaze[0].Clean = 1;
	NewMaze[0].Occupied = ActiveSweeper;

	// Put the two sweepers in the hashtable so they can be clicked
	SweepHere.put("1", new SweepGuy(StartX, StartY, 1));
	SweepHere.put("2", new SweepGuy(SweeperX, SweeperY, 2));
   }

// Build the Maze 

   // Determine the size of the maze - how many rooms and the
   // diameter of the rooms and walls	
   private void DetermineSize()
   {
	int MaxSide = (MazeHeight > MazeWidth)? MazeHeight:MazeWidth;
	RoomSize = (SCRHEIGHT - 20) / MaxSide;
	Wall = RoomSize / 4;
	if (RoomSize % 2 == 1)
	   RoomSize -= 1;
	MaxRooms = MazeHeight * MazeWidth;
	// Get Top left corner of the maze
	TopX = ((SCRWIDTH + MAZEOFFSET)-(MazeWidth * RoomSize)) / 2;
	TopY = (SCRHEIGHT -(MazeHeight * RoomSize)) / 2;
   }

   // Start to set up the maze
   private void SetMaze()
   {
	DetermineSize();
	// Create an array of rooms
	int InOut;
	int a = 0;
	int TempX = TopX;
	int TempY = TopY;
	for (int h = 0; h < MazeHeight; h++)
	{
	   for (int w = 0; w < MazeWidth; w++, a++)
	   {	
		if ((h == 0)&&(w == 0))
		   InOut = TLC;
		else if((h == 0)&&(w == (MazeWidth - 1))) 
		   InOut = TRC;
		else if((h == (MazeHeight - 1))&&(w == 0)) 
		   InOut = BLC;
		else if((h == (MazeHeight - 1))&&(w == (MazeWidth - 1)))
		   InOut = BRC;
		else if(h == 0) 
		   InOut = TOPEDGE;
		else if(w == 0)
		   InOut = LEFTEDGE;
		else if(w == (MazeWidth - 1)) 
		   InOut = RIGHTEDGE;
		else if(h == (MazeHeight - 1)) 
		   InOut = BOTTOMEDGE;
		else
		   InOut = INSIDE;
		NewMaze[a] = new Room(TempX, TempY, InOut);
		TempX += RoomSize;
	   }
	   TempX = TopX;
	   TempY += RoomSize;
	}
	// Set random left and up doors 
	for (int b = 0; b < MaxRooms; b++)
	{
	   if (NewMaze[b].RoomIO == INSIDE)
	   {
	      Date now = new Date();
		rand = new Random((b+2)*(b+3)* now.getTime());
		if (Math.abs(rand.nextInt() % 4) == 0) 
		   OpenUp(b);
	      else if (Math.abs(rand.nextInt() % 4) == 1) 
		   OpenLeft(b);
	      else if (Math.abs(rand.nextInt() % 4) == 2) 
		   OpenRight(b);
		else
		   OpenDown(b);	
	   }
	}
	CheckRooms();
	ConnectRooms();
	NewMaze[0].Left = 1;
	FrontDoor = new Room(TopX - 50, TopY, FRONTDOOR);
	AddFrontDoor();
	SetGoal();
	repaint();
   }	

   private void CheckRooms()
   {
	// Check for rooms without doors - randomly open a door
	for (int a = 0; a < MaxRooms; a++)
	{
	   if((NewMaze[a].Left + NewMaze[a].Right + NewMaze[a].Up + NewMaze[a].Down)== 0)
	   {
	      Date now = new Date();
            rand = new Random((a+2)*(a+3)* now.getTime());   
		int n = Math.abs(rand.nextInt()); 
   		switch(NewMaze[a].RoomIO) 
	      {
	 	   case INSIDE:
		         if ((n % 4) == 0) 
		            OpenUp(a);
	               else if ((n % 4) == 1) 
		            OpenLeft(a);
			   else if ((n % 4) == 2) 
		            OpenRight(a);
			   else
		            OpenDown(a);
			   break;		
	     	   case LEFTEDGE:
		   case TLC:
		         if ((n % 2) == 0) 
		            OpenRight(a);
	               else
		            OpenDown(a);
			   break;
		   case BLC:		
		   case BOTTOMEDGE:
		         if ((n % 2) == 0) 
		            OpenUp(a);
	               else
		            OpenRight(a);
			   break;
		   case TOPEDGE:
		   case TRC:
		         if ((n % 2) == 0) 
		            OpenDown(a);
	               else
		            OpenLeft(a);
			   break;
		   case RIGHTEDGE:
		   case BRC:
			   if ((n % 2) == 0) 
		            OpenUp(a);
	               else
		            OpenLeft(a);
			   break;
		}
	   }				
      }
   }

   private void MakeHarder()
   {
	int Counter = 1;
      Date now = new Date();
      rand = new Random(now.getTime());   
	int MaxSide = (MazeHeight > MazeWidth)? MazeHeight:MazeWidth;
	while(Counter <= MaxSide)
	{
	   int a = Math.abs((rand.nextInt()* Counter) % MaxRooms); 
	   if(OpenDown(a))
	      Counter += 1;
	   if(OpenLeft(a))
	      Counter += 1;
	   if(OpenUp(a))
	      Counter += 1;
	   if(OpenRight(a))
		Counter += 1;
	}
	SetGoal();
      repaint();	 		
   }

   private void ConnectRooms()
   {
      // Go thru maze twice(forwards and backwards)marking all
	// connected rooms - then check if all rooms are connected
	// If they're not, ramdomly open one door and check it again
	int Connected = 0;
	Trys += 1;
	NewMaze[0].Group = 50;
	for (int x = 0; x < MaxRooms; x++)
	   Connect(x);
	for (int y = MaxRooms - 1; y >= 0; y--)
	   Connect(y);
	for (int r = 0; r < MaxRooms; r++)
	{
	   if(NewMaze[r].Group == 50)
		Connected += 1;
	}
	if(Connected < MaxRooms - 1)
	{
	   for (int a = MazeWidth; a < (MaxRooms - MazeWidth); a++)
	   {
		if(NewMaze[a].RoomIO==INSIDE)
		{	
		   if(NewMaze[a - 1].Group + NewMaze[a].Group ==50)  
		   {
	   	      if (OpenLeft(a))
			   break;
		   }
		   if(NewMaze[a + MazeWidth].Group + NewMaze[a].Group ==50)  
		   {
	   	      if(OpenDown(a))
			   break;
		   }
		   if(NewMaze[a + 1].Group + NewMaze[a].Group==50) 
		   {
	   	      if(OpenRight(a))
			   break;
		   }
		   if(NewMaze[a - MazeWidth].Group + NewMaze[a].Group ==50)  
		   {
	   	      if (OpenUp(a))
			   break;
		   }
	     }
	   }
	   if((Trys < 40)&&(Connected < MaxRooms - 1))
	      ConnectRooms();
	} 
   }		

   private void Connect(int i)
   {
	if(NewMaze[i].Group == 50)
	{
	   if((NewMaze[i].Left == 1)&&(i != 0)) 
	      NewMaze[i - 1].Group = 50;  
	   if(NewMaze[i].Right == 1)
	      NewMaze[i + 1].Group = 50;  
	   if(NewMaze[i].Down == 1)
	      NewMaze[i + MazeWidth].Group = 50;  
	   if(NewMaze[i].Up == 1)
	      NewMaze[i - MazeWidth].Group = 50;  
	}
   }	
	
   private void SetGoal()
   {
	Goal = 0;
	int Adder;
	for (int r = 0; r < MaxRooms; r++)
	   Goal += NewMaze[r].Left + NewMaze[r].Right + NewMaze[r].Up + NewMaze[r].Down;
   }

   // Open doors
   private boolean OpenLeft(int b)
   {
	if((NewMaze[b].RoomIO == LEFTEDGE) || (NewMaze[b].RoomIO == TLC) 
|| (NewMaze[b].RoomIO == BLC))
 	   return false;
	NewMaze[b].Left = 1;
	NewMaze[b - 1].Right = 1;
	return true;
   }	
 
   private boolean OpenUp(int b)
   {
	if((NewMaze[b].RoomIO == TOPEDGE) || (NewMaze[b].RoomIO == TLC)
 || (NewMaze[b].RoomIO == TRC))
 	    return false;
	NewMaze[b].Up = 1;
	NewMaze[b - MazeWidth].Down = 1;
	return true;
   }	
 
   private boolean OpenRight(int b)
   {
	if((NewMaze[b].RoomIO == RIGHTEDGE) || (NewMaze[b].RoomIO == TRC) 
|| (NewMaze[b].RoomIO == BRC))
 	    return false;
      NewMaze[b].Right = 1;
	NewMaze[b + 1].Left = 1;
	return true;
   }	
 
   private boolean OpenDown(int b)
   {
	if((NewMaze[b].RoomIO == BOTTOMEDGE) || (NewMaze[b].RoomIO == BLC) 
|| (NewMaze[b].RoomIO == BRC))
 	    return false;
	NewMaze[b].Down = 1;
	NewMaze[b + MazeWidth].Up = 1;
	return true;
   }	
 	
// Move the sweepers
   private void MoveRight(String Sweeper)	
   {
	Room StartRoom = null;
	Room EndRoom = null;
	// Find the active sweeper - put him in a room
	SweepGuy Mover = (SweepGuy)SweepHere.get(Sweeper);
	// Check the front door first
	if((Mover.X1 > FrontDoor.RoomX)&&(Mover.X1 < TopX)&&(Mover.Y1 > FrontDoor.RoomY)&&(Mover.Y1 < (TopY + RoomSize))&& (NewMaze[0].Occupied == 0)) 	   
   	{
	   MoveCounter += 1;	
	   FrontDoor.Occupied = 0;
	   NewMaze[0].Occupied = ActiveSweeper;
	   NewMaze[0].Clean = 1;
	   SweepHere.remove(Sweeper);	
	   SweepHere.put(Sweeper, new SweepGuy(TopX - 1 + Wall, TopY - 1 + Wall, ActiveSweeper));	
	   //Calculate reprecussions of the move	
	   repaint();
	}
	else
	{
         int x = 0;
	   for(; x < MaxRooms; x++)
	   {
	      int FarX = NewMaze[x].RoomX + RoomSize;
	      int FarY = NewMaze[x].RoomY + RoomSize;
	      if((Mover.X1 > NewMaze[x].RoomX)&&(Mover.X1 < FarX)&&(Mover.Y1 > NewMaze[x].RoomY)&&(Mover.Y1 < FarY))  	   
   	      {
	         StartRoom = NewMaze[x];
	  	   EndRoom =(x ==(MaxRooms-1))? NewMaze[x]:NewMaze[x + 1];
	         break;
	      }
	   }
	   // Make sure the current room has a door on the right and the
	   // target room is unoccupied
	   if ((StartRoom.Right == 1)&&(EndRoom.Occupied == 0))
	   {
	      // Repaint the sweeper from the old room to the new one
	      SetRooms(StartRoom, EndRoom, Sweeper);
	      SweepHere.put(Sweeper, new SweepGuy(Mover.X1 + RoomSize, Mover.Y1, ActiveSweeper));	
	      //Calculate reprecussions of the move
		Dirty(x);	
	      repaint();
	   }
	}
   }		

   private void MoveLeft(String Sweeper)	
   {
	Room StartRoom = null;
	Room EndRoom = null;
	// Find the active sweeper - put him in a room
	SweepGuy Mover = (SweepGuy)SweepHere.get(Sweeper);
	int x = 0;
 	for(; x < MaxRooms; x++)
	{
	   int FarX = NewMaze[x].RoomX + RoomSize;
	   int FarY = NewMaze[x].RoomY + RoomSize;
	   if((Mover.X1 > NewMaze[x].RoomX)&&(Mover.X1 < FarX)&&(Mover.Y1 > NewMaze[x].RoomY)&&(Mover.Y1 < FarY))  	   
   	   {
	      StartRoom = NewMaze[x];
		EndRoom = (x == 0)? NewMaze[x] : NewMaze[x - 1];
	      break;
	   }
	}
	// Make sure the current room has a door on the left and the
	// target room is unoccupied
	if ((StartRoom.Left == 1)&&(EndRoom.Occupied == 0))
	{
	   // Repaint the sweeper from the old room to the new one
	   SetRooms(StartRoom, EndRoom, Sweeper);
	   SweepHere.put(Sweeper, new SweepGuy(Mover.X1 - RoomSize, Mover.Y1, ActiveSweeper));	
	   //Calculate reprecussions of the move	
	   Dirty(x);	
	   repaint();
	}
   }
		
   private void MoveUp(String Sweeper)	
   {
	Room StartRoom = null;
	Room EndRoom = null;
	// Find the active sweeper - put him in a room
	SweepGuy Mover = (SweepGuy)SweepHere.get(Sweeper);
	int x = 0;
 	for(; x < MaxRooms; x++)
	{
	   int FarX = NewMaze[x].RoomX + RoomSize;
	   int FarY = NewMaze[x].RoomY + RoomSize;
	   if((Mover.X1 > NewMaze[x].RoomX)&&(Mover.X1 < FarX)&&(Mover.Y1 > NewMaze[x].RoomY)&&(Mover.Y1 < FarY))  	   
   	   {
	      StartRoom = NewMaze[x];
		EndRoom =(x < MazeWidth)? NewMaze[x]:NewMaze[x-MazeWidth];
	      break;
	   }
	}
	// Make sure the current room has a door on the top and the
	// target room is unoccupied
	if ((StartRoom.Up == 1)&&(EndRoom.Occupied == 0))
	{
	   // Repaint the sweeper from the old room to the new one
	   SetRooms(StartRoom, EndRoom, Sweeper);
	   SweepHere.put(Sweeper, new SweepGuy(Mover.X1, Mover.Y1 - RoomSize, ActiveSweeper));	
	   //Calculate reprecussions of the move	
	   Dirty(x);	
	   repaint();
	}
   }		

   private void MoveDown(String Sweeper)	
   {
	Room StartRoom = null;
	Room EndRoom = null;
	// Find the active sweeper - put him in a room
	SweepGuy Mover = (SweepGuy)SweepHere.get(Sweeper);
	int x = 0;
 	for(; x < MaxRooms; x++)
	{
	   int FarX = NewMaze[x].RoomX + RoomSize;
	   int FarY = NewMaze[x].RoomY + RoomSize;
	   if((Mover.X1 > NewMaze[x].RoomX)&&(Mover.X1 < FarX)&&(Mover.Y1 > NewMaze[x].RoomY)&&(Mover.Y1 < FarY))  	   
   	   {
	      StartRoom = NewMaze[x];
		EndRoom = (x >= (MaxRooms - MazeWidth))? NewMaze[x] : NewMaze[x + MazeWidth];
	      break;
	   }
	}
	// Make sure the current room has a door on the bottom and the
	// target room is unoccupied
	if ((StartRoom.Down == 1)&&(EndRoom.Occupied == 0))
	{
	   // Repaint the sweeper from the old room to the new one
	   SetRooms(StartRoom, EndRoom, Sweeper);
	   SweepHere.put(Sweeper, new SweepGuy(Mover.X1, Mover.Y1 + RoomSize, ActiveSweeper));	
	   //Calculate reprecussions of the move	
	   Dirty(x);	
	   repaint();
	}
   }		

   private void SetRooms(Room StartRoom, Room EndRoom, String Sweeper)
   {
      MoveCounter += 1;	
	StartRoom.Occupied = 0;	
	EndRoom.Occupied = ActiveSweeper;
	EndRoom.Clean = 1;
	SweepHere.remove(Sweeper);	
   }	

   private void Dirty(int x)
   {
	// x is the room we just moved out of. Since it is unoccupied, if 
	// any connecting room is, its dirty.  
	int DirtyFlag = 0;

	// Check to see if there is a door first. That way we can't exceed 
	// the boundaries of the array by adding or subtracting to x.	
	{
	   if ((NewMaze[x].Left == 1)&&(x != 0))
	   {
	      if(NewMaze[x-1].Clean == 0) 
	      {
	         NewMaze[x].Clean = 0;	   
	         DirtyFlag = 1;
	      }
	   }
	   if (NewMaze[x].Right == 1)
	   {
	      if(NewMaze[x+1].Clean == 0) 
	      {
		   NewMaze[x].Clean = 0;	   
	   	   DirtyFlag = 1;
	      }
	   }
	   if (NewMaze[x].Up == 1)
	   {
	      if(NewMaze[x-MazeWidth].Clean == 0) 
	      {
	         NewMaze[x].Clean = 0;	   
	         DirtyFlag = 1;
	      }	
	   }
	   if (NewMaze[x].Down == 1)
	   {
	      if(NewMaze[x+MazeWidth].Clean == 0) 
	      {
	         NewMaze[x].Clean = 0;	   
	         DirtyFlag = 1;
	      }	
	   }
	}
	if(DirtyFlag == 1)
	{
	   // If we just dirtied the first room and the front door 
	   // is unoccupied the virus escapes and the game is over.
	   // If we just dirtied the first room and the front door 
	   // is blocked then no other rooms need to be inspected (we
	   // just moved to one of them and the other was already dirty)	 
	   if((x == 0)&&(NewMaze[0].Clean == 0))
	   {
	      if (FrontDoor.Occupied == 0)
		   Loser();
	   }
	   else
		DirtyIt(x); 
	} 
	else
	   Winner();
   }
	
   private void DirtyIt(int x)
   {
	// Now that we're dirty if any connecting room is unoccupied 
	// its dirty. Check to see if there is a door first. That way we 
	// can't exceed the boundaries of the array by adding or 
	// subtracting to x. If we've dirtied a room put it in a table
	// so we can check the reprecussions of that.	
	if (NewMaze[0].Clean == 0) 
	{
	   if (FrontDoor.Occupied == 0)
		Loser();
	}
	if ((NewMaze[x].Left == 1)&&(x != 0)) 
	   CleanIt(x - 1);
	if (NewMaze[x].Right == 1) 
	   CleanIt(x + 1);
	if (NewMaze[x].Up == 1) 
	   CleanIt(x - MazeWidth);
	if (NewMaze[x].Down == 1)
	   CleanIt(x + MazeWidth);
	if(DirtyRoom.isEmpty() == false)
	{
	   Enumeration enum = DirtyRoom.elements();
	   while(enum.hasMoreElements())
	   {
	      String Target = (String)enum.nextElement();
	   	DirtyRoom.remove(Target);
	      int j = Integer.parseInt(Target);
		DirtyIt(j);
	   } 	
	}
   }
  
   private void CleanIt(int x)
   {
	if((NewMaze[x].Occupied == 0)&&(NewMaze[x].Clean == 1))
	{
	   NewMaze[x].Clean = 0;
	   String IsDirty = String.valueOf(x); 		   
	   DirtyRoom.put(IsDirty, IsDirty);
	}	
   }			
   	
   private void Loser()
   {
	setBackground(dirty);
	Graphics g = getGraphics();
	g.clearRect(0, 0, SCRWIDTH, SCRHEIGHT);
	GameOver1 = " Sorry Charlie,"; 
	GameOver2 = "   You Lose!"; 
	repaint();
   }	
		
   private void Winner()
   {
	for (int a = 0; a < MaxRooms; a++)
      {
	   if (NewMaze[a].Clean == 0)
		return;
	}
	GameOver1 = " Congatulations";
	GameOver2 = "     You Win!"; 
	GameOverDisplay();
   }

   private void NewGame()
   {
	if(blinker != null) 
	{
    	   blinker.suspend();
   	   threadSuspended = true;
	}
	setBackground(Color.white);
	Graphics g = getGraphics();
	g.clearRect(0, 0, SCRWIDTH, SCRHEIGHT);
	Trys = 0;
	MoveCounter = 0;
	GameOver1 = "";
	GameOver2 = "";	
      setBackground(Color.white);
	NextSweeper = 3;
	ActiveSweeper = 1;
      SweepHere.clear();
   } 	

// The user has clicked the applet. Figure out where and what they
// want.
   public void mouseReleased(MouseEvent e)
   {	
	int MEx = e.getX();
	int MEy = e.getY();
	Graphics g = getGraphics();
	g.setColor(Color.black);
	g.setFont(smallFont);

	// User pressed the Pick Size button
	if(PickSize.contains(MEx, MEy))
	{
	   if(blinker != null) 
	   {
    	      blinker.suspend();
   	      threadSuspended = true;
	   }
	   SizeSw = true;
	   PaintSetSize();	
	}

	// User pressed the Start button
	if(BuildIt.contains(MEx, MEy))
	{
	   NewGame();
	   SizeSw = false;
	   SetMaze();
	}

	// User pressed the Clear button
	if(Clear.contains(MEx, MEy))
	{
	   NewGame();
	   for (int i = 0; i < MaxRooms; i++)
	   {
		NewMaze[i].Clean = 0; 
		NewMaze[i].Occupied = 0; 
	   }
	   AddFrontDoor();
	   repaint();	 		
	}

	// User pressed the Type button
	if(Type.contains(MEx, MEy))
	   MazeType = (MazeType == 1)? 0 : 1;		 		

	// User pressed the Harder button
	if(Harder.contains(MEx, MEy))
	{
	   NewGame();
	   for (int i = 0; i < MaxRooms; i++)
	   {
		NewMaze[i].Clean = 0; 
		NewMaze[i].Occupied = 0; 
	   }
	   AddFrontDoor();
	   MakeHarder();
	}

	// User pressed one of the size buttons
	if (SizeSw == true)
	{
	   int TempX = TinyOFFSET; 
	   int TempY = 157;
	   int BoxWidth;		
	   outer: for(int i = 0; i < 2; i++)
	   {
	      for(int r = 3; r < 15; r++) 
	      { 	
		   BoxWidth =(r < 10)? 12 : 18; 
       	   if((MEx > TempX) && (MEx < (TempX + BoxWidth)) &&  	
		     (MEy > TempY) && (MEy < (TempY + MedOFFSET))) 
		   {
		      // Repaint only one of the scroll bars / 1 = loop once
		      PaintSizeBars(1, TempY);
		      g.setColor(Color.red);
		      g.fillRect(TempX, TempY, BoxWidth, MedOFFSET);
		      g.setColor(Color.black);
		      g.drawRect(TempX, TempY, BoxWidth, MedOFFSET);
		      g.drawString(String.valueOf(r),TempX + 2,TempY + 12);
		      if (i == 0)
		   	   MazeWidth = r;
		      else 
			   MazeHeight = r;		 	
		      break;
		   }
        	   TempX += BoxWidth + 2;
  	      }	
	      TempY += 50;	
	      TempX = TinyOFFSET;			
	   }
	}

	// user has clicked the bullpen
	if(Bullpen.contains(MEx, MEy))
	{
	   // If the front door is empty and there are reinforcements 
	   // in the bullpen bring out one - make it the active 
	   // sweeper - subtract one from the bullpen and repaint it
	   if((FrontDoor.Occupied == 0)&&(NextSweeper > 0))
		ActivateBullpen();
	}

	// See if user has clicked on a sweeper
	Enumeration enum = SweepHere.elements();
	while(enum.hasMoreElements())
	{
	   SweepGuy	Find = (SweepGuy)enum.nextElement();
	   if((MEx > Find.X1) && (MEx < Find.X2) &&  	
		(MEy > Find.Y1) && (MEy < Find.Y2))
	   {
		// we found our sweeper - turn it yellow
		// turn current 'active sweeper' blue
		ActiveSweeper = Find.Nbr;
	      AddSweepers(g);
		break;
   	   }
	}
   }

   public void keyPressed(KeyEvent e) 
   {
	if(GameOver1.length() > 1)
	   return;
	String Sweeper = String.valueOf(ActiveSweeper);
	int key = e.getKeyCode();
	Graphics g = getGraphics();

	// if the number of the nextsweeper is pressed
	if((key == 48 + NextSweeper)||(key == 96 + NextSweeper))
	{
	   if((FrontDoor.Occupied == 0)&&(NextSweeper > 0))
	      ActivateBullpen();
	}
	// if the number 1 - 6 is pressed (top or number pad)
	else if ((key > 48)&&(key < 55))
	{
         SweepGuy Find =(SweepGuy)SweepHere.get(e.getKeyText(key));
	   ActiveSweeper = Find.Nbr;
	   AddSweepers(g);
      }
	else if ((key > 96)&&(key < 103)) 
	{
	   String NumPad = e.getKeyText(key);
         SweepGuy Find =(SweepGuy)SweepHere.get(NumPad.substring(7));
	   ActiveSweeper = Find.Nbr;
	   AddSweepers(g);
      }
	// If an arrow key or a 'D', 'U', 'L', 'R' key has been pressed
	if ((key == 38)||(key == 85)) 
	   MoveUp(Sweeper);
	if ((key == 40)||(key == 68)) 
	   MoveDown(Sweeper);
	if ((key == 37)||(key == 76)) 
	   MoveLeft(Sweeper);	
 	if ((key == 39)||(key == 82)) 
	   MoveRight(Sweeper);	
   }

   public void keyReleased(KeyEvent e) {}

   public void keyTyped(KeyEvent e) {}

   public void mousePressed(MouseEvent e) {}

   public void mouseClicked(MouseEvent e) {}

   public void mouseEntered(MouseEvent e) {}

   public void mouseExited(MouseEvent e) {}

   public void destroy()
   {
      removeMouseListener(this);
	removeKeyListener(this);
	blinker = null;
   }
   
   public String getAppletInfo() 
   {
	return "Sweeps by Jay Lipp";
   }

   class SweepGuy
   {
	public int X1, Y1, X2, Y2, Nbr;
	
	public SweepGuy(int StartX, int StartY, int nbr)
	{
	   X1 = StartX;
	   Y1 = StartY;
	   X2 = StartX + SWEEPER;
	   Y2 = StartY + SWEEPER;
	   Nbr = nbr;
	}
   }
	 	
   class Room  
   {
	public int RoomX, RoomY, Left, Right, Up, Down; 
	public int Clean, Occupied, RoomIO, Group;

	public Room(int x, int y, int InOut)
	{
	   RoomX = x;
	   RoomY = y;
	   Clean = 0;
	   Occupied = 0;
	   Left = 0;
	   Right = 0;
	   Up = 0;
	   Down = 0;	  		
	   RoomIO = InOut; 
	   Group = 0;	
	}
   }
}
