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.

AbstractGame.java 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. /*
  2. * Copyright (c) Chris 'MD87' Smith, 2007. 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.games;
  8. import com.md87.cardgame.Card;
  9. import com.md87.cardgame.Deck;
  10. import com.md87.cardgame.Player;
  11. import com.md87.cardgame.Rank;
  12. import com.md87.cardgame.Suit;
  13. import com.md87.cardgame.hands.StandardHand;
  14. import com.md87.cardgame.interfaces.Game;
  15. import com.md87.cardgame.interfaces.GameObserver;
  16. import com.md87.cardgame.interfaces.Hand;
  17. import com.md87.cardgame.interfaces.PlayerController;
  18. import java.util.ArrayList;
  19. import java.util.Collections;
  20. import java.util.HashMap;
  21. import java.util.List;
  22. import java.util.Map;
  23. /**
  24. * Implements some basic functions that may be used by a game.
  25. *
  26. * @author Chris
  27. */
  28. public abstract class AbstractGame implements Game, Runnable {
  29. /** The maximum number of players this game will contain. */
  30. protected int numplayers = 0;
  31. protected int bigblind = 0;
  32. protected int ante = 0;
  33. protected int raises = 4;
  34. protected int raisesLeft;
  35. protected int dealer = 0;
  36. protected int player = 0;
  37. /** A list of players who're playing in this game. */
  38. protected final List<Player> players = new ArrayList<Player>();
  39. /** The deck used for this game. */
  40. protected final Deck deck = new Deck();
  41. /** A list of observers registered for this game. */
  42. private final List<GameObserver> observers = new ArrayList<GameObserver>();
  43. public AbstractGame(final int numplayers, final int bigblind, final int ante,
  44. final int raises) {
  45. this.numplayers = numplayers;
  46. this.bigblind = bigblind;
  47. this.ante = ante;
  48. this.raises = raises;
  49. dealer = (int) Math.round(Math.random() * (numplayers - 1));
  50. }
  51. /** {@inheritDoc} */
  52. public void addPlayer(final String name, final int cash,
  53. final PlayerController controller) {
  54. if (numplayers <= players.size()) {
  55. return;
  56. }
  57. final Player player = new Player(this, name, cash, controller);
  58. addPlayer(player);
  59. }
  60. /** {@inheritDoc} */
  61. public void addPlayer(final Player player) {
  62. players.add(player);
  63. notifyNewPlayer(player);
  64. }
  65. /** {@inheritDoc} */
  66. public Player getDealer() {
  67. if (players.size() > dealer) {
  68. return players.get(dealer);
  69. } else {
  70. return null;
  71. }
  72. }
  73. /** {@inheritDoc} */
  74. public int getBigBlind() {
  75. return bigblind;
  76. }
  77. /** {@inheritDoc} */
  78. public int getCurrentPot() {
  79. int pot = 0;
  80. for (Player player : players) {
  81. pot += player.getBet();
  82. }
  83. return pot;
  84. }
  85. /** {@inheritDoc} */
  86. public int getMaxBet() {
  87. int max = 0;
  88. for (Player player : players) {
  89. if (player.getBet() > max) {
  90. max = player.getBet();
  91. }
  92. }
  93. return max;
  94. }
  95. /** {@inheritDoc} */
  96. public int getNumPlayers() {
  97. return numplayers;
  98. }
  99. /**
  100. * Clears and rebuilds the deck used for this game.
  101. */
  102. protected void shuffle() {
  103. deck.clear();
  104. for (Suit suit : Suit.values()) {
  105. for (Rank rank : Rank.values()) {
  106. deck.add(new Card(suit, rank));
  107. }
  108. }
  109. Collections.shuffle(deck);
  110. }
  111. /**
  112. * Clears all player cards.
  113. */
  114. protected void discardCards() {
  115. for (Player player : players) {
  116. player.discardCards();
  117. }
  118. }
  119. /**
  120. * Deals a single card to each active player, starting with the
  121. * specified player, and working clockwise.
  122. *
  123. * @param start The player to start dealing with
  124. * @param isPublic Whether the card is a public card or not
  125. */
  126. protected void dealCard(final Player start, final boolean isPublic) {
  127. int i = players.indexOf(start);
  128. for (int x = 0; x < numplayers; x++) {
  129. final Player player = players.get((i + x) % numplayers);
  130. if (!player.isOut() && !player.hasFolded()) {
  131. final Card card = deck.deal();
  132. card.setPublic(isPublic);
  133. player.dealCard(card);
  134. notifyCardDealt(player, card);
  135. notifyPlayerCardsUpdated();
  136. }
  137. }
  138. }
  139. protected void doDrawRound(final Player start, final int min, final int max, boolean replace) {
  140. int i = players.indexOf(start);
  141. for (int x = 0; x < numplayers; x++) {
  142. final Player player = players.get((i + x) % numplayers);
  143. if (!player.isOut() && !player.hasFolded()) {
  144. notifyPlayersTurn(player);
  145. final Deck discarded = player.doCardDiscard(min, max);
  146. for (Card card : discarded) {
  147. player.removeCard(card);
  148. if (replace) {
  149. player.dealCard(deck.deal());
  150. }
  151. }
  152. notifyDiscards(player, discarded.size());
  153. }
  154. }
  155. }
  156. /**
  157. * Counts the number of players with the specified properties.
  158. *
  159. * @param mustBeIn Whether or not the players must be in
  160. * @param mustNotFolded Whether or not the players must not have folded to be in
  161. * @param mustNotAllIn Whether or not the players must not be all in
  162. * @return The number of players matching the properties specified
  163. */
  164. public int countPlayers(final boolean mustBeIn,
  165. final boolean mustNotFolded, final boolean mustNotAllIn) {
  166. int count = 0;
  167. for (Player player : players) {
  168. if ((!mustBeIn || !player.isOut())
  169. && (!mustNotFolded || !player.hasFolded())
  170. && (!mustNotAllIn || !player.isAllIn())) {
  171. count++;
  172. }
  173. }
  174. return count;
  175. }
  176. /** {@inheritDoc} */
  177. public List<Player> getPlayers() {
  178. return players;
  179. }
  180. /** {@inheritDoc} */
  181. public void startTournament() {
  182. new Thread(this).start();
  183. }
  184. /** {@inheritDoc} */
  185. public void run() {
  186. while (countPlayers(true, false, false) > 1) {
  187. startGame();
  188. }
  189. }
  190. protected abstract void startGame();
  191. protected void doAntes() {
  192. if (ante > 0) {
  193. for (Player player : players) {
  194. if (!player.isOut()) {
  195. player.forceBet(ante);
  196. notifyPlaceBlind(player, ante, "ante");
  197. }
  198. }
  199. }
  200. }
  201. protected void doBlinds() {
  202. if (countPlayers(true, false, false) > 2) {
  203. if (!players.get((dealer + 1) % numplayers).isOut()) {
  204. doSmallBlind(players.get((dealer + 1) % numplayers));
  205. }
  206. doBigBlind(players.get((dealer + 2) % numplayers));
  207. player = (dealer + 2) % numplayers;
  208. } else {
  209. Player big = null;
  210. Player small = null;
  211. for (Player player : players) {
  212. if (!player.isOut()) {
  213. if (player == players.get(dealer)) {
  214. small = player;
  215. } else {
  216. big = player;
  217. }
  218. }
  219. }
  220. doSmallBlind(small);
  221. doBigBlind(big);
  222. player = players.indexOf(big);
  223. }
  224. }
  225. protected void doSmallBlind(final Player player) {
  226. player.forceBet(bigblind / 2);
  227. notifyPlaceBlind(player, bigblind / 2, "small blind");
  228. }
  229. protected void doBigBlind(final Player player) {
  230. player.forceBet(bigblind);
  231. notifyPlaceBlind(player, bigblind / 2, "big blind");
  232. }
  233. @SuppressWarnings("fallthrough")
  234. protected void waitForBets() {
  235. int maxbet = getMaxBet();
  236. int endPlayer = player;
  237. boolean reachedEnd = false;
  238. raisesLeft = raises;
  239. Player myPlayer;
  240. do {
  241. player = (player + 1) % numplayers;
  242. myPlayer = players.get(player);
  243. if (!myPlayer.hasFolded() && !myPlayer.isAllIn() && !myPlayer.isOut()) {
  244. notifyPlayersTurn(myPlayer);
  245. if (playersHaveBet(maxbet)) {
  246. // He can check or open
  247. switch (myPlayer.doOpenCheck()) {
  248. case CHECK:
  249. // Do nothing
  250. notifyCheck(myPlayer);
  251. break;
  252. case OPEN:
  253. int raiseAmount = myPlayer.getRaiseAmount(bigblind);
  254. myPlayer.forceBet(raiseAmount);
  255. maxbet += raiseAmount;
  256. notifyOpen(myPlayer, raiseAmount);
  257. break;
  258. }
  259. } else {
  260. final boolean canRaise = raisesLeft != 0;
  261. // He can call, raise or fold
  262. switch (myPlayer.doCallRaiseFold(maxbet - myPlayer.getBet(), canRaise)) {
  263. case RAISE:
  264. if (canRaise) {
  265. myPlayer.forceBet(maxbet - myPlayer.getBet());
  266. int raiseAmount = myPlayer.getRaiseAmount(bigblind);
  267. myPlayer.forceBet(raiseAmount);
  268. maxbet += raiseAmount;
  269. notifyRaise(myPlayer, raiseAmount);
  270. raisesLeft--;
  271. break;
  272. } // Fall through: call instead
  273. case CALL:
  274. myPlayer.forceBet(maxbet - myPlayer.getBet());
  275. notifyCall(myPlayer);
  276. break;
  277. case FOLD:
  278. myPlayer.setFold();
  279. notifyFold(myPlayer);
  280. break;
  281. }
  282. }
  283. }
  284. if (player == endPlayer) {
  285. reachedEnd = true;
  286. }
  287. } while (!playersHaveBet(maxbet) || (!reachedEnd && countPlayers(true, true, true) > 1));
  288. }
  289. protected void doDealerAdvance() {
  290. if (countPlayers(true, false, false) > 2) {
  291. do {
  292. dealer = (dealer + 1) % numplayers;
  293. } while (players.get((dealer + 2) % numplayers).isOut());
  294. } else if (countPlayers(true, false, false) == 2) {
  295. do {
  296. dealer = (dealer + 1) % numplayers;
  297. } while (players.get(dealer).isOut());
  298. }
  299. }
  300. protected void doBettingRound() {
  301. notifyCommunityCardsUpdated();
  302. player = dealer;
  303. waitForBets();
  304. }
  305. protected boolean playersHaveBet(final int bet) {
  306. for (Player player : players) {
  307. if (!player.isOut() && !player.hasFolded() && !player.isAllIn()) {
  308. if (player.getBet() < bet) {
  309. return false;
  310. }
  311. }
  312. }
  313. return true;
  314. }
  315. protected void doShowDown() {
  316. for (Player player : players) {
  317. if (!player.isOut()) {
  318. player.calculateBestDeck();
  319. }
  320. }
  321. notifyShowdown();
  322. doWinner();
  323. for (Player player : players) {
  324. player.resetBet();
  325. }
  326. }
  327. protected void doWinner() {
  328. doWinner(false);
  329. }
  330. protected void doWinner(final boolean doHalf) {
  331. if (countPlayers(true, true, false) == 1) {
  332. int pot = 0;
  333. Player winner = null;
  334. for (Player player : players) {
  335. if (!player.isOut()) {
  336. if (!player.hasFolded()) {
  337. winner = player;
  338. }
  339. if (doHalf) {
  340. pot += player.getBet() / 2;
  341. } else {
  342. pot += player.getBet();
  343. }
  344. }
  345. }
  346. winner.addCash(pot);
  347. notifyWinner(winner);
  348. return;
  349. }
  350. // tempPlayers is a list of everyone involved in the round
  351. List<Player> tempPlayers = new ArrayList<Player>();
  352. Map<Player, Integer> playerBets = new HashMap<Player, Integer>();
  353. for (Player player : players) {
  354. if (!player.isOut()) {
  355. tempPlayers.add(player);
  356. if (doHalf) {
  357. playerBets.put(player, player.getBet() / 2);
  358. } else {
  359. playerBets.put(player, player.getBet());
  360. }
  361. }
  362. }
  363. int maxbet = 0;
  364. for (Integer bet : playerBets.values()) {
  365. if (bet > maxbet) {
  366. maxbet = bet;
  367. }
  368. }
  369. while (maxbet > 0 && tempPlayers.size() > 0) {
  370. int minbet = maxbet;
  371. for (Player player : tempPlayers) {
  372. if (playerBets.get(player) < minbet) {
  373. minbet = playerBets.get(player);
  374. }
  375. }
  376. int potsize = minbet * tempPlayers.size();
  377. List<Player> minPlayers = new ArrayList<Player>();
  378. for (Player player : tempPlayers) {
  379. if (playerBets.get(player) == minbet) {
  380. minPlayers.add(player);
  381. }
  382. playerBets.put(player, playerBets.get(player) - minbet);
  383. }
  384. List<Player> possibleWinners = new ArrayList<Player>();
  385. for (Player player : tempPlayers) {
  386. if (!player.hasFolded() && !player.isOut()) {
  387. possibleWinners.add(player);
  388. }
  389. }
  390. List<Player> theseWinners = getWinners(possibleWinners);
  391. potsize = potsize / theseWinners.size();
  392. if (potsize != 0) {
  393. for (Player player : theseWinners) {
  394. player.addCash(potsize);
  395. notifyWinner(player);
  396. }
  397. }
  398. tempPlayers.removeAll(minPlayers);
  399. maxbet = 0;
  400. for (Integer bet : playerBets.values()) {
  401. if (bet > maxbet) {
  402. maxbet = bet;
  403. }
  404. }
  405. }
  406. }
  407. /** {@inheritDoc} */
  408. public Deck getBestDeck(final Deck cards) {
  409. final Deck res = new Deck();
  410. res.addAll(cards);
  411. res.addAll(getCommunityCards());
  412. return res;
  413. }
  414. /** {@inheritDoc} */
  415. public boolean hasActiveHuman() {
  416. for (Player player : players) {
  417. if (player.isLocalHuman() && !player.isOut() && !player.hasFolded()) {
  418. return true;
  419. }
  420. }
  421. return false;
  422. }
  423. /** {@inheritDoc} */
  424. public Hand getHand(final Deck deck) {
  425. return new StandardHand(deck);
  426. }
  427. /** {@inheritDoc} */
  428. public String getHandText(final Player player) {
  429. String[] parts = getHand(player.getBestDeck()).getFriendlyName().split(": ");
  430. String res = "";
  431. for (String part : parts) {
  432. res = res + "\n" + part;
  433. }
  434. return res.substring(1);
  435. }
  436. protected List<Player> getWinners(final List<Player> winners) {
  437. final List<Player> res = new ArrayList<Player>();
  438. Collections.sort(winners);
  439. res.add(winners.get(0));
  440. for (int i = 1; i < winners.size(); i++) {
  441. if (winners.get(i).compareTo(winners.get(0)) == 0) {
  442. res.add(winners.get(i));
  443. }
  444. }
  445. return res;
  446. }
  447. protected abstract boolean canDoBringIns();
  448. /** {@inheritDoc} */
  449. public void registerObserver(final GameObserver observer) {
  450. observers.add(observer);
  451. }
  452. /** {@inheritDoc} */
  453. public void unregisterObserver(final GameObserver observer) {
  454. observers.remove(observer);
  455. }
  456. /**
  457. * Notifies all observers that the community cards have been upated.
  458. */
  459. protected void notifyCommunityCardsUpdated() {
  460. for (GameObserver observer : observers) {
  461. observer.communityCardsUpdated();
  462. }
  463. }
  464. /**
  465. * Notifies all observers that the player cards have been updated.
  466. */
  467. protected void notifyPlayerCardsUpdated() {
  468. for (GameObserver observer : observers) {
  469. observer.playerCardsUpdated();
  470. }
  471. }
  472. protected void notifyCardDealt(final Player player, final Card card) {
  473. for (GameObserver observer : observers) {
  474. observer.cardDealt(player, card);
  475. }
  476. }
  477. /**
  478. * Notifies all observers that it is the specified player's turn.
  479. *
  480. * @param player The player whose turn it is
  481. */
  482. protected void notifyPlayersTurn(final Player player) {
  483. for (GameObserver observer : observers) {
  484. observer.playersTurn(player);
  485. }
  486. }
  487. /**
  488. * Notifies all observers that a new player has joined.
  489. *
  490. * @param player The player who has joined
  491. */
  492. protected void notifyNewPlayer(final Player player) {
  493. for (GameObserver observer : observers) {
  494. observer.newPlayer(player);
  495. }
  496. }
  497. /**
  498. * Notifies all observers that a new game has started.
  499. */
  500. protected void notifyNewGame() {
  501. for (GameObserver observer : observers) {
  502. observer.newGame();
  503. }
  504. }
  505. /**
  506. * Notifies all observers that a game has ended.
  507. */
  508. protected void notifyEndGame() {
  509. for (GameObserver observer : observers) {
  510. observer.endGame();
  511. }
  512. }
  513. /**
  514. * Notifies all observers that the specified player is now the dealer.
  515. *
  516. * @param player The player who is now the dealer
  517. */
  518. protected void notifySetDealer(final Player player) {
  519. for (GameObserver observer : observers) {
  520. observer.setDealer(player);
  521. }
  522. }
  523. /**
  524. * Notifies all observers that the specified player has payed the specified
  525. * blind.
  526. *
  527. * @param player The player who's paying the blind
  528. * @param blind The value of the blind
  529. * @param name The name of the blind (big blind, small blind, ante, etc)
  530. */
  531. protected void notifyPlaceBlind(final Player player, final int blind,
  532. final String name) {
  533. for (GameObserver observer : observers) {
  534. observer.placeBlind(player, blind, name);
  535. }
  536. }
  537. /**
  538. * Notifies all observers that the specified player has raised.
  539. *
  540. * @param player The player that has raised
  541. * @param amount The amount the player raised by
  542. */
  543. protected void notifyRaise(final Player player, final int amount) {
  544. for (GameObserver observer : observers) {
  545. observer.raise(player, amount);
  546. }
  547. }
  548. /**
  549. * Notifies all observers that the specified player has folded.
  550. *
  551. * @param player The player who has folded
  552. */
  553. protected void notifyFold(final Player player) {
  554. for (GameObserver observer : observers) {
  555. observer.fold(player);
  556. }
  557. }
  558. /**
  559. * Notifies all observers that the specified player has called.
  560. *
  561. * @param player The player who has called
  562. */
  563. protected void notifyCall(final Player player) {
  564. for (GameObserver observer : observers) {
  565. observer.call(player);
  566. }
  567. }
  568. /**
  569. * Notifies all observers that the specified player has checked.
  570. *
  571. * @param player The player who has checked
  572. */
  573. protected void notifyCheck(final Player player) {
  574. for (GameObserver observer : observers) {
  575. observer.check(player);
  576. }
  577. }
  578. /**
  579. * Notifies all observers that the specified player has opened.
  580. *
  581. * @param player The player who has opened
  582. * @param amount The amount they opened at
  583. */
  584. protected void notifyOpen(final Player player, final int amount) {
  585. for (GameObserver observer : observers) {
  586. observer.open(player, amount);
  587. }
  588. }
  589. protected void notifyDiscards(final Player player, final int amount) {
  590. for (GameObserver observer : observers) {
  591. observer.discards(player, amount);
  592. }
  593. }
  594. /**
  595. * Notifies all observers that the specified player is a winner.
  596. *
  597. * @param player The player who has won
  598. */
  599. protected void notifyWinner(final Player player) {
  600. for (GameObserver observer : observers) {
  601. observer.winner(player);
  602. }
  603. }
  604. /**
  605. * Notifies all observers that the showdown is taking place.
  606. */
  607. protected void notifyShowdown() {
  608. for (GameObserver observer : observers) {
  609. observer.showdown();
  610. }
  611. }
  612. }