Java poker implementation
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GameWindow.java 33KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078
  1. /*
  2. * Copyright (c) Chris 'MD87' Smith, 2007-2008. All rights reserved.
  3. *
  4. * This code may not be redistributed without prior permission from the
  5. * aforementioned copyright holder(s).
  6. */
  7. package com.md87.cardgame.ui;
  8. import com.md87.cardgame.Card;
  9. import com.md87.cardgame.Deck;
  10. import com.md87.cardgame.Player;
  11. import com.md87.cardgame.interfaces.GameObserver;
  12. import com.md87.cardgame.interfaces.Game;
  13. import com.md87.cardgame.controllers.HumanPlayer;
  14. import java.awt.Color;
  15. import java.awt.Graphics;
  16. import java.awt.Graphics2D;
  17. import java.awt.Point;
  18. import java.awt.Rectangle;
  19. import java.awt.event.KeyEvent;
  20. import java.awt.event.KeyListener;
  21. import java.awt.event.MouseEvent;
  22. import java.awt.event.MouseListener;
  23. import java.awt.image.BufferedImage;
  24. import java.io.IOException;
  25. import java.util.ArrayList;
  26. import java.util.HashMap;
  27. import java.util.List;
  28. import java.util.Map;
  29. import java.util.concurrent.Semaphore;
  30. import javax.imageio.ImageIO;
  31. import javax.swing.JFrame;
  32. /**
  33. *
  34. * @author Chris
  35. */
  36. public class GameWindow extends JFrame implements GameObserver, MouseListener, KeyListener {
  37. /**
  38. * A version number for this class. It should be changed whenever the class
  39. * structure is changed (or anything else that would prevent serialized
  40. * objects being unserialized with the new class).
  41. */
  42. private static final long serialVersionUID = 1;
  43. /** The delay between events for the "fast" speed. */
  44. public final static int SPEED_FAST = 200;
  45. /** The delay between events for the "normal" speed. */
  46. public final static int SPEED_NORMAL = 1000;
  47. /** The delay between events for the "slow" speed. */
  48. public final static int SPEED_SLOW = 5000;
  49. /** The initial width of the window. */
  50. private final static int WIDTH = 800;
  51. /** The initial height of the window. */
  52. private final static int HEIGHT = 600;
  53. /** The number of buttons shown on each side. */
  54. private final static int NUM_BUTTONS = 4;
  55. /** The width of buttons. */
  56. private final static int BUTTON_WIDTH = 100;
  57. /** The height of buttons. */
  58. private final static int BUTTON_HEIGHT = 25;
  59. /** The offset from the screen edge of the buttons. */
  60. private final static int BUTTON_OFFSET = 15;
  61. /** The space between each button. */
  62. private final static int BUTTON_SPACER = 10;
  63. /** The width of the cards in use. */
  64. private int cardWidth = 71;
  65. /** The height of the cards in use. */
  66. private int cardHeight = 96;
  67. /** The space between each card. */
  68. private final static int CARD_SPACER = 4;
  69. /** The horizontal offset of the community ccards. */
  70. private int communityOffset = 150;
  71. /** The offset of each community card from the others if there's enough space. */
  72. private int cardLargeOffset = cardWidth + CARD_SPACER;
  73. /** The offset of each community card from the others if there's not enough space. */
  74. private int cardShortOffset = 25;
  75. /** The amount of space required to use the large offset. */
  76. private int communityMinSize = 2 * communityOffset + 6 * cardWidth + 5 * CARD_SPACER;
  77. /** The background colour to use. */
  78. private Color backgroundColour = new Color(0, 100, 0);
  79. /** The current speed. */
  80. private int speed = SPEED_NORMAL;
  81. /** The game we're playing. */
  82. private final Game game;
  83. /** The player whose turn it is. */
  84. private Player turn;
  85. /** The player who has an outstanding message. */
  86. private Player messagePlayer;
  87. /** A list of known winners. */
  88. private final List<Player> winners = new ArrayList<Player>();
  89. /** A position where the next card should be dealt to for each player. */
  90. private final Map<Player, Point> nextCardPos = new HashMap<Player, Point>();
  91. /** The current player message. */
  92. private String message;
  93. /** The human player who we're waiting for to chose his move. */
  94. private HumanPlayer player = null;
  95. /** The human player who we're waiting for to continue. */
  96. private HumanPlayer waitPlayer = null;
  97. /** The human player who we're waiting for to discard. */
  98. private HumanPlayer discardPlayer = null;
  99. /** The minimum number of cards that need discarding. */
  100. private int discardMin = -1;
  101. /** The maximum number of cards that need discarding. */
  102. private int discardMax = -1;
  103. /** The cards that have been chosen to discard. */
  104. private Deck discards = null;
  105. /** Whether the current player can fold. */
  106. private boolean canFold = false;
  107. /** Whether the current player can raise. */
  108. private boolean canRaise = true;
  109. /** Whether or not we're in showdown. */
  110. private boolean inShowdown = false;
  111. /** The image buffer we render to. */
  112. private BufferedImage buffer = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
  113. /** A cache of card images. */
  114. private final Map<String, BufferedImage> cards = new HashMap<String, BufferedImage>();
  115. /** A cache of card positions (for discarding). */
  116. private final Map<Rectangle, Card> cardPositions = new HashMap<Rectangle, Card>();
  117. /** The buttons we're using. */
  118. private final Map<Button.TYPE, Button> buttons = new HashMap<Button.TYPE, Button>();
  119. /** A semaphore used to control rendering access. */
  120. private final Semaphore drawSem = new Semaphore(1);
  121. /** The style of the card to use. */
  122. private final String frontStyle, backStyle;
  123. /**
  124. * Creates a new game window for the specified game.
  125. *
  126. * @param game The game that this window should display
  127. * @param frontStyle The folder name to use for the front of card images
  128. * @param backStyle The file name to use for the back of card images
  129. * @param backgroundColour The background colour to use
  130. */
  131. public GameWindow(final Game game, final String frontStyle,
  132. final String backStyle, final Color backgroundColour) {
  133. super("JaPoker");
  134. this.game = game;
  135. this.frontStyle = frontStyle;
  136. this.backStyle = backStyle;
  137. this.backgroundColour = backgroundColour;
  138. // Determine card size
  139. try {
  140. final String cardFile = "/com/md87/cardgame/res/fronts/" + frontStyle + "/sace.png";
  141. final BufferedImage cardImage = ImageIO.read(getClass().getResource(cardFile));
  142. cardWidth = cardImage.getWidth();
  143. cardHeight = cardImage.getHeight();
  144. cardLargeOffset = cardWidth + CARD_SPACER;
  145. communityMinSize = 2 * communityOffset + 6 * cardWidth + 5 * CARD_SPACER;
  146. } catch (IOException ex) {
  147. ex.printStackTrace();
  148. }
  149. game.registerObserver(this);
  150. setSize(WIDTH, HEIGHT);
  151. initButtons();
  152. addMouseListener(this);
  153. addKeyListener(this);
  154. setVisible(true);
  155. try {
  156. setIconImage(ImageIO.read(getClass()
  157. .getResource("/com/md87/cardgame/res/icons/icon.png")));
  158. } catch (IOException ex) {
  159. System.err.println("Unable to load icon");
  160. }
  161. setDefaultCloseOperation(GameWindow.EXIT_ON_CLOSE);
  162. }
  163. /**
  164. * Initialises the buttons used in the UI.
  165. */
  166. private void initButtons() {
  167. int topButton = (getHeight() - (BUTTON_HEIGHT * NUM_BUTTONS
  168. + BUTTON_SPACER * (NUM_BUTTONS - 1))) / 2;
  169. final int buttonOffset = BUTTON_SPACER + BUTTON_HEIGHT;
  170. final int rightButton = getWidth() - BUTTON_WIDTH - BUTTON_OFFSET;
  171. buttons.put(Button.TYPE.FAST, new Button("Fast",
  172. new Rectangle(BUTTON_OFFSET, topButton + 0 * buttonOffset, BUTTON_WIDTH,
  173. BUTTON_HEIGHT)));
  174. buttons.put(Button.TYPE.NORMAL, new Button("Medium",
  175. new Rectangle(BUTTON_OFFSET, topButton + 1 * buttonOffset, BUTTON_WIDTH,
  176. BUTTON_HEIGHT)));
  177. buttons.put(Button.TYPE.SLOW, new Button("Slow",
  178. new Rectangle(BUTTON_OFFSET, topButton + 2 * buttonOffset, BUTTON_WIDTH,
  179. BUTTON_HEIGHT)));
  180. buttons.put(Button.TYPE.CONTINUE, new Button("Continue",
  181. new Rectangle(BUTTON_OFFSET, topButton + 3 * buttonOffset, BUTTON_WIDTH,
  182. BUTTON_HEIGHT)));
  183. buttons.put(Button.TYPE.CHECK, new Button("Check / Call",
  184. new Rectangle(rightButton, topButton + 0 * buttonOffset, BUTTON_WIDTH,
  185. BUTTON_HEIGHT)));
  186. buttons.put(Button.TYPE.OPEN, new Button("Open / Raise",
  187. new Rectangle(rightButton, topButton + 1 * buttonOffset, BUTTON_WIDTH,
  188. BUTTON_HEIGHT)));
  189. buttons.put(Button.TYPE.FOLD, new Button("Fold",
  190. new Rectangle(rightButton, topButton + 2 * buttonOffset, BUTTON_WIDTH,
  191. BUTTON_HEIGHT)));
  192. buttons.put(Button.TYPE.DISCARD, new Button("Discard",
  193. new Rectangle(rightButton, topButton + 3 * buttonOffset, BUTTON_WIDTH,
  194. BUTTON_HEIGHT)));
  195. }
  196. /**
  197. * Renders the game window.
  198. *
  199. * @param gr The graphics object to render to
  200. */
  201. @Override
  202. public void paint(final Graphics gr) {
  203. drawSem.acquireUninterruptibly();
  204. // Resize the buffer if the window has been resized
  205. if (buffer.getWidth() != getWidth() || buffer.getHeight() != getHeight()) {
  206. buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
  207. }
  208. cardPositions.clear();
  209. Graphics g = buffer.getGraphics();
  210. g.setColor(backgroundColour);
  211. g.fillRect(0, 0, getWidth(), getHeight());
  212. paintButtons((Graphics2D) g);
  213. paintCommunityCards(g);
  214. int yCardPosition = 55;
  215. int yNamePosition = 74 + cardHeight;
  216. int yHandPosition = 38;
  217. int x = 28;
  218. int xDirection = 1;
  219. boolean largeOffset = (game.getPlayers().size() / 2) * (10 + cardLargeOffset
  220. * game.holeCardCount()) < getWidth() - 28 * 2;
  221. int playerNumber = 0;
  222. for (Player myPlayer : game.getPlayers()) {
  223. playerNumber++;
  224. // Flip the direction of players for the lower half of the screen
  225. if (playerNumber > game.getNumPlayers() / 2 && yCardPosition == 55) {
  226. yCardPosition = getHeight() - 45 - cardHeight;
  227. yNamePosition = getHeight() - 56 - cardHeight;
  228. yHandPosition = getHeight() - 30;
  229. if (largeOffset) {
  230. x = getWidth() - 28 - cardLargeOffset * game.holeCardCount();
  231. } else {
  232. x = getWidth() - 28 - cardLargeOffset - cardShortOffset
  233. * (game.holeCardCount() - 1);
  234. }
  235. xDirection = -1;
  236. }
  237. g.setColor(myPlayer.isOut() ? Color.BLACK : Color.WHITE);
  238. g.drawString(myPlayer.getName(), x, yNamePosition);
  239. if (messagePlayer == myPlayer) {
  240. paintMessage(g, x, yNamePosition + 15 * xDirection);
  241. }
  242. if (myPlayer == turn && !inShowdown && !myPlayer.isOut()) {
  243. paintTurnToken(g, x, yNamePosition);
  244. }
  245. if (myPlayer == game.getDealer()) {
  246. paintDealerToken(g, x, yNamePosition < getHeight() / 2 ?
  247. yNamePosition + 20 : yNamePosition - 55);
  248. }
  249. if (winners.contains(myPlayer)) {
  250. paintWinnerToken(g, x, yNamePosition < 400 ?
  251. yNamePosition + 25 : yNamePosition - 45);
  252. }
  253. if (!myPlayer.hasFolded()) {
  254. int xOffset = 0;
  255. int cardNumber = 0;
  256. for (Card card : myPlayer.getCards()) {
  257. cardNumber++;
  258. if ((myPlayer.shouldShowCards() || inShowdown
  259. || card.isPublic() || !game.hasActiveHuman())
  260. && (discardPlayer == null
  261. || discardPlayer.getPlayer() != myPlayer
  262. || !discards.contains(card))) {
  263. showCard(g, card, x + xOffset, yCardPosition);
  264. } else {
  265. showCard(g, null, x + xOffset, yCardPosition);
  266. }
  267. cardPositions.put(new Rectangle(x + xOffset, yCardPosition,
  268. ((largeOffset || cardNumber == myPlayer.getCards().size()) ?
  269. cardLargeOffset : cardShortOffset), cardHeight), card);
  270. if (largeOffset) {
  271. xOffset += cardLargeOffset;
  272. } else {
  273. xOffset += cardShortOffset;
  274. }
  275. }
  276. nextCardPos.put(myPlayer, new Point(x + xOffset, yCardPosition));
  277. if (inShowdown && !myPlayer.hasFolded() && myPlayer.getCards().size() > 0) {
  278. g.setColor(Color.GREEN);
  279. String[] hand = game.getHandText(myPlayer).split("\n");
  280. int yo = 0;
  281. for (String line : hand) {
  282. g.drawString(line, x, yHandPosition + yo);
  283. yo += 13;
  284. }
  285. }
  286. }
  287. x += xDirection * (getWidth() * 2 - 28*4)
  288. / (game.getNumPlayers() + (game.getNumPlayers() % 2));
  289. }
  290. g.setColor(Color.WHITE);
  291. g.drawString("POT: " + game.getCurrentPot(), getWidth() * 3 / 4, getHeight() / 2);
  292. g.drawString("BET: " + game.getMaxBet(), getWidth() * 3 / 4, getHeight() / 2 + 20);
  293. ((Graphics2D) gr).drawImage(buffer, 0, 0, this);
  294. drawSem.release();
  295. }
  296. /**
  297. * Renders the buttons used by the UI.
  298. *
  299. * @param g The graphics object to render the buttons to.
  300. */
  301. private void paintButtons(final Graphics2D g) {
  302. initButtons();
  303. for (Map.Entry<Button.TYPE, Button> entry : buttons.entrySet()) {
  304. entry.getValue().render(g,
  305. entry.getKey().test(speed, waitPlayer, player, canRaise,
  306. canFold, discardPlayer));
  307. }
  308. }
  309. /**
  310. * Paints the visible community cards.
  311. *
  312. * @param g The graphics object to render the buttons to.
  313. */
  314. private void paintCommunityCards(final Graphics g) {
  315. int i = 0;
  316. int xOffset = getWidth() < communityMinSize ? cardShortOffset : cardLargeOffset;
  317. for (Card card : game.getCommunityCards()) {
  318. showCard(g, card, communityOffset + i * xOffset, (getHeight() - cardHeight)/2);
  319. i++;
  320. }
  321. }
  322. /**
  323. * Paints the dealer token at the specified position.
  324. *
  325. * @param g The graphics object to render the token to.
  326. * @param x The x co-ordinate of the token
  327. * @param y The y co-ordinate of the token
  328. */
  329. private void paintDealerToken(final Graphics g, final int x, final int y) {
  330. g.setColor(Color.ORANGE);
  331. g.fillOval(x, y, 25, 25);
  332. g.setColor(Color.BLACK);
  333. g.drawString("D", x + 9, y + 17);
  334. }
  335. /**
  336. * Paints the winner token at the specified position.
  337. *
  338. * @param g The graphics object to render the token to.
  339. * @param x The x co-ordinate of the token
  340. * @param y The y co-ordinate of the token
  341. */
  342. private void paintWinnerToken(final Graphics g, final int x, final int y) {
  343. g.setColor(Color.GREEN);
  344. g.fillOval(x + 37, y - 3, 20, 20);
  345. g.setColor(Color.BLACK);
  346. g.drawString("W", x + 42, y + 12);
  347. }
  348. /**
  349. * Paints the turn token at the specified position.
  350. *
  351. * @param g The graphics object to render the token to.
  352. * @param x The x co-ordinate of the token
  353. * @param y The y co-ordinate of the token
  354. */
  355. private void paintTurnToken(final Graphics g, final int x, final int y) {
  356. g.setColor(Color.WHITE);
  357. g.fillOval(x - 12, y - 10, 10, 10);
  358. }
  359. /**
  360. * Paints the current message at the specified position.
  361. *
  362. * @param g The graphics object to render the message to.
  363. * @param x The x co-ordinate of the message
  364. * @param y The y co-ordinate of the message
  365. */
  366. private void paintMessage(final Graphics g, final int x, final int y) {
  367. g.setColor(Color.YELLOW);
  368. g.drawString(message, x, y);
  369. }
  370. /**
  371. * Displays the specified card at the specified co-ordinates.
  372. *
  373. * @param g The graphics object to render the buttons to
  374. * @param card The card to be drawn, or null for a blank
  375. * @param x The x-coordinate of the card
  376. * @param y The y-coordinate of the card
  377. */
  378. private void showCard(final Graphics g, final Card card, final int x, final int y) {
  379. showCard(g, card, x, y, cardWidth);
  380. }
  381. /**
  382. * Displays the specified card at the specified co-ordinates.
  383. *
  384. * @param g The graphics object to render the buttons to
  385. * @param card The card to be drawn, or null for a blank
  386. * @param x The x-coordinate of the card
  387. * @param y The y-coordinate of the card
  388. * @param width The width that the card should be scaled to
  389. */
  390. private void showCard(final Graphics g, final Card card, final int x, final int y,
  391. final int width) {
  392. try {
  393. final String file = "/com/md87/cardgame/res/"
  394. + (card == null ? "backs/" + backStyle : "fronts/" + frontStyle + "/"
  395. + card.getFileName()) + ".png";
  396. if (!cards.containsKey(file)) {
  397. BufferedImage im = ImageIO.read(getClass().getResource(file));
  398. cards.put(file, im);
  399. }
  400. if (cards.get(file) == null) {
  401. System.err.println("No card image for: " + file);
  402. }
  403. ((Graphics2D) g).drawImage(cards.get(file), x + (cardWidth - width)/2, y,
  404. x + width + (cardWidth - width)/2, y + cardHeight,
  405. 0, 0, cards.get(file).getWidth(), cards.get(file).getHeight(), this);
  406. } catch (IOException ex) {
  407. System.err.print("Error loading image: " + ex);
  408. }
  409. }
  410. /**
  411. * Requests a repaint, and sleeps for a period of time based on the speed
  412. * of the game.
  413. */
  414. private void doUpdate() {
  415. repaint();
  416. try {
  417. Thread.sleep(speed);
  418. } catch (InterruptedException ex) {
  419. // Do nothing
  420. }
  421. }
  422. // ----------------------- OBSERVER METHODS -------------------------------
  423. /** {@inheritDoc} */
  424. @Override
  425. public void playerCardsUpdated() {
  426. inShowdown = false;
  427. repaint();
  428. try {
  429. Thread.sleep(speed / 4);
  430. } catch (InterruptedException ex) {
  431. // Do nothing
  432. }
  433. }
  434. /** {@inheritDoc} */
  435. @Override
  436. public void communityCardsUpdated() {
  437. doUpdate();
  438. }
  439. /** {@inheritDoc} */
  440. @Override
  441. public void playersTurn(final Player player) {
  442. if (!player.hasFolded() && !player.isOut() && !player.isAllIn()) {
  443. turn = player;
  444. messagePlayer = null;
  445. doUpdate();
  446. }
  447. }
  448. /** {@inheritDoc} */
  449. @Override
  450. public void newPlayer(final Player player) {
  451. repaint();
  452. }
  453. /** {@inheritDoc} */
  454. @Override
  455. public void newGame() {
  456. inShowdown = false;
  457. winners.clear();
  458. doUpdate();
  459. }
  460. /** {@inheritDoc} */
  461. @Override
  462. public void endGame() {
  463. doUpdate();
  464. }
  465. /** {@inheritDoc} */
  466. @Override
  467. public void setDealer(final Player player) {
  468. doUpdate();
  469. }
  470. /** {@inheritDoc} */
  471. @Override
  472. public void placeBlind(final Player player, final int blind, final String name) {
  473. winners.clear();
  474. messagePlayer = player;
  475. message = "Pays " + name;
  476. doUpdate();
  477. }
  478. /** {@inheritDoc} */
  479. @Override
  480. public void raise(final Player player, final int amount) {
  481. messagePlayer = player;
  482. message = "Raises " + amount;
  483. doUpdate();
  484. }
  485. /** {@inheritDoc} */
  486. @Override
  487. public void fold(final Player player) {
  488. messagePlayer = player;
  489. message = "Folds";
  490. if (!game.hasActiveHuman()) {
  491. flipAllCards();
  492. }
  493. doUpdate();
  494. }
  495. /** {@inheritDoc} */
  496. @Override
  497. public void call(final Player player) {
  498. messagePlayer = player;
  499. message = "Calls";
  500. doUpdate();
  501. }
  502. /** {@inheritDoc} */
  503. @Override
  504. public void check(final Player player) {
  505. messagePlayer = player;
  506. message = "Checks";
  507. doUpdate();
  508. }
  509. /** {@inheritDoc} */
  510. @Override
  511. public void open(final Player player, final int amount) {
  512. messagePlayer = player;
  513. message = "Opens at " + amount;
  514. doUpdate();
  515. }
  516. /** {@inheritDoc} */
  517. @Override
  518. public void winner(final Player player) {
  519. winners.add(player);
  520. doUpdate();
  521. }
  522. /** {@inheritDoc} */
  523. @Override
  524. public void showdown() {
  525. inShowdown = true;
  526. flipAllCards();
  527. doUpdate();
  528. }
  529. // ------------------ HUMAN PLAYER INTERFACE ------------------------------
  530. /**
  531. * Indicates that a human player needs to make a decision as to whether to
  532. * call/fold/raise.
  533. *
  534. * @param player The player who has to make the decision
  535. * @param canFold Whether or not the player can fold
  536. * @param canRaise Whether or not the player can raise
  537. */
  538. public void setHumanPlayer(final HumanPlayer player, final boolean canFold,
  539. final boolean canRaise) {
  540. this.player = player;
  541. this.canFold = canFold;
  542. this.canRaise = canRaise;
  543. repaint();
  544. }
  545. /**
  546. * Indicates that a human player controller is waiting for the player to
  547. * indicate that it's OK to continue.
  548. *
  549. * @param player The player who has to make the indication
  550. */
  551. public void setWaitPlayer(final HumanPlayer player) {
  552. this.waitPlayer = player;
  553. repaint();
  554. }
  555. /**
  556. * Indicates that a human player controller is waiting for the player to
  557. * discard some cards.
  558. *
  559. * @param player The player who has to do the discarding
  560. * @param min The minimum number of cards to discard
  561. * @param max The maximum number of cards to discard
  562. */
  563. public void setDiscardPlayer(final HumanPlayer player, final int min, final int max) {
  564. this.discardPlayer = player;
  565. discardMin = min;
  566. discardMax = max;
  567. discards = new Deck();
  568. repaint();
  569. }
  570. // ----------------- UI CALLBACKS ------------------------------------------
  571. /** {@inheritDoc} */
  572. @Override
  573. public void mouseClicked(final MouseEvent e) {
  574. for (Map.Entry<Button.TYPE, Button> entry : buttons.entrySet()) {
  575. if (entry.getValue().contains(e.getPoint())) {
  576. processMouseClick(entry.getKey());
  577. }
  578. }
  579. if (discardPlayer != null) {
  580. Card best = null;
  581. for (Map.Entry<Rectangle, Card> entry : cardPositions.entrySet()) {
  582. if (entry.getKey().contains(e.getPoint())) {
  583. best = entry.getValue();
  584. }
  585. }
  586. if (best != null && discardPlayer.getPlayer().getCards().contains(best)) {
  587. if (discards.contains(best)) {
  588. discards.remove(best);
  589. } else {
  590. discards.add(best);
  591. }
  592. repaint();
  593. }
  594. }
  595. }
  596. /**
  597. * Handles the user clicking on a button.
  598. *
  599. * @param type The type of button that was clicked on
  600. */
  601. private void processMouseClick(final Button.TYPE type) {
  602. boolean done = false;
  603. switch(type) {
  604. case FAST:
  605. speed = SPEED_FAST;
  606. break;
  607. case NORMAL:
  608. speed = SPEED_NORMAL;
  609. break;
  610. case SLOW:
  611. speed = SPEED_SLOW;
  612. break;
  613. }
  614. if (type.test(speed, waitPlayer, player, canRaise, canFold, discardPlayer)) {
  615. switch(type) {
  616. case CONTINUE:
  617. synchronized(waitPlayer) {
  618. waitPlayer.notifyAll();
  619. }
  620. waitPlayer = null;
  621. break;
  622. case CHECK:
  623. synchronized(player) {
  624. player.move = 0;
  625. player.notifyAll();
  626. done = true;
  627. }
  628. break;
  629. case OPEN:
  630. synchronized(player) {
  631. player.move = 1;
  632. player.notifyAll();
  633. done = true;
  634. }
  635. break;
  636. case FOLD:
  637. synchronized(player) {
  638. player.move = 2;
  639. player.notifyAll();
  640. done = true;
  641. }
  642. break;
  643. case DISCARD:
  644. if (discards.size() >= discardMin && discards.size() <= discardMax) {
  645. synchronized(discardPlayer) {
  646. discardPlayer.discards = discards;
  647. discardPlayer.notifyAll();
  648. done = true;
  649. }
  650. } else {
  651. messagePlayer = discardPlayer.getPlayer();
  652. message = "Must discard " + discardMin + "-" + discardMax + " cards";
  653. repaint();
  654. }
  655. }
  656. }
  657. if (done) {
  658. player = null;
  659. discardPlayer = null;
  660. }
  661. repaint();
  662. }
  663. /** {@inheritDoc} */
  664. @Override
  665. public void mousePressed(MouseEvent e) {
  666. // Do nothing
  667. }
  668. /** {@inheritDoc} */
  669. @Override
  670. public void mouseReleased(MouseEvent e) {
  671. // Do nothing
  672. }
  673. /** {@inheritDoc} */
  674. @Override
  675. public void mouseEntered(MouseEvent e) {
  676. // Do nothing
  677. }
  678. /** {@inheritDoc} */
  679. @Override
  680. public void mouseExited(MouseEvent e) {
  681. // Do nothing
  682. }
  683. /** {@inheritDoc} */
  684. @Override
  685. public void keyTyped(KeyEvent e) {
  686. // Do nothing
  687. }
  688. /** {@inheritDoc} */
  689. @Override
  690. public void keyPressed(KeyEvent e) {
  691. if (player != null) {
  692. boolean done = false;
  693. synchronized(player) {
  694. switch (e.getKeyCode()) {
  695. case KeyEvent.VK_C:
  696. player.move = 0;
  697. player.notifyAll();
  698. done = true;
  699. break;
  700. case KeyEvent.VK_O:
  701. case KeyEvent.VK_R:
  702. if (canRaise) {
  703. player.move = 1;
  704. player.notifyAll();
  705. done = true;
  706. }
  707. break;
  708. case KeyEvent.VK_F:
  709. if (canFold) {
  710. player.move = 2;
  711. player.notifyAll();
  712. done = true;
  713. }
  714. break;
  715. }
  716. }
  717. if (done) {
  718. player = null;
  719. }
  720. }
  721. if (discardPlayer != null && e.getKeyCode() == KeyEvent.VK_D) {
  722. if (discards.size() >= discardMin && discards.size() <= discardMax) {
  723. synchronized(discardPlayer) {
  724. discardPlayer.discards = discards;
  725. discardPlayer.notifyAll();
  726. }
  727. discardPlayer = null;
  728. } else {
  729. messagePlayer = discardPlayer.getPlayer();
  730. message = "Must discard " + discardMin + "-" + discardMax + " cards";
  731. repaint();
  732. }
  733. }
  734. if (waitPlayer != null && (e.getKeyCode() == KeyEvent.VK_SPACE
  735. || e.getKeyCode() == KeyEvent.VK_ENTER)) {
  736. synchronized(waitPlayer) {
  737. waitPlayer.notifyAll();
  738. }
  739. waitPlayer = null;
  740. }
  741. if (discardPlayer != null) {
  742. switch (e.getKeyCode()) {
  743. case KeyEvent.VK_1:
  744. toggleDiscard(1);
  745. break;
  746. case KeyEvent.VK_2:
  747. toggleDiscard(2);
  748. break;
  749. case KeyEvent.VK_3:
  750. toggleDiscard(3);
  751. break;
  752. case KeyEvent.VK_4:
  753. toggleDiscard(4);
  754. break;
  755. case KeyEvent.VK_5:
  756. toggleDiscard(5);
  757. break;
  758. case KeyEvent.VK_6:
  759. toggleDiscard(6);
  760. break;
  761. case KeyEvent.VK_7:
  762. toggleDiscard(7);
  763. break;
  764. case KeyEvent.VK_8:
  765. toggleDiscard(8);
  766. break;
  767. case KeyEvent.VK_9:
  768. toggleDiscard(9);
  769. break;
  770. }
  771. }
  772. }
  773. /**
  774. * Toggles the "discard" state of the specified card.
  775. *
  776. * @param card The index of the card to be toggled
  777. */
  778. private void toggleDiscard(final int card) {
  779. final Deck playerCards = discardPlayer.getPlayer().getCards();
  780. if (playerCards.size() >= card) {
  781. final Card myCard = playerCards.get(card - 1);
  782. if (discards.contains(myCard)) {
  783. discards.remove(myCard);
  784. flipCard(false, myCard);
  785. } else {
  786. discards.add(myCard);
  787. flipCard(true, myCard);
  788. }
  789. repaint();
  790. }
  791. }
  792. /** {@inheritDoc} */
  793. @Override
  794. public void keyReleased(KeyEvent e) {
  795. // Do nothing
  796. }
  797. /** {@inheritDoc} */
  798. @Override
  799. public void discards(Player player, int number) {
  800. messagePlayer = player;
  801. message = "Discards " + number + " card" + (number == 1 ? "" : "s");
  802. doUpdate();
  803. }
  804. /**
  805. * Flips all cards over.
  806. */
  807. private void flipAllCards() {
  808. final List<Card> unflipped = new ArrayList<Card>();
  809. for (Player curPlayer : game.getPlayers()) {
  810. if (!curPlayer.isOut() && !curPlayer.hasFolded()
  811. && !curPlayer.shouldShowCards()) {
  812. for (Card card : curPlayer.getCards()) {
  813. if (!card.isPublic()) {
  814. unflipped.add(card);
  815. }
  816. }
  817. }
  818. }
  819. flipCard(false, unflipped.toArray(new Card[0]));
  820. }
  821. /**
  822. * Flips the specified cards over.
  823. *
  824. * @param startsVisible Whether or not the card starts visible
  825. * @param cards The cards to be flipped
  826. */
  827. private void flipCard(final boolean startsVisible, final Card ... cards) {
  828. drawSem.acquireUninterruptibly();
  829. boolean visible = startsVisible;
  830. final BufferedImage newbuffer = new BufferedImage(getWidth(), getHeight(),
  831. BufferedImage.TYPE_INT_ARGB);
  832. for (int progress = 0; progress < 100; progress += 5) {
  833. final Graphics g = newbuffer.getGraphics();
  834. g.drawImage(buffer, 0, 0, this);
  835. if (progress == 50) {
  836. visible = !visible;
  837. }
  838. for (Card card : cards) {
  839. int x = 0;
  840. int y = 0;
  841. Rectangle rect = new Rectangle();
  842. for (Map.Entry<Rectangle, Card> entry : cardPositions.entrySet()) {
  843. if (entry.getValue().equals(card)) {
  844. x = entry.getKey().x;
  845. y = entry.getKey().y;
  846. rect = entry.getKey();
  847. }
  848. }
  849. if (x == 0 && y == 0) {
  850. continue;
  851. }
  852. int size = 50 - (progress > 50 ? 100 - progress : progress);
  853. g.setColor(backgroundColour);
  854. g.fillRect(rect.x, rect.y, rect.width, rect.height);
  855. showCard(g, visible ? card : null, x, y, (int) (cardWidth
  856. * ((float) size/50)));
  857. }
  858. getGraphics().drawImage(newbuffer, 0, 0, this);
  859. try {
  860. Thread.sleep(speed / 500);
  861. } catch (InterruptedException ex) {
  862. // Do nothing
  863. }
  864. }
  865. drawSem.release();
  866. }
  867. /** {@inheritDoc} */
  868. @Override
  869. public void cardDealt(final Player player, final Card card) {
  870. drawSem.acquireUninterruptibly();
  871. final int targetX = nextCardPos.get(player).x;
  872. final int targetY = nextCardPos.get(player).y;
  873. cardPositions.put(new Rectangle(targetX, targetY, cardWidth, cardHeight), card);
  874. final BufferedImage newbuffer = new BufferedImage(getWidth(), getHeight(),
  875. BufferedImage.TYPE_INT_ARGB);
  876. for (int x = 0; x < targetX + cardWidth; x += Math.max(15, targetX / 15)) {
  877. int y = ((HEIGHT - cardHeight) / 2)
  878. - Math.round(((float) x / (float) (targetX + cardWidth))
  879. * ((HEIGHT - cardHeight) / 2 - targetY));
  880. newbuffer.getGraphics().drawImage(buffer, 0, 0, this);
  881. showCard(newbuffer.getGraphics(), card.isPublic() ? card : null,
  882. x - cardWidth, y);
  883. getGraphics().drawImage(newbuffer, 0, 0, this);
  884. try {
  885. Thread.sleep(speed / 500);
  886. } catch (InterruptedException ex) {
  887. // Do nothing
  888. }
  889. }
  890. drawSem.release();
  891. if (!card.isPublic() && player.shouldShowCards()) {
  892. flipCard(false, card);
  893. }
  894. }
  895. }