home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 27 / IOPROG_27.ISO / SOFT / WORD.ZIP / WordSearch.java < prev    next >
Encoding:
Java Source  |  1999-04-17  |  33.6 KB  |  1,233 lines

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