Friday 31 March 2017

Scrabblet: The Source Code - Java Tutorials ( Page 5 of 6 )

dragLetter( )
The dragLetter( ) method is handled differently than the other mouse-related events. This is mainly due to performance considerations. The goal is to have as smooth an interaction with the user as possible. dragLetter( ) goes to some length to compute the bounding box of where the tile was before this drag plus where it is now. It then directly calls paint(getGraphics( )). This is nonstandard Java applet programming, but it performs much more reliably.

    private void dragLetter(int x, int y) {
      if (pick != null) {
        int ox = pick.x;
        int oy = pick.y;
        pick.move(x + dx, y + dy);
        x0 = Math.min(ox, pick.x);
        y0 = Math.min(oy, pick.y);
        w0 = pick.w + Math.abs(ox - pick.x);
        h0 = pick.h + Math.abs(oy - pick.y);
        paint(getGraphics());
      }
    }

mousePressed( )
In the following code fragment, notice that MyMouseAdapter is an inner class that extends MouseAdapter. It overrides the mousePressed( ) and mouseReleased( ) methods.

The mousePressed( ) method invokes the selectLetter( ) method to do the necessary processing. The x and y coordinates of the current mouse position are obtained from the argument supplied to the mousePressed( ) method.

    class MyMouseAdapter extends MouseAdapter {
      public void mousePressed(MouseEvent me) {
        selectLetter(me.getX(), me.getY());
      }

mouseReleased( )
The mouseReleased( ) method invokes the dropLetter( ) method to do the necessary
processing. The x and y coordinates of the current mouse position are obtained from
the argument supplied to the mouseReleased( ) method.

      public void mouseReleased(MouseEvent me) {
        dropLetter(me.getX(), me.getY());
      }
    }

mouseDragged( )
In the following code fragment, notice that MyMouseMotionAdapter is an inner class that extends MouseMotionAdapter. It overrides the mouseDragged( ) method. The mouseDragged( ) method invokes the dragLetter( ) method to do the necessary processing. The x and y coordinates of the current mouse position are obtained from the argument supplied to the mouseDragged( ) method.

    class MyMouseMotionAdapter extends MouseMotionAdapter {
      public synchronized void mouseDragged(MouseEvent me) {
        dragLetter(me.getX(), me.getY());
      }
    }
  }


Bag.java

The Bag class is very clean compared with Board. It is a simple abstraction for the bag of letters. When you create a Bag, you pass in a random seed, which allows you to create two bags that are random but the same by passing in the same random seed. The random number generator is stored in rand. There are two somewhat strange arrays of integers, named letter_counts and letter_points. Both arrays are 27 slots long. They represent the blank tile in slot 0, and A through Z in 1 through 26. The letter_counts array says how many of each letter are in a full bag. For example, letter_counts[1] is 9, which says there are nine A tiles in the bag. Similarly, the letter_points array maps each letter to its point value. The A tiles are worth only 1 point, and the lone Z is worth 10. There are 100 letters stored in the array called letters. The number of letters actually left in the bag during game play is stored in n.

  import java.util.Random;

  class Bag {
    private Random rand;
    private int letter_counts[] = {
      2, 9, 2, 2, 4, 12, 2, 3, 2, 9, 1, 1, 4, 2,
      6, 8, 2, 1, 6, 4, 6, 4, 2, 2, 1, 2, 1
    };
    private int letter_points[] = {
      0, 1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3,
      1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10
    };
    private Letter letters[] = new Letter[100];
    private int n = 0;

Bag( )
The Bag constructor takes the seed and makes a Random object out of it. It then scans through the letter_counts array, making the right number of new Letter objects, being careful to replace the blank tile with an asterisk. It then calls putBack( ) for each letter, to put them in the bag.

    Bag(int seed) {
      rand = new Random(seed);
      for (int i = 0; i < letter_counts.length; i++) {
        for (int j = 0; j < letter_counts[i]; j++) {
          Letter l = new Letter(i == 0 ? '*' : (char)('A' + i - 1),
                                letter_points[i]);
          putBack(l);
        }
      }
    }

takeOut( )
This next method is slightly clever and a little inefficient, but in a noncritical way. takeOut( ) picks a random number between 0 and n –1. It then extracts the letter at that offset from the letters array. It closes the hole over that slot in letters using System.arraycopy( ). Then it decrements n and returns the letter.

    synchronized Letter takeOut() {
      if (n == 0)
        return null;
      int i = (int)(rand.nextDouble() * n);
      Letter l = letters[i];
      if (i != n - 1)
        System.arraycopy(letters, i + 1, letters, i, n - i - 1);
      n--;
      return l;
    }

putBack( )
The putBack( ) method is used by the constructor to put the tiles in the bag originally. It could also be used by a future game enhancement that would let players trade in tiles they were unhappy with in exchange for losing a turn. It simply puts the letter back at the end of the array.

    synchronized void putBack(Letter l) {
      letters[n++] = l;
    }
  }


Letter.java

The Letter class is fairly clean in that it doesn’t know anything about the game or the board. It merely encapsulates the position and visual rendering of a single letter. It uses several static variables to hold information about fonts and sizes. This is done so that the applet doesn’t end up with 100 fonts in memory at once. This has the side effect that a browser page cannot contain two instances of the Scrabblet applet if they each have different sizes. The second one to initialize will overwrite the values in these static variables.

The w and h variables hold the constant width and height of every letter. The font and smfont variables are the AWT font objects for the big letter and the smaller point value. The ints y0 and ys0 store the offset of the baseline of the letter and the points, respectively. A few constants are provided to be passed back into paint( ) to describe which color state to paint in: NORMAL, DIM, and BRIGHT mode.

  import java.awt.*;

  class Letter {
    static int w, h;
    private static Font font, smfont;
    private static int y0, ys0;
    private static int lasth = -1;
    static final int NORMAL = 0;
    static final int DIM = 1;
    static final int BRIGHT = 2;

colors[ ], mix( ), gain( ), and clamp( )
The colors array is initialized statically with nine color objects—three sets of three colors. The mix( ) method is used to take a set of RGB values like 250, 220, 100 and turn them into three colors, which can be used to provide 3-D–like highlights and lowlights. The mix( ) method calls on gain( ) to boost or decimate the brightness of a given color and calls on clamp( ) to make sure it remains in the legal range.

    private static Color colors[][] = {
      mix(250, 220, 100), // normal
      mix(200, 150, 80), // dim
      mix(255, 230, 150) // bright
    };

    private static Color mix(int r, int g, int b)[] {
      Color arr[] = new Color[3];

      arr[NORMAL] = new Color(r, g, b);
      arr[DIM] = gain(arr[0], .71);
      arr[BRIGHT] = gain(arr[0], 1.31);
      return arr;
    }
    private static int clamp(double d) {
      return (d < 0) ? 0 : ((d > 255) ? 255 : (int) d);
    }
    private static Color gain(Color c, double f) {
      return new Color(
        clamp(c.getRed() * f),
        clamp(c.getGreen() * f),
        clamp(c.getBlue() * f));
    }

Instance Variables
The valid flag is used to make sure that all of the sizing variables are set up exactly once, the first time this Letter is painted. There are several variables cached here to keep from having to do lots of computation each time the applet paints—such as, x0, w0, xs0, ws0, and gap—which are all explained in the following comments. The tile Point object is used to remember which square on the 15×15 board this Letter is on. If this variable is null, then the Letter is not on the board. The x,y pair is used to exactly locate the Letter.

    private boolean valid = false;

    // quantized tile position of Letter. (just stored here).
    private Point tile = null;
    int x, y; // position of Letter.
    private int x0;        // offset of symbol on tile.
    private int w0;        // width in pixels of symbol.
    private int xs0;       // offset of points on tile.
    private int ws0;       // width in pixels of points.
    private int gap = 1;   // pixels between symbol and points.

Letter( ), getSymbol( ), and getPoints( )
The symbol is a string that holds the letter displayed, and points is the point value of this letter. These are both initialized by the only constructor and returned by the wrapper methods getSymbol( ) and getPoints( ), respectively.

    private String symbol;
    private int points;
      
    Letter(char s, int p) {
      symbol = "" + s;
      points = p;
    }
    String getSymbol() {
      return symbol;
    }

    int getPoints() {
      return points;
    }

move( ), remember( ), and recall( )
The move( ) method is used to tell this tile where to draw. The remember( ) method, however, is more complicated. It can be called with a null, which means that this tile should “forget” where it was. This indicates that the letter is not in play. Otherwise, it tells which coordinate on the board this letter is occupying. This state is inspected by a call to recall( ).

    void move(int x, int y) {
      this.x = x;
      this.y = y;
    }

    void remember(Point t) {
      if (t == null) {
        tile = t;
      } else {
        tile = new Point(t.x, t.y);
      }
    }

    Point recall() {
      return tile;
    }

resize( )
The resize( ) method is called once by the board in order to tell every letter how big to be. Remember, w and h are static, so this affects all Letter instances at once.

    static void resize(int w0, int h0) {
      w = w0;
      h = h0;
    }

No comments:

Post a Comment