home *** CD-ROM | disk | FTP | other *** search
/ Magazyn Internet 2001 November / MICD2001_11_NR1.iso / Www / Anglomaniacy / WordSearch.java < prev    next >
Text File  |  2001-09-26  |  34KB  |  1,234 lines

  1. /******************************************************************************
  2.  
  3.   Word Search, Copyright 1999 by Mike Hall. See accompanying documentation and
  4.   license agreement.
  5.  
  6.   <applet code="WordSearch.class" width=w height=h>
  7.   <param name="screencolors" value="foreground,background,border">
  8.   <param name="buttoncolors" value="foreground,background">
  9.   <param name="gridcolors" value="foreground,background,found,highlight">
  10.   <param name="listcolors" value="foreground,background,found">
  11.   <param name="screenfont" value="name,style,size">
  12.   <param name="buttonfont" value="name,style,size">
  13.   <param name="gridfont" value="name,style,size">
  14.   <param name="listfont" value="name,style,size">
  15.   <param name="gridsize" value="rows,cols,size">
  16.   <param name="files" value="url,url,url...">
  17.   </applet>
  18.  
  19. ******************************************************************************/
  20.  
  21. import java.awt.*;
  22. import java.io.*;
  23. import java.net.*;
  24. import java.util.*;
  25. import java.applet.Applet;
  26.  
  27. /******************************************************************************
  28.   The WSWord class defines data for a single word.
  29. ******************************************************************************/
  30.  
  31. class WSWord {
  32.  
  33.   // Fields:
  34.  
  35.   public String  list   = null;    // Text for word list.
  36.   public String  grid   = null;    // Text for grid.
  37.   public Point   endpt1 = null;    // Starting grid location.
  38.   public Point   endpt2 = null;    // Ending grid location.
  39.   public boolean found  = false;   // Found flag.
  40.  
  41.   // Constructors:
  42.  
  43.   public WSWord(String s) {
  44.  
  45.     String t;
  46.     int i;
  47.     char c;
  48.  
  49.     this.list = s.toUpperCase();
  50.     t = new String();
  51.     for (i = 0; i < s.length(); i++) {
  52.       c = s.charAt(i);
  53.       if (Character.isLetter(c))
  54.         t += c;
  55.     }
  56.     this.grid = t.toUpperCase();
  57.   }
  58. }
  59.  
  60. /******************************************************************************
  61.   The WSButton class defines a button.   
  62. ******************************************************************************/
  63.  
  64. class WSButton {
  65.  
  66.   // Fields:
  67.  
  68.   public int x, y;             // Screen position.
  69.   public int width, height;    // Width and height.
  70.  
  71.   private String  text;       // Text, if a text button.
  72.   private Font    font;       // Font for text.
  73.   private Polygon polygon;    // Shape if a graphical button.
  74.  
  75.   private Color fgColor = Color.black;        // Foreground color.
  76.   private Color bgColor = Color.lightGray;    // Background color.
  77.   private Color bdColor = Color.black;        // Border color.
  78.  
  79.   // Constructors:
  80.  
  81.   public WSButton(String s, Font font, int width, int height) {
  82.  
  83.     // Text button.
  84.  
  85.     this.text = s;
  86.     this.font = font;
  87.     this.x = 0;
  88.     this.y = 0;
  89.     this.width = width;
  90.     this.height = height;
  91.   }
  92.  
  93.   public WSButton(Polygon p, int width, int height) {
  94.  
  95.     // Graphical button.
  96.  
  97.     this.polygon = p;
  98.     this.x = 0;
  99.     this.y = 0;
  100.     this.width = width;
  101.     this.height = height;
  102.   }
  103.  
  104.   public void setColors(Color fg, Color bg, Color bd) {
  105.  
  106.     this.fgColor = fg;
  107.     this.bgColor = bg;
  108.     this.bdColor = bd;
  109.   }
  110.  
  111.   public boolean inside(int x, int y) {
  112.  
  113.     // Determine if point x, y is on the button.
  114.  
  115.     if (x > this.x && x < this.x + this.width &&
  116.         y > this.y && y < this.y + this.height)
  117.       return true;
  118.    return false;
  119.   }
  120.  
  121.   public void draw(Graphics g) {
  122.  
  123.     FontMetrics fm;
  124.     int x, y;
  125.  
  126.     // Color background and draw border.
  127.  
  128.     g.setColor(this.bgColor);
  129.     g.fillRect(this.x, this.y, this.width, this.height);
  130.     g.setColor(this.bdColor);
  131.     g.drawRect(this.x, this.y, this.width - 1, this.height - 1);
  132.  
  133.     // Write text or draw shape.
  134.  
  135.     g.setColor(this.fgColor);
  136.     if (this.text != null) {
  137.       g.setFont(this.font);
  138.       fm = g.getFontMetrics();
  139.       x = this.x + (this.width - fm.stringWidth(this.text)) / 2;
  140.       y = this.y + (this.height + fm.getAscent()) / 2;
  141.       g.drawString(this.text, x, y);
  142.     }
  143.     if (this.polygon != null) {
  144.       x = this.x + this.width / 2;
  145.       y = this.y + this.height / 2;
  146.       g.translate(x, y);
  147.       g.fillPolygon(this.polygon);
  148.       g.translate(-x, -y);
  149.     }
  150.   }
  151. }
  152.  
  153. /******************************************************************************
  154.   The WSGrid class defines the grid canvas.
  155. ******************************************************************************/
  156.  
  157. class WSGrid {
  158.  
  159.   // Constants.
  160.  
  161.   static final String LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  162.   static final int    MAX     = 100;
  163.  
  164.   // Fields.
  165.  
  166.   public int x, y;             // Screen position.
  167.   public int width, height;    // Width and height.
  168.  
  169.   public boolean select;            // Select flag.
  170.   public int     startX, startY;    // Selection endpoints.
  171.   public int     endX, endY;
  172.   public int     size;              // Grid cell size.
  173.  
  174.   private int         rows, cols;    // Grid size.
  175.   private Font        font;          // Font for letters.
  176.   private FontMetrics fm;            // Font metrics.
  177.  
  178.   private Color fgColor = Color.black;    // Foreground color.
  179.   private Color bgColor = Color.white;    // Background color.
  180.   private Color bdColor = Color.black;    // Border color.
  181.   private Color fdColor = Color.black;    // Found word color. 
  182.   private Color hiColor = Color.black;    // Highlighted word color.
  183.  
  184.   private char[][] grid;    // Letter grid.
  185.  
  186.   // Constructor.
  187.  
  188.   public WSGrid(int rows, int cols, int size, Font font) {
  189.  
  190.     this.x = 0;
  191.     this.y = 0;
  192.     this.rows = rows;
  193.     this.cols = cols;
  194.     this.font = font;
  195.     this.size = size;
  196.     this.select = false;
  197.  
  198.     // Determine width and height based on rows, cols and font.
  199.  
  200.     this.width = cols * this.size;
  201.     this.height = rows * this.size;
  202.  }
  203.  
  204.   public void setColors(Color fg, Color bg, Color bd, Color fd, Color hi) {
  205.  
  206.     this.fgColor = fg;
  207.     this.bgColor = bg;
  208.     this.bdColor = bd;
  209.     this.fdColor = fd;
  210.     this.hiColor = hi;
  211.   }
  212.  
  213.   public void clear() {
  214.  
  215.     int i, j;
  216.  
  217.     this.grid = new char[this.rows][this.cols];
  218.     for (i = 0; i < this.rows; i++)
  219.       for (j = 0; j < this.cols; j++)
  220.         grid[i][j] = ' ';
  221.   }
  222.  
  223.   public boolean inside(int x, int y) {
  224.  
  225.     // Determine if point x, y is on the button.
  226.  
  227.     if (x > this.x && x < this.x + this.width &&
  228.         y > this.y && y < this.y + this.height)
  229.       return true;
  230.    return false;
  231.   }
  232.  
  233.   public void fill() {
  234.  
  235.     WSWord ws;
  236.     int i, j, row, col, r, c;
  237.     boolean added;
  238.  
  239.     // Add each word randomly to the grid.
  240.  
  241.     this.clear();
  242.     for (i = 0; i < WordSearch.words.size(); i++) {
  243.  
  244.       // Get word and pick a random starting position.
  245.  
  246.       ws = (WSWord) WordSearch.words.elementAt(i);
  247.       row = (int) (Math.random() * this.rows);
  248.       col = (int) (Math.random() * this.cols);
  249.       added = false;
  250.  
  251.       // If word is too long to fit grid, remove it. Otherwise try adding it to
  252.       // each cell until we succeed or exhaust all possibilities.
  253.  
  254.       if (ws.grid.length() <= Math.max(this.rows, this.cols)) {
  255.         r = row;
  256.         c = col;
  257.         do {
  258.           added = addWord(ws, r, c);
  259.           if (!added)
  260.             if (++c > this.cols - 1) {
  261.               c = 0;
  262.               if (++r > this.rows - 1)
  263.                 r = 0;
  264.             }
  265.         } while (!added && !(r == row && c == col));
  266.       }
  267.  
  268.       // If we couldn't fit it in, remove it from the list.
  269.  
  270.       if (!added) {
  271.         WordSearch.words.removeElementAt(i);
  272.         i -= 1;
  273.       }
  274.     }
  275.  
  276.     // Fill in rest of grid with random letters.
  277.  
  278.     for (i = 0; i < this.rows; i++)
  279.       for (j = 0; j < this.cols; j++)
  280.         if (grid[i][j] == ' ') {
  281.           r = (int) (Math.random() * LETTERS.length());
  282.           grid[i][j] = LETTERS.charAt(r);
  283.         }
  284.   }
  285.  
  286.   private boolean addWord(WSWord ws, int r1, int c1) {
  287.  
  288.     int i, r2, c2, dx, dy, count;
  289.     boolean fit;
  290.     char c;
  291.  
  292.     // Get a random direction to start with.
  293.  
  294.     dy = (int) (Math.random() * 3) - 1;
  295.     dx = (int) (Math.random() * 3) - 1;
  296.     count = 0;
  297.  
  298.     // Try each direction until we find a fit or exhaust all possibilities.
  299.  
  300.     do {
  301.       fit = true;
  302.       r2 = r1 + (ws.grid.length() - 1) * dy;
  303.       c2 = c1 + (ws.grid.length() - 1) * dx;
  304.       if (!(dx == 00 && dy == 0) &&
  305.           r2 >= 0 && r2 < this.rows && c2 >= 0 && c2 < this.cols)
  306.         for (i = 0; i < ws.grid.length(); i++) {
  307.           c = grid[r1 + i * dy][c1 + i * dx];
  308.           if (c != ' ' && c != ws.grid.charAt(i))
  309.             fit = false;
  310.         }
  311.       else
  312.         fit = false;
  313.       if (!fit)
  314.         if (++dx > 1) {
  315.           dx = -1;
  316.           if (++dy > 1)
  317.             dy = -1;
  318.         }
  319.       count++;
  320.     } while (!fit && count <= 8);
  321.  
  322.     // If we found a fit, add word and save endpoints.
  323.  
  324.     if (fit) {
  325.       for (i = 0; i < ws.grid.length(); i++)
  326.         this.grid[r1 + i * dy][c1 + i * dx] = ws.grid.charAt(i);
  327.       ws.endpt1 = new Point(c1, r1);
  328.       ws.endpt2 = new Point(c2, r2);
  329.     }
  330.  
  331.     return fit;
  332.   }
  333.  
  334.   public boolean checkSelection() {
  335.  
  336.     WSWord ws;
  337.     int x1, y1, x2, y2;
  338.     Point p1, p2;
  339.     int i;
  340.  
  341.     // Check selection against word list. If endpoints match, mark it as found.
  342.  
  343.     x1 = Math.min(Math.max(this.startX / this.size, 0), this.cols - 1);
  344.     y1 = Math.min(Math.max(this.startY / this.size, 0), this.rows - 1);
  345.     x2 = Math.min(Math.max(this.endX / this.size, 0), this.cols - 1);
  346.     y2 = Math.min(Math.max(this.endY / this.size, 0), this.rows - 1);
  347.     p1 = new Point(x1, y1);
  348.     p2 = new Point(x2, y2);
  349.     for (i = 0; i < WordSearch.words.size(); i++) {
  350.       ws = (WSWord) WordSearch.words.elementAt(i);
  351.       if ((ws.endpt1.equals(p1) && ws.endpt2.equals(p2)) ||
  352.           (ws.endpt1.equals(p2) && ws.endpt2.equals(p1))) {
  353.         ws.found = true;
  354.         return true;
  355.       }
  356.     }
  357.     return false;
  358.   }
  359.  
  360.   public void draw(Graphics g) {
  361.  
  362.     WSWord ws;
  363.     int i, j;
  364.     int k, l;
  365.     int x, y;
  366.     int x1, y1, x2, y2;
  367.     Polygon p;
  368.     Character c;
  369.     FontMetrics fm;
  370.  
  371.     // Color background and draw border.
  372.  
  373.     g.setColor(this.bgColor);
  374.     g.fillRect(this.x, this.y, this.width, this.height);
  375.     g.setColor(this.bdColor);
  376.     g.drawRect(this.x, this.y, this.width - 1, this.height - 1);
  377.  
  378.     // Shade found words.
  379.  
  380.     g.setColor(this.fdColor);
  381.     for (i = 0; i < WordSearch.words.size(); i++) {
  382.       ws = (WSWord) WordSearch.words.elementAt(i);
  383.       if (ws.found) {
  384.         x1 = this.x + this.size * ws.endpt1.x + this.size / 2;
  385.         y1 = this.y + this.size * ws.endpt1.y + this.size / 2;
  386.         x2 = this.x + this.size * ws.endpt2.x + this.size / 2;
  387.         y2 = this.y + this.size * ws.endpt2.y + this.size / 2;
  388.         p = getPolygon(x1, y1, x2, y2);
  389.         g.fillPolygon(p);
  390.       }
  391.     }
  392.  
  393.     // Shade and circle current selection, if any.
  394.  
  395.     if (WordSearch.words.size() > 0 && this.select) {
  396.       i = Math.min(Math.max(this.startY / this.size, 0), this.rows - 1);
  397.       j = Math.min(Math.max(this.startX / this.size, 0), this.cols - 1);
  398.       k = Math.min(Math.max(this.endY / this.size, 0), this.rows - 1);
  399.       l = Math.min(Math.max(this.endX / this.size, 0), this.cols - 1);
  400.       x1 = this.size * j + this.size / 2;
  401.       y1 = this.size * i + this.size / 2;
  402.       x2 = this.size * l + this.size / 2;
  403.       y2 = this.size * k + this.size / 2;
  404.       p = getPolygon(x1, y1, x2, y2);
  405.       g.setColor(this.hiColor);
  406.       g.fillPolygon(p);
  407.       g.setColor(this.fgColor);
  408.       g.drawPolygon(p);
  409.       g.drawLine(p.xpoints[p.npoints - 1], p.ypoints[p.npoints - 1], p.xpoints[0], p.ypoints[0]);
  410.     }
  411.  
  412.     // Write letters.
  413.  
  414.     g.setFont(this.font);
  415.     g.setColor(this.fgColor);
  416.     fm = g.getFontMetrics();
  417.     for (i = 0; i < this.rows; i++)
  418.       for (j = 0; j < this.cols; j++) {
  419.         c = new Character(this.grid[i][j]);
  420.         x = this.x + j * this.size + (this.size - fm.stringWidth(c.toString())) / 2;
  421.         y = this.y + i * this.size + (this.size - fm.getHeight()) / 2 + fm.getAscent();
  422.         g.drawString(c.toString(), x, y);
  423.       }
  424.  
  425.     // Circle found words.
  426.  
  427.     g.setColor(this.fgColor);
  428.     for (i = 0; i < WordSearch.words.size(); i++) {
  429.       ws = (WSWord) WordSearch.words.elementAt(i);
  430.       if (ws.found) {
  431.         x1 = this.x + this.size * ws.endpt1.x + this.size / 2;
  432.         y1 = this.y + this.size * ws.endpt1.y + this.size / 2;
  433.         x2 = this.x + this.size * ws.endpt2.x + this.size / 2;
  434.         y2 = this.y + this.size * ws.endpt2.y + this.size / 2;
  435.         p = getPolygon(x1, y1, x2, y2);
  436.         g.drawPolygon(p);
  437.         g.drawLine(p.xpoints[p.npoints - 1], p.ypoints[p.npoints - 1], p.xpoints[0], p.ypoints[0]);
  438.       }
  439.     }
  440.   }
  441.  
  442.   private Polygon getPolygon(int x1, int y1, int x2, int y2) {
  443.  
  444.     Polygon p;
  445.     double dx, dy, a, b, angle;
  446.     int r;
  447.  
  448.     // Find angles.
  449.  
  450.     dx = x2 - x1;
  451.     dy = y2 - y1;
  452.     a = 0;
  453.     if (dx == 0) {
  454.       if (dy < 0)
  455.         a = Math.PI / 2;
  456.       else if (dy > 0)
  457.         a = 3 * Math.PI / 2;
  458.         
  459.     }
  460.     else if (dy == 0) {
  461.       if (dx > 0)
  462.         a = 0;
  463.       else
  464.         a = Math.PI;
  465.     }
  466.     else {
  467.       a = Math.atan(Math.abs(dy / dx));
  468.       if (dx < 0 && dy < 0)
  469.         a = Math.PI - a;
  470.       if (dx < 0 && dy > 0)
  471.         a += Math.PI;
  472.       if (dx > 0 && dy > 0)
  473.         a = 2 * Math.PI - a;
  474.     }
  475.     a = a + Math.PI / 2;
  476.     b = a + Math.PI;
  477.  
  478.     // Build polygon as a half circle around each endpoint and connect them.
  479.  
  480.     p = new Polygon();
  481.     r = (int) Math.round(this.size * Math.sin(Math.PI / 4) / 2);
  482.     for (angle = a; angle < a + Math.PI; angle += Math.PI / 30)
  483.       p.addPoint((int) Math.round(x1 + r * Math.cos(angle)),
  484.                  (int) Math.round(y1 - r * Math.sin(angle)));
  485.     p.addPoint((int) Math.round(x1 + r * Math.cos(b)),
  486.                (int) Math.round(y1 - r * Math.sin(b)));
  487.     for (angle = b; angle < b + Math.PI; angle += Math.PI / 30)
  488.       p.addPoint((int) Math.round(x2 + r * Math.cos(angle)),
  489.                  (int) Math.round(y2 - r * Math.sin(angle)));
  490.     p.addPoint((int) Math.round(x2 + r * Math.cos(a)),
  491.                (int) Math.round(y2 - r * Math.sin(a)));
  492.     return p;
  493.   }
  494. }
  495.  
  496. /******************************************************************************
  497.   The WSList class defines the word list.   
  498. ******************************************************************************/
  499.  
  500. class WSList {
  501.  
  502.   // Fields.
  503.  
  504.   public int x, y;             // Screen position.
  505.   public int width, height;    // Width and height.
  506.   public int scroll;           // Starting word for list display.
  507.  
  508.   private Font        font;    // Font for words.
  509.   private FontMetrics fm;      // Font metrics.
  510.  
  511.   private Color fgColor = Color.black;       // Foreground color.
  512.   private Color bgColor = Color.white;       // Background color.
  513.   private Color bdColor = Color.black;       // Border color.
  514.   private Color fdColor = Color.darkGray;    // Found word color.
  515.  
  516.   // Constructor.
  517.  
  518.   public WSList(int width, int height, Font font) {
  519.  
  520.     this.x = 0;
  521.     this.y = 0;
  522.     this.width = width;
  523.     this.height = height;
  524.     this.scroll = 0;
  525.     this.font = font;
  526.  }
  527.  
  528.   public void setColors(Color fg, Color bg, Color bd, Color fd) {
  529.  
  530.     this.fgColor = fg;
  531.     this.bgColor = bg;
  532.     this.bdColor = bd;
  533.     this.fdColor = fd;
  534.   }
  535.  
  536.   public void draw(Graphics g) {
  537.  
  538.     int i;
  539.     int x, y;
  540.     Rectangle rect;
  541.     FontMetrics fm;
  542.     WSWord ws;
  543.  
  544.     // Color background.
  545.  
  546.     g.setColor(this.bgColor);
  547.     g.fillRect(this.x, this.y, this.width, this.height);
  548.  
  549.     // Write each word in the list.
  550.  
  551.     g.setFont(this.font);
  552.     fm = g.getFontMetrics();
  553.     this.scroll = Math.min(Math.max(this.scroll, 0), Math.max(WordSearch.words.size() - this.height / fm.getHeight(), 0));
  554.     x = this.x + fm.getMaxAdvance() / 2;
  555.     y = this.y + fm.getHeight();
  556.     for (i = this.scroll; i < WordSearch.words.size() && y < this.y + this.height; i++) {
  557.       ws = (WSWord) WordSearch.words.elementAt(i);
  558.       if (ws.found)
  559.         g.setColor(this.fdColor);
  560.       else
  561.         g.setColor(this.fgColor);
  562.       g.drawString(ws.list, x, y);
  563.       y += fm.getHeight();
  564.     }
  565.  
  566.     // Draw border.
  567.  
  568.     g.setColor(this.bdColor);
  569.     g.drawRect(this.x, this.y, this.width - 1, this.height - 1);
  570.   }
  571. }
  572.  
  573. /******************************************************************************
  574.   Main applet code.
  575. ******************************************************************************/
  576.  
  577. public class WordSearch extends Applet implements Runnable {
  578.  
  579.   // Thread control variables.
  580.  
  581.   Thread loopThread;    // Main thread.
  582.  
  583.   // Constants
  584.  
  585.   static final int DELAY = 50;    // Milliseconds between screen updates.
  586.  
  587.   static final int INIT  =  1;    // Game states.
  588.   static final int PLAY  =  2;
  589.   static final int OVER  =  3;
  590.  
  591.   // Parameters and defaults.
  592.  
  593.   Color  scrnFgColor = Color.black;             // Background color.
  594.   Color  scrnBgColor = Color.white;             // Foreground color.
  595.   Color  scrnBdColor = Color.black;             // Border color.   
  596.   String scrnFontStr = "Helvetica,bold,12";     // Font.
  597.  
  598.   Color  bttnFgColor = Color.black;             // Button background color.
  599.   Color  bttnBgColor = Color.lightGray;         // Button foreground color.
  600.   String bttnFontStr = "Dialog,bold,10";        // Button font.
  601.  
  602.   Color  gridFgColor = Color.black;             // Grid text color.
  603.   Color  gridBgColor = Color.white;             // Grid background color.
  604.   Color  gridHiColor = Color.yellow;            // Grid highlight color.
  605.   Color  gridFdColor = Color.lightGray;         // Grid found color.
  606.   String gridFontStr = "Courier,plain,14";      // Grid font.
  607.  
  608.   Color  listFgColor = Color.black;             // List text color.
  609.   Color  listBgColor = Color.white;             // List background color.
  610.   Color  listFdColor = Color.lightGray;         // List found color.
  611.   String listFontStr = "Helvetica,plain,12";    // List font.
  612.  
  613.   int    gridRows    = 15;                      // Grid rows.
  614.   int    gridCols    = 15;                      // Grid columns.
  615.   int    gridSize    = 20;                      // Grid cell size.
  616.  
  617.   Vector files       = new Vector();            // List if text file URLs.
  618.  
  619.   // Global variables.
  620.  
  621.   static Vector words;    // Word list.
  622.  
  623.   Font scrnFont;    // Screen font.
  624.   Font bttnFont;    // Screen font.
  625.   Font gridFont;    // Grid font.
  626.   Font listFont;    // List font.
  627.  
  628.   // Display elements.
  629.  
  630.   WSGrid   grid;
  631.   WSList   list;
  632.   WSButton newGame;
  633.   WSButton solveGame;
  634.   WSButton scrollUp;
  635.   WSButton scrollDn;
  636.  
  637.   // File data.
  638.  
  639.   int fileNum;
  640.  
  641.   // Game data.
  642.  
  643.   int gap = 4;     // Gap between display elements.
  644.  
  645.   int    gameState;    // Game state.
  646.   int    scroll;       // Scroll direction.
  647.   int    count;        // Number of words found.
  648.   long   startTime;    // Start time of current game.
  649.   String timeText;     // Elapsed time text.
  650.   String statText;     // Words found/total text.
  651.   String subjText;     // Word list subject, from file.
  652.  
  653.   // Values for the off-screen and scratch image.
  654.  
  655.   Dimension offDimension;
  656.   Image offImage;
  657.   Graphics offGraphics;
  658.  
  659.   // Applet information.
  660.  
  661.   public String getAppletInfo() {
  662.  
  663.     return("Word Search\n\nCopyright 1999 by Mike Hall");
  664.   }
  665.  
  666.   public void init() {
  667.  
  668.     Graphics g;
  669.     Dimension d;
  670.     Font f;
  671.     FontMetrics fm;
  672.     String s;
  673.     StringTokenizer st;
  674.     int n;
  675.     Polygon p;
  676.     int x, y;
  677.     int w, h;
  678.  
  679.     // Take credit.
  680.  
  681.     System.out.println("Word Search, Copyright 1999 by Mike Hall.");
  682.  
  683.     // Get colors.
  684.  
  685.     try {
  686.       s = getParameter("screencolors");
  687.       if (s != null) {
  688.         st = new StringTokenizer(s, ",");
  689.         scrnFgColor = getColorParm(st.nextToken());
  690.         scrnBgColor = getColorParm(st.nextToken());
  691.         scrnBdColor = getColorParm(st.nextToken());
  692.       }
  693.     }
  694.     catch (Exception e) {}
  695.     try {
  696.       s = getParameter("buttoncolors");
  697.       if (s != null) {
  698.         st = new StringTokenizer(s, ",");
  699.         bttnFgColor = getColorParm(st.nextToken());
  700.         bttnBgColor = getColorParm(st.nextToken());
  701.       }
  702.     }
  703.     catch (Exception e) {}
  704.     try {
  705.       s = getParameter("gridcolors");
  706.       if (s != null) {
  707.         st = new StringTokenizer(s, ",");
  708.         gridFgColor = getColorParm(st.nextToken());
  709.         gridBgColor = getColorParm(st.nextToken());
  710.         gridFdColor = getColorParm(st.nextToken());
  711.         gridHiColor = getColorParm(st.nextToken());
  712.       }
  713.     }
  714.     catch (Exception e) {}
  715.     try {
  716.       s = getParameter("listcolors");
  717.       if (s != null) {
  718.         st = new StringTokenizer(s, ",");
  719.         listFgColor = getColorParm(st.nextToken());
  720.         listBgColor = getColorParm(st.nextToken());
  721.         listFdColor = getColorParm(st.nextToken());
  722.       }
  723.     }
  724.     catch (Exception e) {}
  725.  
  726.     // Get fonts.
  727.  
  728.     scrnFont = getFontParm(scrnFontStr);
  729.     try {
  730.       s = getParameter("screenfont");
  731.       if (s != null)
  732.         if ((f = getFontParm(s)) != null)
  733.           scrnFont = f;
  734.     }
  735.     catch (Exception e) {}
  736.     bttnFont = getFontParm(bttnFontStr);
  737.     try {
  738.       s = getParameter("buttonfont");
  739.       if (s != null)
  740.         if ((f = getFontParm(s)) != null)
  741.           bttnFont = f;
  742.     }
  743.     catch (Exception e) {}
  744.     gridFont = getFontParm(gridFontStr);
  745.     try {
  746.       s = getParameter("gridfont");
  747.       if (s != null)
  748.         if ((f = getFontParm(s)) != null)
  749.           gridFont = f;
  750.     }
  751.     catch (Exception e) {}
  752.     listFont = getFontParm(listFontStr);
  753.     try {
  754.       s = getParameter("listfont");
  755.       if (s != null)
  756.         if ((f = getFontParm(s)) != null)
  757.           listFont = f;
  758.     }
  759.     catch (Exception e) {}
  760.  
  761.     // Get grid size.
  762.  
  763.     try {
  764.       s = getParameter("gridsize");
  765.       if (s != null) {
  766.         st = new StringTokenizer(s, ",");
  767.         if ((n = Integer.parseInt(st.nextToken())) > 0)
  768.           gridRows = n;
  769.         if ((n = Integer.parseInt(st.nextToken())) > 0)
  770.           gridCols = n;
  771.         if ((n = Integer.parseInt(st.nextToken())) > 0)
  772.           gridSize = n;
  773.       }
  774.     }
  775.     catch (Exception e) {}
  776.  
  777.     // Get list of word file URLs.
  778.  
  779.     try {
  780.       s = getParameter("files");
  781.       if (s != null) {
  782.         st = new StringTokenizer(s, ",");
  783.         while (st.hasMoreTokens())
  784.           files.addElement(st.nextToken());
  785.       }
  786.     }
  787.     catch (Exception e) {}
  788.  
  789.     // Get screen size and build display.
  790.  
  791.     g = getGraphics();
  792.     d = size();
  793.  
  794.     // Create and position grid.
  795.  
  796.     grid = new WSGrid(gridRows, gridCols, gridSize, gridFont);
  797.     grid.setColors(gridFgColor, gridBgColor, scrnBdColor, gridFdColor, gridHiColor);
  798.     grid.clear();
  799.  
  800.     // Create and position list.
  801.  
  802.     g.setFont(listFont);
  803.     fm = g.getFontMetrics();
  804.     x = grid.x + grid.width + gap;
  805.     w = d.width - x;
  806.     h = grid.height - 2 * (fm.getHeight() + gap);
  807.     list = new WSList(w, h, listFont);
  808.     list.x = x;
  809.     list.y = grid.y + fm.getHeight() + gap;
  810.     list.setColors(listFgColor, listBgColor, scrnBdColor, listFdColor);
  811.  
  812.     // Create and position scroll buttons.
  813.  
  814.     g.setFont(bttnFont);
  815.     w = list.width;
  816.     h = fm.getHeight();
  817.     p = new Polygon();
  818.     p.addPoint(0, -h / 2 + 2);
  819.     p.addPoint(-fm.getMaxAdvance() / 2, h / 2 - 2);
  820.     p.addPoint(fm.getMaxAdvance() / 2, h / 2 - 2);
  821.     scrollUp = new WSButton(p, w, h);
  822.     scrollUp.x = list.x;
  823.     scrollUp.y = grid.y;
  824.     scrollUp.setColors(bttnFgColor, bttnBgColor, scrnBdColor);
  825.     p = new Polygon();
  826.     p.addPoint(0, h / 2 - 2);
  827.     p.addPoint(-fm.getMaxAdvance() / 2, -h / 2 + 2);
  828.     p.addPoint(fm.getMaxAdvance() / 2, -h / 2 + 2);
  829.     scrollDn = new WSButton(p, w, h);
  830.     scrollDn.x = list.x;
  831.     scrollDn.y = list.y + list.height + gap;
  832.     scrollDn.setColors(bttnFgColor, bttnBgColor, scrnBdColor);
  833.  
  834.     // Create and position text buttons.
  835.  
  836.     g.setFont(bttnFont);
  837.     fm = g.getFontMetrics();
  838.     s = "New Game";
  839.     w = fm.stringWidth(s) + fm.getMaxAdvance();
  840.     h = 3 * fm.getHeight() / 2;
  841.     newGame = new WSButton(s, bttnFont, w, h);
  842.     x = 0;
  843.     y = d.height - h;
  844.     newGame.x = x;
  845.     newGame.y = y;
  846.     newGame.setColors(bttnFgColor, bttnBgColor, scrnBdColor);
  847.     s = "Solve Game";
  848.     x = x + w + gap;
  849.     w = fm.stringWidth(s) + fm.getMaxAdvance();
  850.     solveGame = new WSButton(s, bttnFont, w, h);
  851.     solveGame.x = x;
  852.     solveGame.y = y;
  853.     solveGame.setColors(bttnFgColor, bttnBgColor, scrnBdColor);
  854.  
  855.     // Initialize game data.
  856.  
  857.     fileNum = 0;
  858.     scroll = 0;
  859.     timeText = "";
  860.     statText = "";
  861.     subjText = "";
  862.     words = new Vector();
  863.     grid.fill();
  864.     endGame();
  865.     gameState = INIT;
  866.   }
  867.  
  868.   public Color getColorParm(String s) {
  869.  
  870.     int r, g, b;
  871.  
  872.     // Check if a pre-defined color is specified.
  873.  
  874.     if (s.equalsIgnoreCase("black"))
  875.       return(Color.black);
  876.     if (s.equalsIgnoreCase("blue"))
  877.       return(Color.blue);
  878.     if (s.equalsIgnoreCase("cyan"))
  879.       return(Color.cyan);
  880.     if (s.equalsIgnoreCase("darkGray"))
  881.       return(Color.darkGray);
  882.     if (s.equalsIgnoreCase("gray"))
  883.       return(Color.gray);
  884.     if (s.equalsIgnoreCase("green"))
  885.       return(Color.green);
  886.     if (s.equalsIgnoreCase("lightGray"))
  887.       return(Color.lightGray);
  888.     if (s.equalsIgnoreCase("magenta"))
  889.       return(Color.magenta);
  890.     if (s.equalsIgnoreCase("orange"))
  891.       return(Color.orange);
  892.     if (s.equalsIgnoreCase("pink"))
  893.       return(Color.pink);
  894.     if (s.equalsIgnoreCase("red"))
  895.       return(Color.red);
  896.     if (s.equalsIgnoreCase("white"))
  897.       return(Color.white);
  898.     if (s.equalsIgnoreCase("yellow"))
  899.       return(Color.yellow);
  900.  
  901.     // If the color is specified in HTML format, build it from the red, green
  902.     // and blue values.
  903.  
  904.     if (s.length() == 7 && s.charAt(0) == '#') {
  905.       r = Integer.parseInt(s.substring(1,3),16);
  906.       g = Integer.parseInt(s.substring(3,5),16);
  907.       b = Integer.parseInt(s.substring(5,7),16);
  908.       return(new Color(r, g, b));
  909.     }
  910.  
  911.     // If we can't figure it out, default to black.
  912.  
  913.     return(Color.black);
  914.   }
  915.  
  916.   public Font getFontParm(String s) {
  917.  
  918.     String t, fontName;
  919.     StringTokenizer st;
  920.     int n, fontStyle, fontSize;
  921.  
  922.     fontName = "";
  923.     fontStyle = -1;
  924.     fontSize = -1;
  925.  
  926.     // Parse font name.
  927.  
  928.     st = new StringTokenizer(s, ",");
  929.     t = st.nextToken();
  930.     if (t.equalsIgnoreCase("Courier"))
  931.       fontName = "Courier";
  932.     else if (t.equalsIgnoreCase("Dialog"))
  933.       fontName = "Dialog";
  934.     else if (t.equalsIgnoreCase("Helvetica"))
  935.       fontName = "Helvetica";
  936.     else if (t.equalsIgnoreCase("Symbol"))
  937.       fontName = "Symbol";
  938.     else if (t.equalsIgnoreCase("TimesRoman"))
  939.       fontName = "TimesRoman";
  940.  
  941.     // Parse font style.
  942.  
  943.     t = st.nextToken();
  944.     if (t.equalsIgnoreCase("plain"))
  945.       fontStyle = Font.PLAIN;
  946.     else if (t.equalsIgnoreCase("bold"))
  947.       fontStyle = Font.BOLD;
  948.     else if (t.equalsIgnoreCase("italic"))
  949.       fontStyle = Font.ITALIC;
  950.     else if (t.equalsIgnoreCase("boldItalic"))
  951.       fontStyle = Font.BOLD + Font.ITALIC;
  952.  
  953.     // Parse font size.
  954.  
  955.     t = st.nextToken();
  956.     if ((n = Integer.parseInt(t)) > 0)
  957.       fontSize = n;
  958.  
  959.     // Return the specified font.
  960.  
  961.     if (fontName != "" && fontStyle >= 0 && fontSize >= 0)
  962.       return(new Font(fontName, fontStyle, fontSize));
  963.  
  964.     // If we can't figure it out, return a null value.
  965.  
  966.     return (Font) null;
  967.   }
  968.  
  969.   public void start() {
  970.  
  971.     if (loopThread == null) {
  972.       loopThread = new Thread(this);
  973.       loopThread.start();
  974.     }
  975.   }
  976.  
  977.   public void stop() {
  978.  
  979.     if (loopThread != null) {
  980.       loopThread.stop();
  981.       loopThread = null;
  982.     }
  983.   }
  984.  
  985.   public void run() {
  986.  
  987.     long threadTime;
  988.     Date date;
  989.     String mm, ss;
  990.  
  991.     // Lower this thread's priority and get the current time.
  992.  
  993.     Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
  994.     threadTime = System.currentTimeMillis();
  995.  
  996.     // This is the main loop.
  997.  
  998.     while (Thread.currentThread() == loopThread) {
  999.  
  1000.       // Scroll word list.
  1001.  
  1002.       list.scroll += scroll;
  1003.  
  1004.       // Update game time.
  1005.  
  1006.       if (gameState == PLAY) {
  1007.         date = new Date(System.currentTimeMillis() - startTime);
  1008.         mm = (new Integer(date.getMinutes())).toString();
  1009.         ss = (new Integer(date.getSeconds())).toString();
  1010.         if (mm.length() < 2)
  1011.           mm = "0" + mm;
  1012.         if (ss.length() < 2)
  1013.           ss = "0" + ss;
  1014.         timeText = "Time: " + mm + ":" + ss;
  1015.         statText = "Found: " + count + "/" + words.size();
  1016.       }
  1017.  
  1018.       // Update the screen and set the timer for the next loop.
  1019.  
  1020.       repaint();
  1021.       try {
  1022.         threadTime += DELAY;
  1023.         Thread.sleep(Math.max(0, threadTime - System.currentTimeMillis()));
  1024.       }
  1025.       catch (InterruptedException e) {
  1026.         break;
  1027.       }
  1028.     }
  1029.   }
  1030.  
  1031.   public boolean mouseDown(Event e, int x, int y) {
  1032.  
  1033.     // Check buttons.
  1034.  
  1035.     if (newGame.inside(x, y))
  1036.       initGame();
  1037.     if (gameState == PLAY && solveGame.inside(x, y))
  1038.       solveGame();
  1039.     if (gameState != INIT && scrollUp.inside(x, y))
  1040.       scroll = -1;
  1041.     if (gameState != INIT && scrollDn.inside(x, y))
  1042.       scroll = 1;
  1043.  
  1044.     // Check grid.
  1045.  
  1046.     if (gameState == PLAY && grid.inside(x, y)) {
  1047.       grid.select = true;
  1048.       grid.startX = x;
  1049.       grid.startY = y;
  1050.       grid.endX = x;
  1051.       grid.endY = y;
  1052.     }
  1053.  
  1054.     return true;
  1055.   }
  1056.  
  1057.   public boolean mouseDrag(Event e, int x, int y) {
  1058.  
  1059.     if (gameState == PLAY && grid.select) {
  1060.       grid.endX = x;
  1061.       grid.endY = y;
  1062.     }
  1063.  
  1064.     return true;
  1065.   }
  1066.  
  1067.   public boolean mouseUp(Event e, int x, int y) {
  1068.  
  1069.     // Stop any scrolling.
  1070.  
  1071.     scroll = 0;
  1072.  
  1073.     // If a selection was being made, check it.
  1074.  
  1075.     if (gameState == PLAY && grid.select) {
  1076.       grid.select = false;
  1077.       if (grid.checkSelection())
  1078.         if (++count >= words.size()) {
  1079.           timeText += " Done!";
  1080.           statText = "Found: " + count + "/" + words.size();
  1081.           endGame();
  1082.         }
  1083.     }
  1084.  
  1085.     return true;
  1086.   }
  1087.  
  1088.   public void initGame() {
  1089.  
  1090.     setWords();
  1091.     grid.select = false;
  1092.     grid.fill();
  1093.     list.scroll = 0;
  1094.     count = 0;
  1095.     startTime = System.currentTimeMillis();
  1096.     timeText = "";
  1097.     statText = "";
  1098.     gameState = PLAY;
  1099.   }
  1100.  
  1101.   public void solveGame() {
  1102.  
  1103.     WSWord ws;
  1104.     int i;
  1105.  
  1106.     // Mark all words as found.
  1107.  
  1108.     for (i = 0; i < words.size(); i++) {
  1109.       ws = (WSWord) words.elementAt(i);
  1110.       ws.found = true;
  1111.     }
  1112.     count = words.size();
  1113.     timeText = "Cheated!";
  1114.     endGame();
  1115.   }
  1116.  
  1117.   public void endGame() {
  1118.  
  1119.     gameState = OVER;
  1120.   }
  1121.  
  1122.   public void setWords() {
  1123.  
  1124.     String s;
  1125.     URL url;
  1126.     InputStream in;
  1127.     DataInputStream ds;
  1128.  
  1129.     // Clear word list.
  1130.  
  1131.     words.removeAllElements();
  1132.  
  1133.     // Get next file URL.
  1134.  
  1135.     if (fileNum >= files.size())
  1136.       fileNum = 0;
  1137.     s = (String) files.elementAt(fileNum);
  1138.     url = (URL) null;
  1139.     try {
  1140.       url = new URL(getDocumentBase(), s);
  1141.     }
  1142.     catch (Exception e) {}
  1143.     fileNum++;
  1144.  
  1145.     // Open it up and read list of words.
  1146.  
  1147.     subjText = "";
  1148.     try {
  1149.       in = url.openStream();
  1150.       ds = new DataInputStream(in);
  1151.       while((s = ds.readLine()) != null)
  1152.         if (s.startsWith("#"))
  1153.           subjText = "'" + s.substring(1) + "'";
  1154.         else
  1155.           words.addElement(new WSWord(s));
  1156.     }
  1157.     catch (IOException e) {}
  1158.   }
  1159.  
  1160.   public void paint(Graphics g) {
  1161.  
  1162.     update(g);
  1163.   }
  1164.  
  1165.   public void update(Graphics g) {
  1166.  
  1167.     Dimension d = size();
  1168.     FontMetrics fm;
  1169.     String s1, s2;
  1170.     int x, y;
  1171.     int w, h;
  1172.  
  1173.     // Create the offscreen graphics context, if no good one exists.
  1174.  
  1175.     if (offGraphics == null || d.width != offDimension.width || d.height != offDimension.height) {
  1176.       offDimension = new Dimension(d.width, d.height);
  1177.       offImage = createImage(d.width, d.height);
  1178.       offGraphics = offImage.getGraphics();
  1179.     }
  1180.  
  1181.     // Color background.
  1182.  
  1183.     offGraphics.setColor(scrnBgColor);
  1184.     offGraphics.fillRect(0, 0, d.width, d.height);
  1185.  
  1186.     // Draw elements.
  1187.  
  1188.     grid.draw(offGraphics);
  1189.     list.draw(offGraphics);
  1190.     scrollUp.draw(offGraphics);
  1191.     scrollDn.draw(offGraphics);
  1192.     newGame.draw(offGraphics);
  1193.     solveGame.draw(offGraphics);
  1194.  
  1195.     // Display title, messages, etc. as appropriate.
  1196.  
  1197.     offGraphics.setColor(scrnFgColor);
  1198.     offGraphics.setFont(scrnFont);
  1199.     fm = offGraphics.getFontMetrics();
  1200.  
  1201.     if (gameState == INIT) {
  1202.       x = (grid.x + grid.width) / 2;
  1203.       y = (grid.y + grid.height) / 2;
  1204.       s1 = "Word Search";
  1205.       s2 = "Copyright 1999 by Mike Hall";
  1206.       w = Math.max(fm.stringWidth(s1), fm.stringWidth(s2)) + 2 * fm.getMaxAdvance();
  1207.       h = 5 * fm.getHeight();
  1208.       offGraphics.setColor(gridFgColor);
  1209.       offGraphics.fillRect(x - w / 2, y - h / 2, w, h);
  1210.       offGraphics.setColor(gridBgColor);
  1211.       offGraphics.drawRect(x - w / 2 + 1, y - h / 2 + 1, w - 3, h - 3);
  1212.       offGraphics.drawString(s1, x - fm.stringWidth(s1) / 2, y - h / 2 + 2 * fm.getHeight());
  1213.       offGraphics.drawString(s2, x - fm.stringWidth(s2) / 2, y + h / 2 - fm.getHeight() - fm.getDescent());
  1214.     }
  1215.  
  1216.     x = (grid.x + grid.width - fm.stringWidth(subjText)) / 2;
  1217.     y = grid.y + grid.height + fm.getHeight();
  1218.     offGraphics.drawString(subjText, x, y);
  1219.  
  1220.     s1 = timeText;
  1221.     x = Math.min(d.width - fm.stringWidth(s1), list.x);
  1222.     offGraphics.drawString(s1, x, y);
  1223.  
  1224.     s1 = statText;
  1225.     x = Math.min(d.width - fm.stringWidth(s1), list.x);
  1226.     y += fm.getHeight();
  1227.     offGraphics.drawString(s1, x, y);
  1228.  
  1229.     // Copy the off-screen buffer to the screen.
  1230.  
  1231.     g.drawImage(offImage, 0, 0, this);
  1232.   }
  1233. }
  1234.