/** * This file is part of JSkat. * * JSkat is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * JSkat is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with JSkat. If not, see <http://www.gnu.org/licenses/>. */ package org.jskat.gui.human; import org.jskat.data.GameAnnouncement; import org.jskat.gui.action.JSkatAction; import org.jskat.gui.action.JSkatActionEvent; import org.jskat.player.JSkatPlayer; import org.jskat.util.Card; import org.jskat.util.CardList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Human player */ public class SwingHumanPlayer extends AbstractHumanJSkatPlayer { private static Logger log = LoggerFactory.getLogger(SwingHumanPlayer.class); private Idler idler = new Idler(); private Boolean holdBid; private Integer bidValue; private GameAnnouncementStep gameAnnouncementStep; private Boolean playGrandHand; private Boolean callContra; private Boolean callRe; private Boolean pickUpSkat; private CardList discardSkat; private GameAnnouncement gameAnnouncement; private Card nextCard; /** * Used for situations where a human player can make more than one move. */ private enum GameAnnouncementStep { /** * Before any anouncement */ BEFORE_ANNOUNCEMENT, /** * Player looked into skat */ LOOKED_INTO_SKAT, /** * Player discarded skat */ DISCARDED_SKAT, /** * Player announced hand game */ PLAYS_HAND, /** * Game announcement is done */ DONE_GAME_ANNOUNCEMENT; } /** * Constructor */ public SwingHumanPlayer() { resetPlayer(); } /** * @see JSkatPlayer#announceGame() */ @Override public GameAnnouncement announceGame() { log.debug("Waiting for human game announcing..."); //$NON-NLS-1$ waitForUserInput(); gameAnnouncementStep = GameAnnouncementStep.DONE_GAME_ANNOUNCEMENT; return this.gameAnnouncement; } /** * @see JSkatPlayer#bidMore(int) */ @Override public Integer bidMore(final int nextBidValue) { log.debug("Waiting for human next bid value..."); //$NON-NLS-1$ waitForUserInput(); if (this.holdBid) { this.bidValue = nextBidValue; } else { this.bidValue = -1; } return this.bidValue; } /** * @see JSkatPlayer#discardSkat() */ @Override public CardList getCardsToDiscard() { log.debug("Waiting for human discarding..."); //$NON-NLS-1$ waitForUserInput(); return this.discardSkat; } /** * @see JSkatPlayer#preparateForNewGame() */ @Override public void preparateForNewGame() { resetPlayer(); } /** * @see JSkatPlayer#finalizeGame() */ @Override public void finalizeGame() { // TODO implement it } /** * @see JSkatPlayer#holdBid(int) */ @Override public Boolean holdBid(final int currBidValue) { log.debug("Waiting for human holding bid..."); //$NON-NLS-1$ waitForUserInput(); return this.holdBid; } /** * @see JSkatPlayer#pickUpSkat() */ @Override public Boolean playGrandHand() { log.debug("Waiting for human to decide if playing a grand hand..."); //$NON-NLS-1$ waitForUserInput(); return this.playGrandHand; } /** * @see JSkatPlayer#pickUpSkat() */ @Override public Boolean pickUpSkat() { log.debug("Waiting for human looking into skat..."); //$NON-NLS-1$ waitForUserInput(); return this.pickUpSkat; } /** * @see JSkatPlayer#playCard() */ @Override public Card playCard() { log.debug("Waiting for human playing next card..."); //$NON-NLS-1$ Card cardToPlay = null; if (nextCard == null) { waitForUserInput(); } cardToPlay = nextCard; nextCard = null; return cardToPlay; } @Override public void actionPerformed(final JSkatActionEvent e) { Object source = e.getSource(); String command = e.getActionCommand(); boolean interrupt = true; if (JSkatAction.PASS_BID.toString().equals(command)) { // player passed this.holdBid = false; } else if (JSkatAction.MAKE_BID.toString().equals(command)) { // player makes next bid value this.holdBid = true; } else if (JSkatAction.HOLD_BID.toString().equals(command)) { // player hold bid this.holdBid = true; } else if (JSkatAction.PLAY_GRAND_HAND.toString().equals(command)) { // player wants to play a grand hand this.playGrandHand = true; } else if (JSkatAction.PLAY_SCHIEBERAMSCH.toString().equals(command)) { this.playGrandHand = false; } else if (JSkatAction.CALL_CONTRA.toString().equals(command)) { callContra = true; } else if (JSkatAction.CALL_RE.toString().equals(command)) { if (source instanceof Boolean) { callRe = (Boolean) source; } } else if (JSkatAction.PICK_UP_SKAT.toString().equals(command)) { // player wants to pick up the skat this.pickUpSkat = true; gameAnnouncementStep = GameAnnouncementStep.LOOKED_INTO_SKAT; } else if (JSkatAction.SCHIEBEN.toString().equals(command)) { if (source instanceof CardList) { CardList cards = (CardList) source; if (cards.size() == 0) { pickUpSkat = false; } else { pickUpSkat = true; discardSkat = new CardList(cards); } } } else if (JSkatAction.ANNOUNCE_GAME.toString().equals(command)) { if (source instanceof GameAnnouncement) { // player did game announcement gameAnnouncement = (GameAnnouncement) source; if (gameAnnouncement.isHand()) { gameAnnouncementStep = GameAnnouncementStep.PLAYS_HAND; } else { setDiscardedSkatCards(gameAnnouncement.getDiscardedCards()); gameAnnouncementStep = GameAnnouncementStep.DISCARDED_SKAT; } } else { log.warn("Wrong source for " + command); //$NON-NLS-1$ interrupt = false; } } else if (JSkatAction.PLAY_CARD.toString().equals(command) && source instanceof Card) { this.nextCard = (Card) source; } else { log.warn("Unknown action event occured: " + command + " from " + source); //$NON-NLS-1$ //$NON-NLS-2$ } if (interrupt) { this.idler.interrupt(); } } /** * Starts waiting for user input */ public void waitForUserInput() { this.idler = new Idler(); this.idler.setMonitor(this); if (!isPlayerHasAlreadyPlayed()) { this.idler.start(); try { this.idler.join(); } catch (InterruptedException e) { log.warn("wait for user input was interrupted"); } } } private boolean isPlayerHasAlreadyPlayed() { log.debug("Game announcement step: " + gameAnnouncementStep); //$NON-NLS-1$ boolean result = false; if (GameAnnouncementStep.DISCARDED_SKAT.equals(gameAnnouncementStep) || GameAnnouncementStep.PLAYS_HAND.equals(gameAnnouncementStep)) { result = true; } return result; } private void setDiscardedSkatCards(final CardList discardedCards) { discardSkat = discardedCards; } /*------------------------------------------------------------------- * Inner class *-------------------------------------------------------------------*/ /** * Protected class implementing the waiting thread for user input */ protected static class Idler extends Thread { /** * Sets the monitoring object * * @param newMonitor * Monitor */ public void setMonitor(final Object newMonitor) { this.monitor = newMonitor; } /** * Stops the waiting */ public void stopWaiting() { this.doWait = false; } /** * @see Thread#run() */ @Override public void run() { synchronized (this.monitor) { while (this.doWait) { try { this.monitor.wait(); } catch (InterruptedException e) { stopWaiting(); } } } } private boolean doWait = true; private Object monitor = null; } /** * @see org.jskat.player.AbstractJSkatPlayer#startGame() */ @Override public void startGame() { // TODO is there something todo? } private void resetPlayer() { bidValue = 0; holdBid = false; playGrandHand = false; callContra = false; callRe = false; gameAnnouncementStep = GameAnnouncementStep.BEFORE_ANNOUNCEMENT; pickUpSkat = false; discardSkat = null; gameAnnouncement = null; nextCard = null; } @Override public Boolean callContra() { log.debug("Waiting for human calling contra..."); //$NON-NLS-1$ if (callContra == null) { waitForUserInput(); } return callContra == null ? false : callContra; } @Override public Boolean callRe() { log.debug("Waiting for human calling re..."); //$NON-NLS-1$ if (callRe == null) { waitForUserInput(); } return callRe == null ? false : callRe; } }