1   /**
2    * Copyright (c) 2000-2007 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.applets.hangman;
24  
25  import java.applet.Applet;
26  import java.applet.AudioClip;
27  
28  import java.awt.Color;
29  import java.awt.Font;
30  import java.awt.FontMetrics;
31  import java.awt.Graphics;
32  import java.awt.Image;
33  import java.awt.MediaTracker;
34  import java.awt.event.KeyEvent;
35  import java.awt.event.KeyListener;
36  
37  import java.util.ArrayList;
38  import java.util.List;
39  import java.util.StringTokenizer;
40  
41  /**
42   * <a href="Hangman.java.html"><b><i>View Source</i></b></a>
43   *
44   * @author Alexander Chow
45   *
46   */
47  public class Hangman extends Applet implements KeyListener {
48  
49      private static final int MEDIA_TRACKER_HANG = 0;
50  
51      private static final int MEDIA_TRACKER_DANCE = 1;
52  
53      private static final int TOTAL_IMAGES_HANG = 5;
54  
55      private static final int TOTAL_IMAGES_DANCE = 5;
56  
57      private static final int HANG_WIDTH = 42;
58  
59      private static final int HANG_HEIGHT = 64;
60  
61      private static final int DANCE_WIDTH = 100;
62  
63      private static final int DANCE_HEIGHT = 80;
64  
65      private static final int[] DANCE_STEP = new int[] {
66          12, 28, 8, -6, -20, 12, -10, -6, -10, -20, 8, 8, 12, -8, -4
67      };
68  
69      public void init() {
70          _initValues();
71          _buildWordList();
72          _loadResources();
73  
74          addKeyListener(this);
75          _newGame();
76      }
77  
78      public void paint(Graphics g) {
79          // gallows and noose
80          g.setColor(new Color(128, 64, 0));
81          g.fillRect(10, HANG_HEIGHT * 2 + 10, HANG_WIDTH + 3, 3);
82          g.fillRect(HANG_WIDTH / 2 + 10, 10, 3, HANG_HEIGHT * 2);
83          g.fillRect(HANG_WIDTH / 2 + 10, 10, HANG_WIDTH / 2 + HANG_WIDTH / 2, 3);
84          g.fillRect(HANG_WIDTH * 3 / 2 + 9, 10, 3, HANG_HEIGHT / 3 + 1);
85  
86          // hanging images and wrong letters
87          if (_wrongGuess.length() != 0) {
88              g.drawImage(
89                  _hangImages[_wrongGuess.length()-1],
90                  HANG_WIDTH + 10, HANG_HEIGHT / 3, this);
91  
92              g.setColor(Color.RED);
93              g.setFont(_wrongFont);
94              g.drawChars(
95                  _wrongGuess.toUpperCase().toCharArray(), 0,
96                  _wrongGuess.length(), HANG_WIDTH * 2,
97                  _wrongMetrics.getHeight() + 1);
98          }
99  
100         // blank lines and correct letters
101         g.setColor(Color.BLACK);
102         g.setFont(_rightFont);
103         int width = _rightMetrics.charWidth(' ');
104         char [] wordChars = _getWord().toCharArray();
105         for (int i = 0; i < wordChars.length; i++) {
106             if (wordChars[i] != ' ') {
107                 g.drawLine(
108                     HANG_WIDTH * 2 + 2 * i * width + 1, HANG_HEIGHT * 2 + 1,
109                     HANG_WIDTH * 2 + 2 * (i+1) * width - 1,
110                     HANG_HEIGHT * 2 + 1);
111 
112                 if (_gameOver() || _rightGuess.indexOf(wordChars[i]) != -1) {
113                     g.drawChars(
114                         _getWord().toUpperCase().toCharArray(), i, 1,
115                         HANG_WIDTH * 2 + 2 * i * width + width / 2,
116                         HANG_HEIGHT * 2);
117                 }
118             }
119         }
120 
121         // dancing images
122         g.clearRect(_danceX, _danceY, DANCE_WIDTH, DANCE_HEIGHT);
123         if (_danceIndex != -1) {
124             _danceX += DANCE_STEP[_danceIndex % DANCE_STEP.length];
125             _danceX = Math.max(_danceX, _danceXLo);
126             _danceX = Math.min(_danceX, _danceXHi);
127 
128             g.drawImage(
129                 _danceImages[_danceIndex % TOTAL_IMAGES_DANCE],
130                 _danceX, _danceY, this);
131         }
132     }
133 
134     public void keyPressed(KeyEvent e) { }
135 
136     public void keyReleased(KeyEvent e) {
137         char key = e.getKeyChar();
138 
139         if (!_gameOver()) {
140             if ((key < 'a' || key > 'z') ||
141                 (_rightGuess.indexOf(key) != -1) ||
142                 (_wrongGuess.indexOf(key) != -1)) {
143 
144                 play(getCodeBase(), "audio/ding.au");
145             }
146             else {
147                 Character letter = new Character(key);
148                 if (_getWord().indexOf(key) != -1) {
149                     _rightGuess += letter;
150 
151                     for (int i = 0; i < _getWord().length(); i++) {
152                         if (_getWord().charAt(i) == key) {
153                             _guessLength++;
154                         }
155                     }
156 
157                     if (_guessLength == _getWord().length()) {
158                         play(getCodeBase(), "audio/whoopy.au");
159                         _startDance();
160                     }
161                     else {
162                         play(getCodeBase(), "audio/ah.au");
163                     }
164                 }
165                 else {
166                     _wrongGuess += letter;
167 
168                     if (_wrongGuess.length() < TOTAL_IMAGES_HANG) {
169                         play(getCodeBase(), "audio/ooh.au");
170                     }
171                     else {
172                         play(getCodeBase(), "audio/scream.au");
173                     }
174                 }
175             }
176 
177             repaint();
178         }
179         else {
180             _stopDance();
181             _newGame();
182         }
183         e.consume();
184     }
185 
186     public void keyTyped(KeyEvent e) { }
187 
188     private void _initValues() {
189         _tracker = new MediaTracker(this);
190         _danceImages = new Image[TOTAL_IMAGES_DANCE];
191         _hangImages = new Image[TOTAL_IMAGES_HANG];
192         _danceIndex = -1;
193         _danceY = HANG_HEIGHT / 2;
194         _danceX = _danceXLo = HANG_WIDTH * 2;
195         _danceXHi = Math.min(
196             DANCE_WIDTH * 2 + HANG_WIDTH * 2, getWidth() - DANCE_WIDTH);
197         _wordList = new ArrayList();
198         _wrongFont = new Font("MONOSPACED", Font.BOLD, 16);
199         _wrongMetrics = getFontMetrics(_wrongFont);
200         _rightFont = new Font("MONOSPACED", Font.PLAIN, 14);
201         _rightMetrics = getFontMetrics(_rightFont);
202     }
203 
204     private void _buildWordList() {
205         String paramWords = getParameter("word_list");
206         if (paramWords != null) {
207             StringTokenizer st = new StringTokenizer(paramWords, ",");
208 
209             while (st.hasMoreTokens()) {
210                 boolean skip = false;
211 
212                 String token = st.nextToken().trim();
213                 for (int i = 0; i < token.length(); i++) {
214                     char c = token.charAt(i);
215                     if ((c < 'a' || c > 'z') && c != ' ') {
216                         skip = true;
217                         break;
218                     }
219                 }
220 
221                 if (!skip) {
222                     _wordList.add(token);
223                 }
224                 else {
225                     System.err.println("Token '" + token +
226                         "' contains invalid characters");
227                 }
228             }
229         }
230 
231         // Populate list with something
232         if (_wordList.isEmpty()) {
233             _wordList.add("liferay");
234             _wordList.add("open source");
235             _wordList.add("enterprise");
236             _wordList.add("for life");
237         }
238     }
239 
240     private void _loadResources() {
241         for (int i = 0; i < TOTAL_IMAGES_HANG; i++) {
242             Image img = getImage(
243                 getCodeBase(), "images/hanging/h" + (i+1) + ".gif");
244             _tracker.addImage(img, MEDIA_TRACKER_HANG);
245             _hangImages[i] = img;
246         }
247 
248         for (int i = 0; i < TOTAL_IMAGES_DANCE; i++) {
249             Image img = getImage(
250                 getCodeBase(), "images/dancing/d" + (i+1) + ".gif");
251             _tracker.addImage(img, MEDIA_TRACKER_DANCE);
252             _danceImages[i] = img;
253         }
254 
255         _danceMusic = getAudioClip(getCodeBase(), "audio/dance.au");
256     }
257 
258     private void _newGame() {
259         try {
260             _tracker.waitForID(MEDIA_TRACKER_HANG);
261         } catch (InterruptedException ex) {
262             // don't do anything
263         }
264         _tracker.checkAll(true);
265 
266         _rightGuess = new String();
267         _wrongGuess = new String();
268         _wordIndex = (int) (Math.random() * 100) % _wordList.size();
269 
270         _guessLength = 0;
271         String word = _getWord();
272         for (int i = 0; i < word.length(); i++) {
273             if (word.charAt(i) == ' ') {
274                 _guessLength++;
275             }
276         }
277 
278         repaint();
279     }
280 
281     private boolean _gameOver() {
282         return
283             _guessLength == _getWord().length() ||
284             _wrongGuess.length() == TOTAL_IMAGES_HANG;
285     }
286 
287     private String _getWord() {
288         return (String) _wordList.get(_wordIndex);
289     }
290 
291     private void _startDance() {
292         _danceThread = new DanceThread();
293         _danceThread.start();
294     }
295 
296     private void _stopDance() {
297         if (_danceThread != null) {
298             _danceThread.interrupt();
299             _danceThread = null;
300         }
301     }
302 
303     private class DanceThread extends Thread {
304         public void run() {
305             _danceX = _danceXLo;
306             _danceX += (int) (Math.random() * 100) % (_danceXHi - _danceXLo);
307             _danceIndex = 0;
308             _danceMusic.loop();
309             try {
310                 _tracker.waitForID(MEDIA_TRACKER_DANCE);
311 
312                 for (;;) {
313                     _danceIndex++;
314                     repaint();
315                     sleep(250);
316                 }
317             }
318             catch (InterruptedException ex) {
319                 // do nothing
320             }
321             _danceIndex = -1;
322             _danceMusic.stop();
323             repaint();
324         }
325     }
326 
327     // Media variables
328     private MediaTracker _tracker;
329     private AudioClip _danceMusic;
330     private Image[] _danceImages;
331     private Image[] _hangImages;
332     private Thread _danceThread;
333     private int _danceIndex;
334     private int _danceY;
335     private int _danceX;
336     private int _danceXLo;
337     private int _danceXHi;
338 
339     // Specific to the words
340     private List _wordList;
341     private int _wordIndex;
342     private String _rightGuess;
343     private String _wrongGuess;
344     private int _guessLength;
345 
346     // Font values
347     private Font _wrongFont;
348     private FontMetrics _wrongMetrics;
349     private Font _rightFont;
350     private FontMetrics _rightMetrics;
351 
352 }