View Javadoc

1   /*
2    * AI Soccer Project - network gaming environment for AI warriors.
3    * Copyright (C) 2001-2004  Marcin Werla, Pawel Widera
4    *
5    * This program is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU General Public License
7    * as published by the Free Software Foundation; either version 2
8    * of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with this program; if not, you can find it here:
17   * http://www.gnu.org/licenses/gpl.html
18   */
19  
20  package aigames.soccer.observer;
21  
22  import aigames.soccer.InvalidMoveException;
23  import aigames.soccer.Move;
24  
25  import aigames.soccer.field.BasicGameField;
26  import aigames.soccer.field.FieldPoint;
27  import aigames.soccer.field.GameField;
28  import aigames.soccer.field.constructor.Constructor;
29  import aigames.soccer.field.constructor.StandardConstructor;
30  
31  import foxtrot.Task;
32  import foxtrot.Worker;
33  
34  import org.apache.log4j.BasicConfigurator;
35  import org.apache.log4j.Level;
36  import org.apache.log4j.LogManager;
37  import org.apache.log4j.Logger;
38  
39  import org.dom4j.Document;
40  import org.dom4j.Element;
41  import org.dom4j.Node;
42  
43  import java.awt.Color;
44  import java.awt.Dimension;
45  import java.awt.Graphics;
46  import java.awt.Image;
47  import java.awt.Insets;
48  import java.awt.Point;
49  
50  import java.text.DateFormat;
51  import java.text.ParseException;
52  import java.text.SimpleDateFormat;
53  
54  import java.util.Date;
55  import java.util.Iterator;
56  import java.util.List;
57  import java.util.Vector;
58  
59  import javax.swing.Box;
60  import javax.swing.BoxLayout;
61  import javax.swing.JFrame;
62  
63  
64  /***
65   * The observer main class.
66   * @version $Id: Observer.java,v 1.15 2004/05/08 21:55:28 mwerla Exp $
67   */
68  public class Observer extends JFrame {
69      private static Logger logger = Logger.getLogger(Observer.class);
70      private static Color bgColor = new Color(0, 204, 0);
71      private Communication comm = new Communication();
72      private Thread commThread = null;
73      private GameField field;
74      private int scale;
75      private int[] translate = new int[2];
76      private int turn;
77      private String player1Name;
78      private String player2Name;
79      private Date player1Time;
80      private Date player2Time;
81      private DateFormat df = new SimpleDateFormat("HH:mm:ss");
82      private javax.swing.JPanel jContentPane = null;
83      private javax.swing.JPanel leftPanel = null;
84      private FieldPanel fieldPanel = null;
85      private aigames.soccer.observer.PlayerPanel player1Panel = null;
86      private aigames.soccer.observer.PlayerPanel player2Panel = null;
87      private aigames.soccer.observer.OptionsPane optionsPane = null;
88  
89      /***
90       * This is the default constructor.
91       */
92      public Observer() {
93          super();
94          initialize();
95          comm.setObserver(this);
96      }
97  
98      /***
99       * The observer main function.
100      * @param args
101      */
102     public static void main(String[] args) {
103         BasicConfigurator.configure();
104         LogManager.getRootLogger().setLevel(Level.INFO);
105 
106         Observer frame = new Observer();
107     }
108 
109     /***
110      * This method initializes this.
111      */
112     private void initialize() {
113         this.setSize(750, 400);
114         this.setContentPane(getJContentPane());
115         this.setTitle("Soccer Observer");
116         this.setVisible(true);
117         this.addWindowListener(new java.awt.event.WindowAdapter() {
118                 public void windowClosing(java.awt.event.WindowEvent e) {
119                     System.exit(0);
120                 }
121             });
122     }
123 
124     /***
125      * This method initializes jContentPane.
126      * @return javax.swing.JPanel
127      */
128     private javax.swing.JPanel getJContentPane() {
129         if (jContentPane == null) {
130             jContentPane = new javax.swing.JPanel();
131             jContentPane.setLayout(new java.awt.BorderLayout());
132             jContentPane.add(getLeftPanel(), java.awt.BorderLayout.WEST);
133             jContentPane.add(getFieldPanel(), java.awt.BorderLayout.CENTER);
134         }
135 
136         return jContentPane;
137     }
138 
139     /***
140      * This method initializes jPanel.
141      * @return javax.swing.JPanel
142      */
143     private javax.swing.JPanel getLeftPanel() {
144         if (leftPanel == null) {
145             leftPanel = new javax.swing.JPanel();
146             leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.PAGE_AXIS));
147             leftPanel.add(getPlayer1Panel(), null);
148             leftPanel.add(Box.createRigidArea(new Dimension(0, 2)));
149             leftPanel.add(getPlayer2Panel(), null);
150             leftPanel.add(Box.createVerticalGlue());
151             leftPanel.add(getOptionsPane(), null);
152             leftPanel.setBackground(bgColor);
153         }
154 
155         return leftPanel;
156     }
157 
158     /***
159      * This method initializes player1Panel.
160      * @return aigames.soccer.observer.PlayerPanel
161      */
162     private aigames.soccer.observer.PlayerPanel getPlayer1Panel() {
163         if (player1Panel == null) {
164             player1Panel = new aigames.soccer.observer.PlayerPanel();
165         }
166 
167         return player1Panel;
168     }
169 
170     /***
171      * This method initializes player2Panel.
172      * @return aigames.soccer.observer.PlayerPanel
173      */
174     private aigames.soccer.observer.PlayerPanel getPlayer2Panel() {
175         if (player2Panel == null) {
176             player2Panel = new aigames.soccer.observer.PlayerPanel();
177         }
178 
179         return player2Panel;
180     }
181 
182     /***
183      * This method initializes optionsPane.
184      * @return aigames.soccer.observer.OptionsPane
185      */
186     private aigames.soccer.observer.OptionsPane getOptionsPane() {
187         if (optionsPane == null) {
188             optionsPane = new aigames.soccer.observer.OptionsPane();
189             optionsPane.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
190                     public void propertyChange(java.beans.PropertyChangeEvent e) {
191                         if (e.getPropertyName().equals("connected")) {
192                             connect(e.getNewValue());
193                         } else if (e.getPropertyName().equals("selected")) {
194                             selectGame();
195                         }
196                     }
197                 });
198         }
199 
200         return optionsPane;
201     }
202 
203     /***
204      * This method initializes fieldPanel.
205      * @return javax.swing.JPanel
206      */
207     private javax.swing.JPanel getFieldPanel() {
208         if (fieldPanel == null) {
209             fieldPanel = new FieldPanel();
210 
211             Dimension size = fieldPanel.getPreferredSize();
212             size.width += 20;
213             fieldPanel.setMaximumSize(size);
214             fieldPanel.setBackground(bgColor);
215         }
216 
217         return fieldPanel;
218     }
219 
220     /***
221      * Connection/disconnection event handler.
222      * @param value True for connection, false for disconnection.
223      */
224     private void connect(Object value) {
225         if (value.toString().equals("true")) {
226             //@TODO set status to "connecting..."
227             String[] gamesList = null;
228 
229             try {
230                 gamesList = (String[]) Worker.post(new Task() {
231                             public Object run() throws Exception {
232                                 comm.connect(optionsPane.getIp(),
233                                     optionsPane.getPort());
234                                 comm.sayHello("JavaObserver");
235 
236                                 return comm.getGamesList();
237                             }
238                         });
239             } catch (Exception e) {
240                 //@TODO set status to "connection failed: " + e.getMessage. 
241                 optionsPane.setConnected(false);
242 
243                 return;
244             }
245 
246             optionsPane.setGamesList(gamesList);
247             optionsPane.setConnected(true);
248         } else {
249             comm.shutdown();
250 
251             if (null != commThread) {
252                 try {
253                     commThread.interrupt();
254 
255                     while (commThread.isAlive()) {
256                         Thread.sleep(100);
257                     }
258                 } catch (InterruptedException e) {
259                     //we don't give a fuck to interruptions
260                     //@TODO consider if we should do so...
261                 }
262             }
263 
264             commThread = null;
265 
266             try {
267                 comm.disconnect();
268                 optionsPane.setConnected(false);
269             } catch (Exception e) {
270                 //@TODO set status to "socket closing error:" + e.getMessage
271             }
272         }
273     }
274 
275     private void selectGame() {
276         //comm.chooseGame(optionsPane.getGame());
277         optionsPane.setSelected(true);
278         player1Time = null;
279         player1Panel.setPlayerName("PlayerName");
280         player1Panel.setMovesAverage(0);
281         player1Panel.setMovesTotal(0);
282         player1Panel.setTime("00:00:00");
283         player2Panel.setPlayerName("PlayerName");
284         player2Time = null;
285         player2Panel.setMovesAverage(0);
286         player2Panel.setMovesTotal(0);
287         player2Panel.setTime("00:00:00");
288         commThread = new Thread(comm);
289         commThread.start();
290     }
291 
292     /***
293      * Process a message and updates the field.
294      * @param doc dom4j Document
295      */
296     public void processMessage(Document doc) {
297         int playersCount = 0;
298         String type = doc.valueOf("//message/@type");
299 
300         if (type.equals("log")) {
301             Element root = doc.getRootElement();
302 
303             for (Iterator i = root.elementIterator(); i.hasNext();) {
304                 Element element = (Element) i.next();
305                 String name = element.getName();
306 
307                 if ("name".equals(name)) {
308                     String player = element.getText();
309 
310                     if (1 == ++playersCount) {
311                         player1Panel.setPlayerName(player);
312                         player1Name = player;
313                     } else {
314                         player2Panel.setPlayerName(player);
315                         player2Name = player;
316                     }
317                 } else if ("map".equals(name)) {
318                     int width = Integer.parseInt(element.attributeValue("width"));
319                     int height = Integer.parseInt(element.attributeValue(
320                                 "height"));
321                     int goal = Integer.parseInt(element.attributeValue("goal"));
322 
323                     Constructor fieldConstructor = null;
324 
325                     try {
326                         fieldConstructor = new StandardConstructor(width,
327                                 height, goal);
328                     } catch (IllegalArgumentException e) {
329                         logger.error("configuration: " + e.getMessage());
330                     }
331 
332                     field = new BasicGameField(fieldConstructor);
333                     field.startGame();
334                 } else if ("begin".equals(name)) {
335                     String firstPlayer = element.getText();
336                     turn = 1;
337 
338                     if (firstPlayer.equals(player2Name)) {
339                         turn = 2;
340                     }
341                 } else if ("move".equals(name)) {
342                     try {
343                         setTime(element.attributeValue("time"));
344                     } catch (ParseException e) {
345                         logger.error("time parsing: " + e.getMessage());
346                     }
347 
348                     markMove(element);
349                 } else if ("error".equals(name)) {
350                     //@TODO implement error tag handling
351                 } else if ("winner".equals(name)) {
352                     String winner = element.getText();
353 
354                     //@TODO implement winner showing
355                     connect("false");
356                 }
357             }
358 
359             drawField();
360         } else {
361             logger.error("Incorrect message type: " + type);
362         }
363     }
364 
365     private void setTime(String time) throws ParseException {
366         if (1 == turn) {
367             if (null == player1Time) {
368                 player1Time = df.parse(time);
369             } else {
370                 long diff = df.parse(time).getTime() - player1Time.getTime();
371                 Date date = new Date(diff - (3600 * 1000));
372                 player1Panel.setTime(df.format(date));
373             }
374         } else if (2 == turn) {
375             if (null == player2Time) {
376                 player2Time = df.parse(time);
377             } else {
378                 long diff = df.parse(time).getTime() - player2Time.getTime();
379                 Date date = new Date(diff - (3600 * 1000));
380                 player2Panel.setTime(df.format(date));
381             }
382         }
383     }
384 
385     private void markMove(Node node) {
386         List directionsList = node.selectNodes("//type");
387         Vector movesVector = new Vector();
388 
389         for (Iterator j = directionsList.iterator(); j.hasNext();) {
390             Node directionNode = (Node) j.next();
391             movesVector.add(new Integer(directionNode.getText()));
392         }
393 
394         try {
395             Move move = new Move((Integer[]) movesVector.toArray(new Integer[0]));
396             field.makeMove(move, turn);
397         } catch (InvalidMoveException e) {
398             logger.error("Invalid move.");
399         }
400 
401         //draw stats
402         if (1 == turn) {
403             player1Panel.setMovesAverage(field.getAvgMoveLength(1));
404             player1Panel.setMovesTotal(field.getNumberOfMoves(1));
405         } else {
406             player2Panel.setMovesAverage(field.getAvgMoveLength(2));
407             player2Panel.setMovesTotal(field.getNumberOfMoves(2));
408         }
409 
410         //switch turn
411         if (1 == turn) {
412             turn = 2;
413         } else if (2 == turn) {
414             turn = 1;
415         }
416     }
417 
418     private void drawField() {
419         Insets insets = fieldPanel.getInsets();
420         int imageWidth = fieldPanel.getWidth() - insets.left - insets.right;
421         int imageHeight = fieldPanel.getHeight() - insets.top - insets.bottom;
422 
423         Image buffer = createImage(imageWidth, imageHeight);
424         Graphics g = buffer.getGraphics();
425 
426         int fieldWidth = field.getMaximumXIndex();
427         int fieldHeight = field.getMaximumYIndex();
428 
429         int scaleX = imageWidth / fieldWidth;
430         int scaleY = imageHeight / fieldHeight;
431         translate[0] = 0;
432         translate[1] = 0;
433 
434         if (scaleX > scaleY) {
435             scale = scaleY;
436             translate[0] = (imageWidth - (fieldWidth * scaleY)) / 2;
437         } else if (scaleX > scaleY) {
438             scale = scaleX;
439             translate[1] = (imageHeight - (fieldHeight * scaleY)) / 2;
440         }
441 
442         for (int x = 0; x <= fieldWidth; ++x) {
443             for (int y = 0; y <= fieldHeight; ++y) {
444                 FieldPoint p = field.getFieldPoint(x, y);
445 
446                 for (int i = 1; i < 5; ++i) {
447                     Point begin = new Point(x, y);
448                     Point end = (Point) begin.clone();
449                     int[] v = Move.getTranslation(new Integer(i));
450                     end.translate(v[0], v[1]);
451 
452                     begin.x *= scale;
453                     begin.y *= scale;
454                     end.x *= scale;
455                     end.y *= scale;
456                     begin.translate(translate[0], translate[1]);
457                     end.translate(translate[0], translate[1]);
458 
459                     drawLine(begin, end, p.getEdgeColor(i), g);
460                 }
461             }
462         }
463 
464         fieldPanel.setImage(buffer);
465         fieldPanel.repaint();
466     }
467 
468     private void drawLine(Point begin, Point end, int edgeColor, Graphics g) {
469         Color color = null;
470 
471         if (-2 == edgeColor) {
472             return;
473         } else if (-1 == edgeColor) {
474             color = new Color(0, 0, 0);
475         } else if (0 == edgeColor) {
476             //return;
477             color = bgColor;
478         } else if (1 == edgeColor) {
479             color = new Color(255, 0, 0);
480         } else if (2 == edgeColor) {
481             color = new Color(0, 0, 255);
482         }
483 
484         g.setColor(color);
485 
486         int thickness = scale / 20;
487 
488         if (begin.y == end.y) {
489             for (int i = -thickness; i <= thickness; ++i) {
490                 g.drawLine(begin.x, begin.y + i, end.x, end.y + i);
491             }
492         } else if (begin.x == end.x) {
493             for (int i = -thickness; i <= thickness; ++i) {
494                 g.drawLine(begin.x + i, begin.y, end.x + i, end.y);
495             }
496         } else if (begin.y > end.y) {
497             for (int i = 1; i <= thickness; ++i) {
498                 g.drawLine(begin.x, begin.y - i, end.x - i, end.y);
499             }
500 
501             for (int i = 1; i <= thickness; ++i) {
502                 g.drawLine(begin.x + i, begin.y, end.x, end.y + i);
503             }
504 
505             g.drawLine(begin.x, begin.y, end.x, end.y);
506         } else if (begin.y < end.y) {
507             for (int i = 1; i <= thickness; ++i) {
508                 g.drawLine(begin.x + i, begin.y, end.x, end.y - i);
509             }
510 
511             for (int i = 1; i <= thickness; ++i) {
512                 g.drawLine(begin.x, begin.y + i, end.x - i, end.y);
513             }
514 
515             g.drawLine(begin.x, begin.y, end.x, end.y);
516         }
517     }
518 }
519 
520 
521 //  @jve:visual-info  decl-index=0 visual-constraint="10,10"