// PROGRAM LISTING
// _________________________________________________________________________
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
/**
 * Launcher.java: a Mastermind (or Pico-Fermi) game using AWT.
 * Written by MarkF, May 2001.
 * Assignment 1 Part 2, CPG341 Programming in the Small.
 *
 * Works as both an Application and an Applet without modification.
 * Developed in Win98 using Notepad and Sun Java 2 SDK, SE Version 1.3.0.
 * Rules of Mastermind available in the HTML.
 *
 * -  Application: java Launcher
 * 
-  Applet: applet code="Launcher.class" width=120 height=40
 * 
-  Compile: javac Launcher.java
 * 
-  Javadoc: javadoc -private -author Launcher.java
 * 
-  Jar Tool: jar cmf0 mainClass.txt mmind.jar *.class
 * 
-  mainClass.txt: "Main-Class: Launcher"
 * 
-  Jar Applet:
 *      applet code="Launcher.class" archive="mmind.jar" width=120 height=40
 *
* The Launcher class instantiates Board objects to start a new game.
 *
 * @since JDK1.0
 * @author MarkF (008095880@00.humber.ac.uk).
 * @see Board
 */
public class Launcher extends Applet implements ActionListener
{
  // Variables
  private Button newButton;
  // Constructors
  public Launcher() { }
  // Methods
  /** Here is the Application's main(). */
  public static void main( String[] args ) {
    Board frame = new Board( 323,590 ); // construct a Board object
    frame.inAnApplet = false;
  } // main()
  /** Here is the Applet's init(). */
  public void init() {
    setBackground( new Color ( 127,151,175 ) );
    newButton = new Button( "    New Game    " ); // button
    newButton.addActionListener( this ); // assign the listener
    add( newButton ); // add the component
  } // init()
  /** Called when the Applet's button is pressed. */
  public void actionPerformed( ActionEvent e ) {
    Board frame = new Board( 323,611 ); // construct a Board object
  } // actionPerformed();
} // End class Launcher
/**
 * The Board class paints and plays a Mastermind board on screen.
 *
 * @since JDK1.0
 * @author MarkF (008095880@00.humber.ac.uk).
 * @see Launcher
 * @see Peg
 * @see MessageBox
 */
class Board extends Frame implements MouseListener
{
  // Variables
  private static final int
    HOLES = 4, OFX[] = {230,250,230,250}, OFY[] = {546,546,566,566},
    SECRET = 0, SCORE = 10, BUTTONS1 = 21, BUTTONS2 = 22;
  /** Peg row: 0 is 'SECRET CODE', 1-10 is Turns, 11-20 is Score Pins,
      21 is coloured button pegs 1-4, 22 is button pegs 5-8. */
  private Peg peg[][] = new Peg[23][HOLES]; // array of Peg objects
  private int code[] = new int[HOLES]; // the code you're guessing
  private int pegX = 0; // your current Peg
  private int turn = 1; // your current turn
  private int x, y, pegUpdate;
  private boolean isUpdate = true, isPlaying = true, isRowFull = false;
  public static boolean inAnApplet = true; // modified externally
  // Constructors
  public Board( int frameWidth,int frameHeight ) { 
    setTitle( "Mastermind" ); // set the frame title
    setSize( frameWidth,frameHeight ); // set the frame size
    setResizable( false ); // user can't resize the frame
    // initialize Pegs
    for ( y = 0; y <= SCORE; y++ )
      for ( x = 0; x < HOLES; x++ ) {
        peg[y][x] = new Peg( 20,0 ); // 'SECRET CODE' and Board Pegs
        peg[y][x].setPosition( x * 50 + 33,556 - y * 50 );
        peg[y+SCORE][x] = new Peg( 8,0 ); // Score Pins
        peg[y+SCORE][x].setPosition(OFX[x],OFY[x] - y * 50);
        } // end for
    for ( x = 0; x < HOLES; x++ ) { // Coloured button Pegs in top-right
      peg[BUTTONS1][x] = new Peg( 20,x + 1 );
      peg[BUTTONS1][x].setPosition( 298,x * 50 + 56);
      peg[BUTTONS2][x] = new Peg( 20,x + 5 );
      peg[BUTTONS2][x].setPosition( 298,x * 50 + 256 );
      } // end for
    // start Game
    randomCode(); // fill code with a random code
    // To close the window on the X
    addWindowListener( new WindowAdapter() { // register a listener
      public void windowClosing(WindowEvent e) { // construct a listener
        if (inAnApplet)
          dispose();
        else
          System.exit(0);
      } });
    addMouseListener(this); // add mouse listener
    setVisible( true ); // ask it to become visible on screen
  } // Board()
  // Methods
  /** Custom painting code, called automatically by the Java system. */
  public void paint( Graphics g ) {
    g.setColor( new Color( 51,51,102 ) ); // draws the board itself
    g.fillRoundRect( 4,27,264,559,15,15 );
    g.setColor( new Color( 255,204,0 ) ); // draws the 'SECRET CODE' area
    g.fill3DRect( 10,533,197,53,true );
    g.setFont( new Font( "SansSerif",Font.BOLD,26 ) );
    g.setColor( Color.yellow );
    g.drawString( "SECRET CODE",15,569 );
    g.setColor( new Color ( 127,151,175 ) ); // draws the Peg selection area
    g.fill3DRect( 269,27,50,459,true );
    g.setColor( Color.green ); // draws the 'Go' button
    g.fillOval( 278,436,40,40);
    g.setColor( Color.black );
    g.drawOval( 278,436,40,40);
    g.setColor( Color.green.darker() );
    g.drawString( "Go",280,466 );
    // draws the Pegs
    for ( y = 0; y < peg.length; y++ ) {
      peg[y][0].paint(g); peg[y][1].paint(g);
      peg[y][2].paint(g); peg[y][3].paint(g);
      } // end for
  } // paint()
  /** Overrides the update() method, see repaintPegs(). */
  public void update( Graphics g ) {
    if ( isUpdate ) {
      paint( g ); // update everything
      }
    else { // else, update only the Pegs flagged by repaintPegs()
      isUpdate = true;
      peg[pegUpdate][0].paint( g ); peg[pegUpdate][1].paint( g );
      peg[pegUpdate][2].paint( g ); peg[pegUpdate][3].paint( g );
      }
  } // update()
  /** Repaints Pegs, Java system calls update(). */
  void repaintPegs( int pegUpdate ) {
    this.pegUpdate = pegUpdate;
    isUpdate = false;
    repaint(); // schedule for update()
  } // repaintPegs()
  /** Called just after a mouse button press (Mouse Listener). */
  public void mousePressed(MouseEvent e) {
    Point mouse = new Point(); // holds mouse's current location
    mouse = e.getPoint(); // get its coordinates
    if ((mouse.y >= 533) && (mouse.x <= 207)) // is the 'SECRET CODE' area?
      revealCode();
    else
      if (isPlaying)
        if (mouse.x > 270) { // is the peg selection area?
          int button = 1 + (int)((mouse.y - 32) / 50); //translate mouse.y
          if ((button >= 1) && (button <= 8)) // is a colored Peg?
            fillHole( button );
          else
            if (button == 9) // else, is the 'Go' button?
              if (isRowFull)
                Go();
              else {
                MessageBox message = new MessageBox( this,"Mastermind Help",
                " You must first fill in a row with coloured pegs!" );
        }     }
        else { // is playing and are unknown mouse co-ordinates?
          MessageBox message = new MessageBox( this,"Mastermind Help",
            " Fill in a row with coloured pegs and then click on 'Go'." );
        }
      else { // is not the 'SECRET CODE' area and is not playing?
        MessageBox message = new MessageBox( this,"Mastermind",
          " Sorry... this game has stopped playing!" );
      }
  } // mousePressed()
  /** These four interface methods are not used but must be defined. */
  public void mouseClicked(MouseEvent e) {}
  public void mouseReleased(MouseEvent e){}
  public void mouseEntered(MouseEvent e) {}
  public void mouseExited(MouseEvent e)  {}
  /** Picks the current Peg, called from mousePressed(). */
  void fillHole( int newC ) {
    peg[turn][pegX].setColor( newC );
    repaintPegs( turn );
    pegX = (pegX + 1) % HOLES; // next Peg or first Peg
    if (pegX == 0)
      isRowFull = true;
  } // fillHole()
  /** 'Go' button pressed, called from mousePressed().
       Scoring based on www.students.cs.uu.nl/~smotterl/mmind */
  void Go() {
    int junkPegs[] = new int[HOLES], junkCode[] = new int[HOLES];
    int pegCount = 0, pico = 0;
    for ( x = 0; x < HOLES; x++ ) { // copy, as we will overwrite
      junkPegs[x] = peg[turn][x].getColor();
      junkCode[x] = code[x];
    }
    // Red pins, right colour, right place
    for ( x = 0; x < HOLES; x++ )
      if (junkPegs[x]==junkCode[x]) {
        peg[turn+SCORE][pegCount].setColor( 1 );
        pegCount++;
        pico++;
        junkPegs[x] = 98; // trash it, so it won't...
        junkCode[x] = 99; // match again
      }
    // Yellow pins, right colour, wrong place
    for ( x = 0; x < HOLES; x++ )
      for ( y = 0; y < HOLES; y++ )
        if (junkPegs[x]==junkCode[y]) {
          peg[turn+SCORE][pegCount].setColor( 2 );
          pegCount++;
          junkPegs[x] = 98; // trash it, so it won't...
          junkCode[y] = 99; // match again
          y = HOLES; // abort inner loop
        }
    repaintPegs( turn+SCORE ); // updates the score
    if ( pico == HOLES ) { // has won?
      MessageBox message = new MessageBox( this,"Mastermind",
        " Congratulations! Hey, you won. Good job!" );
      isPlaying = false; // halt gameplay
    }
    else
      if ( turn >= 10 ) { // else, has lost?
        MessageBox message = new MessageBox( this,"Mastermind",
          " Game over, you lose. Click on the 'SECRET CODE'." );
        isPlaying = false; // halt gameplay
      }
      else { // else, start the next row of Pegs
        pegX = 0;
        isRowFull = false;
        turn++;
        }
  } // Go()
  /** Shows the 'SECRET CODE', called from mousePressed(). */
  void revealCode() {
    for ( x = 0; x < HOLES; x++ )
      peg[SECRET][x].setColor( code[x] );
    repaintPegs( SECRET );
  } // revealCode()
  /** Generates a random 'SECRET CODE', called from init(). */
  void randomCode() {
    for ( x = 0; x < HOLES; x++ )
      code[x] = 1 + (int)(Math.random() * 8);
  } // randomCode()
} // End class Board
/**
 * The Peg class is a blueprint for a Peg object.
 *
 * @since JDK1.0
 * @author MarkF (008095880@00.humber.ac.uk).
 *         Based on Circle class in Chapter 38 of...
 *         Bradley Kjell's "Introduction to Computer Science using Java".
 * @see Board
 */
class Peg
{
  // Variables
  private int color, x, y, radius;
  private static final Color PALETTE[] = {
    Color.black,Color.red,Color.yellow,Color.green,Color.orange,
    Color.pink,Color.magenta,Color.blue,Color.white,Color.lightGray };
  // Constructors
  public Peg()
  { x = 0; y = 0; radius = 0; color = 0; }
  public Peg( int radius,int color )
  { x = 0; y = 0; this.radius = radius; this.color = color; }
  // Methods
  public void paint( Graphics g ) {
    int ulX = x-radius ; // X of upper left corner of rectangle
    int ulY = y-radius ; // Y of upper left corner of rectangle
    int d14 = radius >>1; // one-quarter diameter
    int d34 = radius+d14; // three-quarter diameter
    if (color > 0) {
      g.setColor( PALETTE[color].darker() );
      g.fillOval( ulX,ulY,2*radius,2*radius );
      g.setColor( PALETTE[color] );
      g.fillOval( ulX+d14-1,ulY+d14-1,d34,d34 );
      g.setColor( Color.white );
      g.fillOval( x,y,d14,d14);
      }
    else {
      g.setColor( Color.lightGray );
      g.drawOval( ulX,ulY,2*radius-1,2*radius-1 );
      }
    g.setColor( Color.black );
    g.drawOval( ulX,ulY,2*radius,2*radius );
  } // paint()
  /** Changes the center of the peg to a new X and Y. */
  public void setPosition( int newX,int newY ) {
    x = newX ;
    y = newY ;
  } // setPosition()
  /** Changes the radius of the peg. */
  public void setRadius( int newR ) {
    radius = newR;
  } // setRadius()
  /** Changes the color of the peg. */
  public void setColor( int newC ) {
    color = newC;
  } // setColor()
  /** Returns the color of the peg. */
  public int getColor() {
    return color;
  } // getColor()
} // End class Peg
/**
 * The MessageBox class implements a message dialog box.
 *
 * @since JDK1.0
 * @author MarkF (008095880@00.humber.ac.uk).
 *         Based on Java How to Program, p. 616.
 * @see Board
 */
class MessageBox extends Dialog implements ActionListener
{
  // Variables
  private Button okButton;
  // Constructors
  public MessageBox( Frame f,String title,String message ) {
    super( f,title,true ); // construct a Dialog box
    setBackground( new Color ( 127,151,175 ) );
    setResizable( false ); // user can't resize the frame
    add( new Label( message ), BorderLayout.NORTH );
    okButton = new Button( "    OK    " ); // button
    okButton.addActionListener( this ); // assign the listener
    add( okButton, BorderLayout.EAST ); // add the component
    pack(); // pack layout
    setVisible( true ); // ask it to become visible
  } // MessageBox()
  // Methods
  /** Called when the Dialog's button is pressed. */
  public void actionPerformed( ActionEvent e )
  { dispose(); }
} // End class MessageBox