/** * Copyright (C) 2017 Jan Schäfer (jansch@users.sourceforge.net) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jskat.control; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.jskat.ai.nn.data.SkatNetworks; import org.jskat.ai.nn.train.NNTrainer; import org.jskat.control.command.general.ShowTrainingOverviewCommand; import org.jskat.control.command.table.CreateTableCommand; import org.jskat.control.event.general.NewJSkatVersionAvailableEvent; import org.jskat.control.event.iss.IssConnectedEvent; import org.jskat.control.event.table.DuplicateTableNameInputEvent; import org.jskat.control.event.table.EmptyTableNameInputEvent; import org.jskat.control.event.table.TableRemovedEvent; import org.jskat.control.iss.IssController; import org.jskat.data.GameAnnouncement; import org.jskat.data.JSkatApplicationData; import org.jskat.data.JSkatOptions; import org.jskat.data.JSkatOptions.SupportedLanguage; import org.jskat.data.JSkatViewType; import org.jskat.gui.JSkatView; import org.jskat.gui.action.JSkatAction; import org.jskat.gui.action.JSkatActionEvent; import org.jskat.gui.human.AbstractHumanJSkatPlayer; import org.jskat.player.JSkatPlayer; import org.jskat.player.JSkatPlayerResolver; import org.jskat.util.Card; import org.jskat.util.CardList; import org.jskat.util.GameType; import org.jskat.util.version.VersionChecker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.Subscribe; /** * Controls everything in JSkat */ // FIXME b0n541 2013-07-07: this is a god class, everything is controlled by and // over this class. It must be split into smaller pieces with respect to SoC and // SRP public class JSkatMaster { private static Logger log = LoggerFactory.getLogger(JSkatMaster.class); public final static JSkatMaster INSTANCE = new JSkatMaster(); private final JSkatOptions options; private final JSkatApplicationData data; private JSkatView view; private final IssController issControl; private final List<NNTrainer> runningNNTrainers; /** * Constructor */ private JSkatMaster() { this.options = JSkatOptions.instance(); this.data = JSkatApplicationData.INSTANCE; this.issControl = new IssController(this); this.runningNNTrainers = new ArrayList<NNTrainer>(); JSkatEventBus.INSTANCE.register(this); } /** * Checks the version of JSkat * * @param latestLocalVersion * Local version * @param latestRemoteVersion * Remote version */ public void checkJSkatVersion(final String latestLocalVersion, final String latestRemoteVersion) { log.debug("Latest version web: " + latestRemoteVersion); //$NON-NLS-1$ log.debug("Latest version local: " + latestLocalVersion); //$NON-NLS-1$ if (VersionChecker.isHigherVersionAvailable(latestLocalVersion, latestRemoteVersion)) { log.debug("Newer version " + latestRemoteVersion + " is available on the JSkat website."); //$NON-NLS-1$//$NON-NLS-2$ JSkatEventBus.INSTANCE.post(new NewJSkatVersionAvailableEvent(latestRemoteVersion)); } } /** * Creates a new skat table */ public void createTable() { // TODO check whether a connection to ISS is established // TODO ask whether a local or a remote tabel should be created String tableName = this.view.getNewTableName(this.data.getLocalTablesCreated()); if (tableName == null) { log.debug("Create table was cancelled..."); //$NON-NLS-1$ return; } if (tableName.isEmpty()) { showEmptyInputNameMessage(); // try again createTable(); return; } if (this.data.isFreeTableName(tableName)) { createLocalTable(tableName, this.view.getHumanPlayerForGUI()); } else { JSkatEventBus.INSTANCE.post(new DuplicateTableNameInputEvent(tableName)); // try again createTable(); } } private void createLocalTable(final String tableName, final AbstractHumanJSkatPlayer humanPlayer) { JSkatEventBus.INSTANCE.post(new CreateTableCommand(JSkatViewType.LOCAL_TABLE, tableName)); } /** * Gets the view implementation. * * @return The JSkat view implementation * * @deprecated Use only until event processing is completely implemented. */ @Deprecated public JSkatView getView() { return view; } /** * Removes a table * * @param event * Table removed event */ @Subscribe public void removeTableDataOn(final TableRemovedEvent event) { if (JSkatViewType.LOCAL_TABLE.equals(event.tableType)) { this.data.removeLocalSkatTable(event.tableName); } else if (JSkatViewType.ISS_TABLE.equals(event.tableType)) { this.data.removeJoinedIssSkatTable(event.tableName); } } /** * Invites players on ISS to the current table */ public void invitePlayer() { Set<String> issPlayerNames = this.data.getAvailableISSPlayer(); issPlayerNames.remove(this.data.getIssLoginName()); List<String> player = this.view.getPlayerForInvitation(issPlayerNames); for (String currPlayer : player) { getIssController().invitePlayer(this.data.getActiveTable(), currPlayer); } } /** * Starts a new series with given parameters * * @param allPlayer * Player types * @param playerNames * Player names * @param numberOfRounds * Number of rounds to be played * @param unlimited * TRUE, if unlimited rounds should be played * @param onlyPlayRamsch * TRUE, if only Ramsch games should be played * @param sleeps * Milliseconds to wait after a games ends during a series */ public void startSeries(List<String> allPlayer, List<String> playerNames, int numberOfRounds, boolean unlimited, boolean onlyPlayRamsch, int sleeps) { log.debug(this.data.getActiveTable()); SkatTable table = this.data.getLocalSkatTable(this.data.getActiveTable()); table.removePlayers(); int playerCount = 0; for (String player : allPlayer) { JSkatPlayer newPlayer = null; if (JSkatPlayerResolver.HUMAN_PLAYER_CLASS.equals(player)) { newPlayer = this.data.getHumanPlayer(table.getName()); } else { newPlayer = createPlayer(player); } newPlayer.setPlayerName(playerNames.get(playerCount)); table.placePlayer(newPlayer); playerCount++; } table.startSkatSeries(numberOfRounds, unlimited, onlyPlayRamsch, sleeps); } public JSkatPlayer createPlayer(String player) { JSkatPlayer newPlayer = null; try { newPlayer = (JSkatPlayer) Class.forName(player).newInstance(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } return newPlayer; } /** * Pauses a skat series at a table * * @param tableName * Table name */ public void pauseSkatSeries(final String tableName) { SkatTable table = this.data.getLocalSkatTable(tableName); if (table.isSeriesRunning()) { table.pauseSkatSeries(); } } /** * Starts a new skat series */ public void resumeSkatSeries() { log.debug(this.data.getActiveTable()); resumeSkatSeries(this.data.getActiveTable()); } /** * Resumes a skat series at a table * * @param tableName * Table name */ public void resumeSkatSeries(final String tableName) { SkatTable table = this.data.getLocalSkatTable(tableName); if (table.isSeriesRunning()) { table.resumeSkatSeries(); } } /** * Resumes a skat game at a table * * @param tableName * Table name */ public void resumeSkatGame(final String tableName) { SkatTable table = this.data.getLocalSkatTable(tableName); if (table.isSeriesRunning()) { table.resumeSkatGame(); } } /** * Checks whether a skat game is waiting * * @param tableName * Table name * @return TRUE if the game is waiting */ public boolean isSkatGameWaiting(final String tableName) { boolean result = false; SkatTable table = this.data.getLocalSkatTable(tableName); if (table.isSeriesRunning()) { result = table.isSkatGameWaiting(); } return result; } /** * Checks whether a skat series is waiting * * @param tableName * Table name * @return TRUE if the series is waiting */ public boolean isSkatSeriesWaiting(final String tableName) { boolean result = false; SkatTable table = this.data.getLocalSkatTable(tableName); if (table.isSeriesRunning()) { result = table.isSkatSeriesWaiting(); } return result; } /** * Places a skat player on a table * * @param tableName * Table ID * @param player * Skat player * @return TRUE if the placing was successful */ public synchronized boolean placePlayer(final String tableName, final JSkatPlayer player) { boolean result = false; SkatTable table = this.data.getLocalSkatTable(tableName); if (!table.isSeriesRunning()) { if (table.getPlayerCount() < table.getMaxPlayerCount()) { result = table.placePlayer(player); } } return result; } /** * Sets the view (for MVC) * * @param newView * View */ public void setView(final JSkatView newView) { this.view = newView; this.issControl.setView(this.view); } /** * Exits JSkat */ public void exitJSkat() { this.options.saveJSkatProperties(); System.exit(0); } /** * Shows the error message of wrong (null) name input */ public void showEmptyInputNameMessage() { JSkatEventBus.INSTANCE.post(new EmptyTableNameInputEvent()); } /** * Trains the neural networks */ public void trainNeuralNetworks() { JSkatEventBus.INSTANCE.post(new ShowTrainingOverviewCommand()); NNTrainer nullTrainer = new NNTrainer(); nullTrainer.setGameType(GameType.NULL); nullTrainer.start(); this.runningNNTrainers.add(nullTrainer); NNTrainer grandTrainer = new NNTrainer(); grandTrainer.setGameType(GameType.GRAND); grandTrainer.start(); this.runningNNTrainers.add(grandTrainer); NNTrainer clubsTrainer = new NNTrainer(); clubsTrainer.setGameType(GameType.CLUBS); clubsTrainer.start(); this.runningNNTrainers.add(clubsTrainer); NNTrainer spadesTrainer = new NNTrainer(); spadesTrainer.setGameType(GameType.SPADES); spadesTrainer.start(); this.runningNNTrainers.add(spadesTrainer); NNTrainer heartsTrainer = new NNTrainer(); heartsTrainer.setGameType(GameType.HEARTS); heartsTrainer.start(); this.runningNNTrainers.add(heartsTrainer); NNTrainer diamondsTrainer = new NNTrainer(); diamondsTrainer.setGameType(GameType.DIAMONDS); diamondsTrainer.start(); this.runningNNTrainers.add(diamondsTrainer); NNTrainer ramschTrainer = new NNTrainer(); ramschTrainer.setGameType(GameType.RAMSCH); ramschTrainer.start(); this.runningNNTrainers.add(ramschTrainer); } public void stopTrainNeuralNetworks() { for (NNTrainer trainer : this.runningNNTrainers) { trainer.stopTraining(true); } this.runningNNTrainers.clear(); } /** * Loads the weigths for the neural networks */ public void loadNeuralNetworks() { SkatNetworks.instance(); SkatNetworks.loadNetworks(); } /** * Resets neural networks */ public void resetNeuralNetworks() { SkatNetworks.instance(); SkatNetworks.resetNeuralNetworks(); } public void saveNeuralNetworks(GameType gameType) { SkatNetworks.instance(); SkatNetworks.saveNetworks(this.options.getSavePath(), gameType); } /** * Saves the weigths for the neural networks */ public void saveNeuralNetworks() { SkatNetworks.instance(); SkatNetworks.saveNetworks(this.options.getSavePath()); } /** * Triggers the human player interface to stop waiting * * @param event * Action event */ public void triggerHuman(final JSkatActionEvent event) { log.debug(event.toString()); String tableName = this.data.getActiveTable(); String command = event.getActionCommand(); Object source = event.getSource(); if (isIssTable(tableName)) { handleHumanInputForISSTable(tableName, command, source); } else { this.data.getHumanPlayer(tableName).actionPerformed(event); } } private void handleHumanInputForISSTable(final String tableName, final String command, final Object source) { if (JSkatAction.PASS_BID.toString().equals(command)) { // player passed this.issControl.sendPassBidMove(tableName); } else if (JSkatAction.MAKE_BID.toString().equals(command)) { // player makes bid this.issControl.sendBidMove(tableName); } else if (JSkatAction.HOLD_BID.toString().equals(command)) { // player hold bid this.issControl.sendHoldBidMove(tableName); } else if (JSkatAction.PICK_UP_SKAT.toString().equals(command)) { // player wants to pick up the skat this.issControl.sendPickUpSkatMove(tableName); } else if (JSkatAction.PLAY_HAND_GAME.toString().equals(command)) { // player wants to play a hand game // FIXME (jan 02.11.2010) decision is not sent to ISS } else if (JSkatAction.DISCARD_CARDS.toString().equals(command)) { if (source instanceof CardList) { // player discarded cards CardList discardSkat = (CardList) source; log.debug(discardSkat.toString()); // FIXME (jan 02.11.2010) Discarded cards are sent with the // game announcement to ISS // issControl.sendDiscardMove(tableName, // discardSkat.get(0), discardSkat.get(1)); } else { log.warn("No discarded cards found for " + command); //$NON-NLS-1$ } } else if (JSkatAction.ANNOUNCE_GAME.toString().equals(command)) { if (source instanceof GameAnnouncement) { // player did game announcement // FIXME (jan 02.11.2010) Discarded cards are sent with the // game announcement to ISS GameAnnouncement gameAnnouncement = (GameAnnouncement) source; this.issControl.sendGameAnnouncementMove(tableName, gameAnnouncement); } else { log.warn("No game announcement found for " + command); //$NON-NLS-1$ } } else if (JSkatAction.PLAY_CARD.toString().equals(command) && source instanceof Card) { Card nextCard = (Card) source; this.issControl.sendCardMove(tableName, nextCard); } else { log.error("Unknown action event occured: " + command + " from " + source); //$NON-NLS-1$ //$NON-NLS-2$ } } private boolean isIssTable(final String tableName) { return this.data.isTableJoined(tableName); } /** * Takes a card from the skat on the active skat table * * @param e * Event */ public void takeCardFromSkat(final JSkatActionEvent e) { if (!(e.getSource() instanceof Card)) { throw new IllegalArgumentException(); } this.view.takeCardFromSkat(this.data.getActiveTable(), (Card) e.getSource()); } /** * Put a card into the skat on the active skat table * * @param event * Event */ public void putCardIntoSkat(final JSkatActionEvent event) { if (!(event.getSource() instanceof Card)) { throw new IllegalArgumentException(); } this.view.putCardIntoSkat(this.data.getActiveTable(), (Card) event.getSource()); } /** * Gets the controller for playing on the ISS. * * @return ISS controller */ public IssController getIssController() { return this.issControl; } /** * Sets the active table. * * @param tableName * Table name */ public void setActiveTable(final String tableName) { if (this.data.isExistingLocalSkatTable(tableName)) { setActiveTable(JSkatViewType.LOCAL_TABLE, tableName); } else if (this.data.isTableJoined(tableName)) { setActiveTable(JSkatViewType.ISS_TABLE, tableName); } else { setActiveTable(JSkatViewType.OTHER, tableName); } } /** * Sets the name of the active table. * * @param type * View type * @param tableName * Table name */ public void setActiveTable(JSkatViewType type, String tableName) { this.data.setActiveTable(type, tableName); if (this.view != null) { // might not be instantiated yet this.view.setActiveView(tableName); } if (type == JSkatViewType.LOCAL_TABLE) { this.view.setGameState(tableName, this.data.getLocalSkatTable(tableName).getGameState()); } } /** * Sets the login name for ISS * * @param event * ISS connected event */ @Subscribe public void setLoginNameOn(final IssConnectedEvent event) { this.data.setIssLoginName(event.login); } /** * Leaves a skat table */ public void leaveTable() { String tableName = this.data.getActiveTable(); // FIXME distinguish between ISS and local skat table this.issControl.leaveTable(tableName); } /** * Updates ISS player information * * @param playerName * Player name * @param language * Language * @param gamesPlayed * Games played * @param strength * Playing strength */ public void updateISSPlayer(final String playerName, final String language, final long gamesPlayed, final double strength) { this.data.addAvailableISSPlayer(playerName); this.view.updateISSLobbyPlayerList(playerName, language, gamesPlayed, strength); } /** * Removes an ISS player * * @param playerName * Player name */ public void removeISSPlayer(final String playerName) { this.data.removeAvailableISSPlayer(playerName); this.view.removeFromISSLobbyPlayerList(playerName); } /** * Opens the ISS homepage viewin the default browser */ public void openIssHomepage() { openWebPage(getISSHomepageLink()); } private String getISSHomepageLink() { String result = "http://www.skatgame.net/iss/"; //$NON-NLS-1$ SupportedLanguage lang = JSkatOptions.instance().getLanguage(); switch (lang) { case GERMAN: result += "index-de.html"; //$NON-NLS-1$ break; case ENGLISH: result += "index.html"; //$NON-NLS-1$ break; } return result; } private void openWebPage(final String link) { this.view.openWebPage(link); } /** * Opens the ISS registration form in the default browser */ public void openIssRegisterPage() { openWebPage(getIssRegisterLink()); } private String getIssRegisterLink() { String result = "http://skatgame.net:7000/"; //$NON-NLS-1$ SupportedLanguage lang = JSkatOptions.instance().getLanguage(); switch (lang) { case GERMAN: result += "de-register"; //$NON-NLS-1$ break; case ENGLISH: result += "en-register"; //$NON-NLS-1$ break; } return result; } }