View Javadoc

1   /**
2    * This program is free software: you can redistribute it and/or modify
3    * it under the terms of the GNU General Public License as published by
4    * the Free Software Foundation, version 3.
5    *
6    * This program is distributed in the hope that it will be useful,
7    * but WITHOUT ANY WARRANTY; without even the implied warranty of
8    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9    * GNU General Public License for more details.
10   *
11   * You should have received a copy of the GNU General Public License
12   * along with this program. If not, see <http://www.gnu.org/licenses/>.
13   */
14  
15  package net.curre.prefcount.gui;
16  
17  import java.awt.geom.Point2D;
18  import java.util.HashMap;
19  import java.util.Map;
20  
21  import net.curre.prefcount.gui.type.Place;
22  import static net.curre.prefcount.gui.type.Place.EAST;
23  import static net.curre.prefcount.gui.type.Place.NORTH;
24  import static net.curre.prefcount.gui.type.Place.SOUTH;
25  import static net.curre.prefcount.gui.type.Place.WEST;
26  import net.curre.prefcount.gui.type.ScoreItem;
27  import static net.curre.prefcount.gui.type.ScoreItem.FINAL_MOUNT;
28  import static net.curre.prefcount.gui.type.ScoreItem.FINAL_SCORE;
29  import static net.curre.prefcount.gui.type.ScoreItem.PLAYER_MOUNT;
30  import static net.curre.prefcount.gui.type.ScoreItem.PLAYER_NAME;
31  import static net.curre.prefcount.gui.type.ScoreItem.PLAYER_POOL;
32  import static net.curre.prefcount.gui.type.ScoreItem.WHIST_EAST;
33  import static net.curre.prefcount.gui.type.ScoreItem.WHIST_EAST_SALDO;
34  import static net.curre.prefcount.gui.type.ScoreItem.WHIST_NORTH;
35  import static net.curre.prefcount.gui.type.ScoreItem.WHIST_NORTH_SALDO;
36  import static net.curre.prefcount.gui.type.ScoreItem.WHIST_SALDO_TOTAL;
37  import static net.curre.prefcount.gui.type.ScoreItem.WHIST_SOUTH;
38  import static net.curre.prefcount.gui.type.ScoreItem.WHIST_SOUTH_SALDO;
39  import static net.curre.prefcount.gui.type.ScoreItem.WHIST_WEST;
40  import static net.curre.prefcount.gui.type.ScoreItem.WHIST_WEST_SALDO;
41  
42  /**
43   * This class is a utility to determine locaitons of
44   * all items on the score board given the current dimensions
45   * of the table/board.
46   * <p/>
47   * Created date: Jun 16, 2007
48   *
49   * @author Yevgeny Nyden
50   */
51  public class ScoreBoardLocationsMap {
52  
53    /** Number of pixels to leave near the borders. */
54    public static final int MARGIN = 7;
55  
56    /** Current board width. */
57    public int width;
58  
59    /** Current board height. */
60    public int height;
61  
62    /** Center's current X coordinate. */
63    public int centerX;
64  
65    /** Center's current X coordinate. */
66    public int centerY;
67  
68    /** Computed 2/5 of half width value. */
69    public int twoFifthX;
70  
71    /** Computed 3/5 of the half width value. */
72    public int threeFifthX;
73  
74    /** Computed 1/5 of the half height value. */
75    public int oneFifthY;
76  
77    /** Computed 2/5 of the half height value. */
78    public int twoFifthY;
79  
80    /** Computed 3/5 of the half height value. */
81    public int threeFifthY;
82  
83    /** Computed whist-pool divider X coordinate. */
84    public int whistPoolDividerX;
85  
86    /** Computed whist-pool divider Y coordinate. */
87    public int whistPoolDividerY;
88  
89    /** Computed pool-mount divider X coordinate. */
90    public int poolMountDividerX;
91  
92    /** Computed pool-mount divider Y coordinate. */
93    public int poolMountDividerY;
94  
95    /** Computed value for whist division lines. */
96    public int whistDividerX1;
97  
98    /** Computed value for whist division lines. */
99    public int whistDividerX2;
100 
101   /** Computed value for whist division lines. */
102   public int whistDividerY1;
103 
104   /** Computed value for whist division lines. */
105   public int whistDividerY2;
106 
107   /** Reference to the score board panel. */
108   private ScoreBoardPanel scoreBoard;
109 
110   /**
111    * Total number of players in the game
112    * (supported values are 3 and 4)
113    */
114   private int numberOfPlayers;
115 
116   /**
117    * Map for each player in the game that
118    * store locations of all items on the score board.
119    */
120   private Map<Place, Map<ScoreItem, Point2D.Double>> locationMaps;
121 
122   /**
123    * Map that holds the other player's whist score items
124    * for each player currently in the game.
125    */
126   private Map<Place, ScoreItem[]> othersWhists;
127 
128   /**
129    * Map that holds the other player's whist saldo score items
130    * for each player currently in the game.
131    */
132   private Map<Place, ScoreItem[]> othersWhistSaldos;
133 
134   /**
135    * Constructor. After the number of players in the
136    * game is known (scoreBoard.results.getPlayerStats() is created),
137    * the initialize() method must be called.
138    *
139    * @param scoreBoard Reference to the score board panel.
140    */
141   public ScoreBoardLocationsMap(ScoreBoardPanel scoreBoard) {
142     this.scoreBoard = scoreBoard;
143   }
144 
145   /**
146    * Method that initializes properties and computes
147    * the locations for the first time. This method must
148    * be called as soon as the number of players is set
149    * (scoreBoard.results.getPlayerStats() is created).
150    *
151    * @param numberOfPlayers Number of players in the game.
152    * @throws IllegalArgumentException If number of players is not supported.
153    */
154   public void initialize(int numberOfPlayers) {
155     if (numberOfPlayers != 3 && numberOfPlayers != 4) {
156       throw new IllegalArgumentException(numberOfPlayers + " number of players is NOT supported!");
157     }
158 
159     this.numberOfPlayers = numberOfPlayers;
160     this.locationMaps = new HashMap<Place, Map<ScoreItem, Point2D.Double>>();
161     for (Place place : Place.getPlaces(numberOfPlayers)) {
162       this.locationMaps.put(place, new HashMap<ScoreItem, Point2D.Double>());
163     }
164     this.width = 0;
165     this.height = 0;
166 
167     this.othersWhists = new HashMap<Place, ScoreItem[]>(numberOfPlayers);
168     this.othersWhistSaldos = new HashMap<Place, ScoreItem[]>(numberOfPlayers);
169     for (Place place : Place.getPlaces(numberOfPlayers)) {
170       this.othersWhists.put(place, getOtherWhistItemsHelper(place));
171       this.othersWhistSaldos.put(place, getOtherWhistSaldoItemsHelper(place));
172     }
173 
174     final int newWidth = this.scoreBoard.getWidth();
175     final int newHeight = this.scoreBoard.getHeight();
176 
177     computeLocations(newWidth, newHeight, 0, 0, true);
178   }
179 
180   /**
181    * Getter for the locations maps for the given player.
182    *
183    * @param place Players place.
184    * @return The locations maps for the given player.
185    */
186   public Map<ScoreItem, Point2D.Double> getLocationsMap(Place place) {
187     return this.locationMaps.get(place);
188   }
189 
190   /**
191    * Computes or recomputes if necessary locations of
192    * all items on the score board and stores the values
193    * in the locationsMap map.
194    *
195    * @param newWidth  new width of the score board.
196    * @param newHeight new height of the score board.
197    * @param offsetX   canvas X coordinate.
198    * @param offsetY   canvas Y coordinate.
199    * @param force     true will ensure that all locations will get recomputed;
200    *                  when false, the method does nothing if the scoreboard width.
201    */
202   public void computeLocations(int newWidth, int newHeight, int offsetX, int offsetY, boolean force) {
203 
204     // if the frame size didn't change from last time,
205     // returning - we can use the already computed values
206     if (force == false && this.width == newWidth && this.height == newHeight) {
207       return;
208     }
209 
210     this.width = newWidth;
211     this.height = newHeight;
212     final int halfWidth = newWidth / 2;
213     final int halfHeight = newHeight / 2;
214 
215     this.centerX = newWidth / 2 + offsetX;
216     this.centerY = newHeight / 2 + offsetY;
217     this.twoFifthX = 2 * halfWidth / 5 + offsetX;
218     this.oneFifthY = halfHeight / 5 + offsetY;
219     this.twoFifthY = 2 * halfHeight / 5 + 2 + offsetY;
220     this.threeFifthX = 3 * halfWidth / 5 + 2 + offsetX;
221     this.threeFifthY = 3 * halfHeight / 5 + 2 + offsetY;
222     this.whistPoolDividerX = newWidth - (2 * halfWidth / 5) - 1 + offsetX;
223     this.whistPoolDividerY = newHeight - (2 * halfHeight / 5) - 2 + offsetY;
224     this.poolMountDividerX = newWidth - (3 * halfWidth / 5) - 2 + offsetX;
225     this.poolMountDividerY = newHeight - (3 * halfHeight / 5) - 2 + offsetY;
226 
227     final int whistThirdX = 2 * halfWidth / 5;
228     this.whistDividerX1 = this.twoFifthX + whistThirdX - (whistThirdX / 5);
229     this.whistDividerX2 = this.whistPoolDividerX - whistThirdX + (whistThirdX / 5);
230     final int whistThirdY = 2 * halfHeight / 5;
231     this.whistDividerY1 = this.twoFifthY + whistThirdY - (whistThirdY / 5);
232     this.whistDividerY2 = this.whistPoolDividerY - whistThirdY + (whistThirdY / 5);
233 
234     final int topY = MARGIN + 17 + offsetY;
235     final int bottomY = newHeight - MARGIN - 5 + offsetY;
236 
237     // generating score board items locations
238     switch (this.numberOfPlayers) {
239       case (3):
240         // computing the EAST player items locations
241         Map<ScoreItem, Point2D.Double> m = this.locationMaps.get(EAST);
242         m.put(PLAYER_NAME, new Point2D.Double(this.centerX + 11, this.centerY));
243         m.put(PLAYER_MOUNT, new Point2D.Double(this.centerX + 14, topY));
244         m.put(PLAYER_POOL, new Point2D.Double((newWidth / 2) + this.twoFifthX + 10, topY));
245         m.put(WHIST_SOUTH, new Point2D.Double(this.whistPoolDividerX + 20, this.poolMountDividerY));
246         m.put(WHIST_WEST, new Point2D.Double(this.whistPoolDividerX + 20, topY + 23));
247         m.put(WHIST_SOUTH_SALDO, new Point2D.Double(this.whistPoolDividerX + 30, this.poolMountDividerY + 40));
248         m.put(WHIST_WEST_SALDO, new Point2D.Double(this.whistPoolDividerX + 30, topY + 53));
249         m.put(WHIST_SALDO_TOTAL, new Point2D.Double(this.whistPoolDividerX + 20, this.centerY));
250         m.put(FINAL_MOUNT, new Point2D.Double(this.centerX + 40, (halfHeight / 2) + offsetY));
251         m.put(FINAL_SCORE, new Point2D.Double(this.poolMountDividerX + 10, this.centerY - 30));
252 
253         // computing the SOUTH player items locations
254         m = this.locationMaps.get(SOUTH);
255         m.put(PLAYER_NAME, new Point2D.Double(this.centerX - 7, this.centerY + 24));
256         m.put(PLAYER_MOUNT, new Point2D.Double(this.threeFifthX + 50, this.poolMountDividerY - 8));
257         m.put(PLAYER_POOL, new Point2D.Double(this.twoFifthX + 50, this.whistPoolDividerY - 10));
258         m.put(WHIST_EAST, new Point2D.Double(this.centerX + 20, bottomY));
259         m.put(WHIST_WEST, new Point2D.Double(MARGIN + 50 + offsetX, bottomY));
260         m.put(WHIST_EAST_SALDO, new Point2D.Double(this.whistPoolDividerX - 20, bottomY - 26));
261         m.put(WHIST_WEST_SALDO, new Point2D.Double(this.twoFifthX - 5, bottomY - 26));
262         m.put(WHIST_SALDO_TOTAL, new Point2D.Double(this.centerX + 20, bottomY - 30));
263         m.put(FINAL_MOUNT, new Point2D.Double(this.centerX, (newHeight / 2) + this.oneFifthY + 10));
264         m.put(FINAL_SCORE, new Point2D.Double(this.centerX, this.whistPoolDividerY - 10));
265 
266         // computing the WEST player items locations
267         m = this.locationMaps.get(WEST);
268         m.put(PLAYER_NAME, new Point2D.Double(this.centerX - 20, this.centerY));
269         m.put(PLAYER_MOUNT, new Point2D.Double(this.threeFifthX + 12, topY));
270         m.put(PLAYER_POOL, new Point2D.Double(this.twoFifthX + 10, topY));
271         m.put(WHIST_EAST, new Point2D.Double(MARGIN + 20 + offsetX, topY + 23));
272         m.put(WHIST_SOUTH, new Point2D.Double(MARGIN + 20 + offsetX, this.poolMountDividerY));
273         m.put(WHIST_EAST_SALDO, new Point2D.Double(MARGIN + 40 + offsetX, topY + 53));
274         m.put(WHIST_SOUTH_SALDO, new Point2D.Double(MARGIN + 40 + offsetX, this.poolMountDividerY + 40));
275         m.put(WHIST_SALDO_TOTAL, new Point2D.Double(MARGIN + 44 + offsetX, this.centerY));
276         m.put(FINAL_MOUNT, new Point2D.Double(this.threeFifthX + 20, halfHeight / 2 + offsetY));
277         m.put(FINAL_SCORE, new Point2D.Double(this.twoFifthX + 10, this.centerY - 30));
278         break;
279 
280       case (4):
281         // computing the NORTH player items locations
282         m = this.locationMaps.get(NORTH);
283         m.put(PLAYER_NAME, new Point2D.Double(this.centerX - 5, this.centerY - 10));
284         m.put(PLAYER_MOUNT, new Point2D.Double(this.threeFifthX + 40, this.threeFifthY + 22));
285         m.put(PLAYER_POOL, new Point2D.Double(this.twoFifthX + 50, this.twoFifthY + 22));
286         m.put(WHIST_EAST, new Point2D.Double(this.whistDividerX2 + 20, topY));
287         m.put(WHIST_SOUTH, new Point2D.Double(this.whistDividerX1 + 20, topY));
288         m.put(WHIST_WEST, new Point2D.Double(MARGIN + 40 + offsetX, topY));
289         m.put(WHIST_EAST_SALDO, new Point2D.Double(this.whistDividerX2 + 60, topY + 20));
290         m.put(WHIST_SOUTH_SALDO, new Point2D.Double(this.whistDividerX1 + 60, topY + 20));
291         m.put(WHIST_WEST_SALDO, new Point2D.Double(MARGIN + 80 + offsetX, topY + 20));
292         m.put(WHIST_SALDO_TOTAL, new Point2D.Double(this.centerX, topY + 44));
293         m.put(FINAL_MOUNT, new Point2D.Double(this.centerX, this.threeFifthY + 46));
294         m.put(FINAL_SCORE, new Point2D.Double(this.centerX, this.twoFifthY + 30));
295 
296         // computing the EAST player items locations
297         m = this.locationMaps.get(EAST);
298         m.put(PLAYER_NAME, new Point2D.Double(this.centerX + 14, this.centerY + 6));
299         m.put(PLAYER_MOUNT, new Point2D.Double(this.poolMountDividerX - 44, this.threeFifthY + 56));
300         m.put(PLAYER_POOL, new Point2D.Double(this.whistPoolDividerX - 32, this.twoFifthY + 56));
301         m.put(WHIST_NORTH, new Point2D.Double(this.whistPoolDividerX + 20, this.twoFifthY + 10));
302         m.put(WHIST_SOUTH, new Point2D.Double(this.whistPoolDividerX + 20, this.whistDividerY2 + 30));
303         m.put(WHIST_WEST, new Point2D.Double(this.whistPoolDividerX + 20, this.whistDividerY1 + 30));
304         m.put(WHIST_NORTH_SALDO, new Point2D.Double(this.whistPoolDividerX + 30, this.twoFifthY + 36));
305         m.put(WHIST_SOUTH_SALDO, new Point2D.Double(this.whistPoolDividerX + 30, this.whistDividerY2 + 56));
306         m.put(WHIST_WEST_SALDO, new Point2D.Double(this.whistPoolDividerX + 30, this.whistDividerY1 + 56));
307         m.put(WHIST_SALDO_TOTAL, new Point2D.Double(this.whistPoolDividerX + 20, this.centerY + 20));
308         m.put(FINAL_MOUNT, new Point2D.Double(this.centerX + 70, this.centerY + 34));
309         m.put(FINAL_SCORE, new Point2D.Double(this.poolMountDividerX + 10, this.centerY - 10));
310 
311         // computing the SOUTH player items locations
312         m = this.locationMaps.get(SOUTH);
313         m.put(PLAYER_NAME, new Point2D.Double(this.centerX - 7, this.centerY + 24));
314         m.put(PLAYER_MOUNT, new Point2D.Double(this.threeFifthX + 40, this.poolMountDividerY - 8));
315         m.put(PLAYER_POOL, new Point2D.Double(this.twoFifthX + 50, this.whistPoolDividerY - 10));
316         m.put(WHIST_EAST, new Point2D.Double(this.whistDividerX2 + 20, bottomY));
317         m.put(WHIST_NORTH, new Point2D.Double(this.whistDividerX1 + 20, bottomY));
318         m.put(WHIST_WEST, new Point2D.Double(MARGIN + 40 + offsetX, bottomY));
319         m.put(WHIST_EAST_SALDO, new Point2D.Double(this.whistDividerX2 + 60, bottomY - 20));
320         m.put(WHIST_NORTH_SALDO, new Point2D.Double(this.whistDividerX1 + 60, bottomY - 20));
321         m.put(WHIST_WEST_SALDO, new Point2D.Double(MARGIN + 80 + offsetX, bottomY - 20));
322         m.put(WHIST_SALDO_TOTAL, new Point2D.Double(this.centerX, bottomY - 44));
323         m.put(FINAL_MOUNT, new Point2D.Double(this.centerX, this.centerY + (halfHeight / 5) + 16));
324         m.put(FINAL_SCORE, new Point2D.Double(this.centerX, this.whistPoolDividerY - 10));
325 
326         // computing the WEST player items locations
327         m = this.locationMaps.get(WEST);
328         m.put(PLAYER_NAME, new Point2D.Double(this.centerX - 22, this.centerY + 6));
329         m.put(PLAYER_MOUNT, new Point2D.Double(this.threeFifthX + 10, this.threeFifthY + 56));
330         m.put(PLAYER_POOL, new Point2D.Double(this.twoFifthX + 10, this.twoFifthY + 56));
331         m.put(WHIST_NORTH, new Point2D.Double(MARGIN + 20 + offsetX, this.twoFifthY + 10));
332         m.put(WHIST_SOUTH, new Point2D.Double(MARGIN + 20 + offsetX, this.whistDividerY2 + 30));
333         m.put(WHIST_EAST, new Point2D.Double(MARGIN + 20 + offsetX, this.whistDividerY1 + 30));
334         m.put(WHIST_NORTH_SALDO, new Point2D.Double(MARGIN + 40 + offsetX, this.twoFifthY + 36));
335         m.put(WHIST_SOUTH_SALDO, new Point2D.Double(MARGIN + 40 + offsetX, this.whistDividerY2 + 56));
336         m.put(WHIST_EAST_SALDO, new Point2D.Double(MARGIN + 40 + offsetX, this.whistDividerY1 + 56));
337         m.put(WHIST_SALDO_TOTAL, new Point2D.Double(MARGIN + 32 + offsetX, this.centerY + 20));
338         m.put(FINAL_MOUNT, new Point2D.Double(this.threeFifthX + 20, this.centerY + 34));
339         m.put(FINAL_SCORE, new Point2D.Double(this.twoFifthX + 10, this.centerY - 10));
340         break;
341 
342       default:
343         throw new UnsupportedOperationException(this.numberOfPlayers +
344                                                 " number of players is NOT supported!");
345     }
346   }
347 
348   /**
349    * Gets whist score items for all other players
350    * relative to the given player.
351    *
352    * @param place player's place.
353    * @return whist score items for all opponents of the given player.
354    */
355   public ScoreItem[] getOtherWhistItems(Place place) {
356     return this.othersWhists.get(place);
357   }
358 
359   /**
360    * Gets whist saldo score items for all other players
361    * relative to the given player.
362    *
363    * @param place player's place.
364    * @return whist saldo score items for all opponents of the given player.
365    */
366   public ScoreItem[] getOtherWhistSaldoItems(Place place) {
367     return this.othersWhistSaldos.get(place);
368   }
369 
370   /** Private methods ***********************/
371 
372   /**
373    * Gets whist score items for all other players
374    * relative to the given player.
375    *
376    * @param place player's place.
377    * @return whist score items for all opponents of the given player.
378    */
379   private ScoreItem[] getOtherWhistItemsHelper(Place place) {
380     ScoreItem[] items = new ScoreItem[this.numberOfPlayers - 1];
381     switch (place) {
382       case EAST:
383         items[0] = WHIST_SOUTH;
384         items[1] = WHIST_WEST;
385         break;
386 
387       case SOUTH:
388         items[0] = WHIST_EAST;
389         items[1] = WHIST_WEST;
390         break;
391 
392       case WEST:
393         items[0] = WHIST_EAST;
394         items[1] = WHIST_SOUTH;
395         break;
396 
397       case NORTH:
398         items[0] = WHIST_EAST;
399         items[1] = WHIST_SOUTH;
400         items[2] = WHIST_WEST;
401         break;
402 
403       default:
404         throw new IllegalArgumentException("Illegal place " + place + "!");
405     }
406     if (this.numberOfPlayers == 4 && place != NORTH) {
407       items[2] = WHIST_NORTH;
408     }
409 
410     return items;
411   }
412 
413   /**
414    * Gets whist saldo score items for all other players
415    * relative to the given player.
416    *
417    * @param place player's place.
418    * @return whist saldo score items for all opponents of the given player.
419    */
420   private ScoreItem[] getOtherWhistSaldoItemsHelper(Place place) {
421     ScoreItem[] items = new ScoreItem[this.numberOfPlayers - 1];
422     switch (place) {
423       case EAST:
424         items[0] = WHIST_SOUTH_SALDO;
425         items[1] = WHIST_WEST_SALDO;
426         break;
427 
428       case SOUTH:
429         items[0] = WHIST_EAST_SALDO;
430         items[1] = WHIST_WEST_SALDO;
431         break;
432 
433       case WEST:
434         items[0] = WHIST_EAST_SALDO;
435         items[1] = WHIST_SOUTH_SALDO;
436         break;
437 
438       case NORTH:
439         items[0] = WHIST_EAST_SALDO;
440         items[1] = WHIST_SOUTH_SALDO;
441         items[2] = WHIST_WEST_SALDO;
442         break;
443 
444       default:
445         throw new IllegalArgumentException("Illegal place " + place + "!");
446     }
447     if (this.numberOfPlayers == 4 && place != NORTH) {
448       items[2] = WHIST_NORTH_SALDO;
449     }
450 
451     return items;
452   }
453 
454 }