Code Search for Developers
 
 
  

Game.java from The Geronimo Project at Krugle


Show Game.java syntax highlighted

package geronimo.hoshigo.model.game;

import geronimo.hoshigo.model.goban.GoColor;
import geronimo.hoshigo.model.goban.GoColorTools;
import geronimo.hoshigo.model.goban.IllegalGobanSize;
import geronimo.hoshigo.model.goban.IllegalVertexException;
import geronimo.hoshigo.model.goban.TakenVertexException;
import geronimo.hoshigo.model.goban.Vertex;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;

public class Game implements Runnable
{
	private enum InternalState { WAITING_FOR_MOVE,
		                         WAITING_FOR_LEGAL_MOVES,
		                         WAITING_FOR_SCORE,
		                         WAITING_NOTHING,
		                         ENDED };
    // joueur qui est en train de jouer ou qui vient de jouer
    private GoColor playingColor;
    // coups legaux générés
    private Set<Move> legalMoves;
    
	// nombre de pierres d'handicap pour noir
	public final int handicap;
	// valeur du komi
	public final float komi;
	
	// joueurs et arbitre
	private Player white;
	private Player black;
	private Referee referee;
	
	// état origine de la partie
	private GameState rootGameState;
	// état courrant de la partie
	public GameState currentGameState; // TODO changer la visibilité en provate - test en cours
	// état interne du jeu
	private InternalState internalState;
	// score de la partie lorsque elle est terminée
	private Score score;
	
	// thread associé au jeu
	private Thread gameThread;
	
	// écouteurs de changement d'état du jeu
	private final Set<GameListener> gameListeners;

	/**
	 * Constructeur
	 * @param gobanSize taille du goban
	 * @param handicap nombre de pierres de handicap
	 * @param komi valeur du komi
	 * @param freeHandicap definit si l'handicap est libre ou non
	 * @param black joueur noir
	 * @param white joueur blanc
	 * @param referee arbitre
	 * @throws InvalidHandicapException si les paramètre d'andicap sont invalides
	 * @throws IllegalGobanSize si la taille du goban est invalide
	 */
	public Game(int gobanSize, int handicap, float komi, boolean freeHandicap,
			    Player black, Player white, Referee referee) 
		throws InvalidHandicapException, IllegalGobanSize
	{
		super();
		// initialisation des champs simples
		this.black = null;
		this.white = null;
		this.referee = null;
		this.score = null;
		this.handicap = handicap;
		this.komi = komi;
		this.rootGameState = new GameState(gobanSize);
		this.currentGameState = this.rootGameState;

		this.gameListeners =
			Collections.synchronizedSet(new HashSet<GameListener>());
		
		this.internalState = InternalState.WAITING_NOTHING;
		this.gameThread = null;
		this.playingColor = GoColor.BLACK;
		this.legalMoves = null;
		
		// verification de la valeur du handicap
		if (this.handicap < 0 && this.handicap > gobanSize * gobanSize)
		{
			throw new InvalidHandicapException(handicap);
		}
		
		if (handicap != 0 && ! freeHandicap)
		{
			// si l'handicap n'est pas free on verifie qu'il coresponde à un
			// placement fixé normal
			if ( ! FixedHandicapGameGenerator.
			      isValidFixedHandicap(gobanSize,handicap))
			{
				throw new InvalidHandicapException(handicap);
			}
			else
			{
				this.currentGameState = 
					FixedHandicapGameGenerator.
				       getHandicapGameState(this.rootGameState,handicap);
				// change de joueur courrant après avoir placé les pierres 
				// de handicap
				this.switchCurrentPlayer();
				this.rootGameState = this.currentGameState;
			}
		}
		
		this.setPlayer(GoColor.BLACK,black);
		this.setPlayer(GoColor.WHITE, white);
		this.setReferee(referee);
		
		
	}

	public synchronized void start()
	{
		if (this.gameThread == null)
		{
			// demarre les acteurs du jeu
			this.black.start();
			this.white.start();
			this.referee.start();
			
			// lance le jeu
			this.gameThread = new Thread(this);
			this.internalState = InternalState.WAITING_NOTHING;
			this.gameThread.start();
		}

	}
	
	private void switchCurrentPlayer()
	{
		if (this.hasToPlaceHandicap())
		{
			this.playingColor = GoColor.BLACK;
		}
		else
		{
			if (this.currentGameState != this.rootGameState)
			{
				this.playingColor = 
					GoColorTools.opposite(this.currentGameState.lastMove.color);
			}
		}
	}
	
	/**
	 * Décide de l'action à effectuer en fonction de l'état interne et de
	 * l'état courrant du jeu
	 */
	private void performActions()
	{
		// si on attends une réponse alors on n'a rien à faire dans cette 
		// méthode
		if (this.internalState != InternalState.WAITING_NOTHING)
		{
			return;
		}
		
		// si on doit placer des pierres d'handicap
		if (this.isEnded())
		{
			this.queryScore();
		}
		else if (this.hasToPlaceHandicap())
		{
			this.requestHandicapMove();
		}
		else if (! this.hasLegalMoves())
		{
			this.requestLegalMoves();
		}
		else if (this.hasLegalMoves())
		{
			// demande les coups légaux au
			this.requestMove();
		}
	}
	
	/**
	 * @return <tt>true</tt> si les légal moves ont été calculées
	 *         <tt>false</tt> sinon
	 */
	private boolean hasLegalMoves()
	{
		return this.legalMoves != null;
	}

	/**
	 * Demande les coups légaux
	 */
	private void requestLegalMoves()
	{
        // si on attends une réponse ou qu'on a déjà reçu les coups légaux
		// alors on n'a rien à faire dans cette méthode
		if (this.internalState != InternalState.WAITING_NOTHING ||
			this.hasLegalMoves())
		{
			return;
		}
		
		this.internalState = InternalState.WAITING_FOR_LEGAL_MOVES;
		this.referee.legalMoveNeeded();
	}

	/**
	 * Demande au joueur suivant de générer sont coup
	 */
	private void requestMove()
	{
		// si on attends une réponse alors on n'a rien à faire dans cette 
		// méthode
		if (this.internalState != InternalState.WAITING_NOTHING ||
			! this.hasLegalMoves())
		{
			return;
		}
		
		this.internalState = InternalState.WAITING_FOR_MOVE;
		this.getPlayer(this.playingColor).moveNeeded(this.legalMoves);
	}

	/**
	 * Demande au joueur handicap de donner ses coups
	 */
	private void requestHandicapMove()
	{
		// si on attends une réponse alors on n'a rien à faire dans cette 
		// méthode
		if (this.internalState != InternalState.WAITING_NOTHING)
		{
			return;
		}
		
		this.internalState = InternalState.WAITING_FOR_MOVE;
		// génération de tout les coups possibles
		Set<Move> legalMoves = new HashSet<Move>();
		for (Vertex v : this.currentGameState.getGoban().getEmptyCoords())
		{
			legalMoves.add(new StoneMove(v,this.playingColor));
		}
		
		// envoi du message au joueur noir
		this.black.moveNeeded(legalMoves);
	}
	
	/**
	 * @return <tt>true</tt> si on a encore besoin de placer des pierres
	 *         de handicap, <tt>false</tt> sinon 
	 */
	private boolean hasToPlaceHandicap()
	{
		return this.currentGameState.depth < this.handicap;
	}
	/**
	 * Retourne le joueur d'une certaine couleur
	 * @param color couleur du joueur voulu
	 * @return le joueur d'une certaine couleur
	 */
	public Player getPlayer(GoColor color)
	{
		if (color == GoColor.BLACK)
		{
			return this.black;
		}
		else
		{
			return this.white;
		}
	}

	/**
	 * Met à jour le joueur d'une certaine couleur
	 * @param color couleur du joueur voulu
	 * @param player player
	 * @return le joueur d'une certaine couleur
	 */
	public synchronized void setPlayer(GoColor color, Player player)
	{
		Player p = this.getPlayer(color);
		if (p != null)
		{
			this.removeGameListener(p);
			p.setGame(null);
		}
		if (player != null)
		{
			
			if (color == GoColor.BLACK)
			{
				this.black = player;
				this.black.setGame(this);
				this.addGameListener(player);
			}
			else
			{
				this.white = player;
				this.white.setGame(this);
				this.addGameListener(player);
			}
		}
		
		
		// notifie les écouteurs
		this.notifyGameListeners();
	}
	
	/**
	 * Change l'arbitre du jeu
	 * @param referee
	 */
	public synchronized void setReferee(Referee referee)
	{
		if (this.referee != null)
		{
			this.removeGameListener(this.referee);
			this.referee.setGame(null);
		}
		if (referee != null)
		{
			this.referee = referee;
			this.referee.setGame(this);
			this.addGameListener(this.referee);
			// notifie les écouteurs
			this.notifyGameListeners();
		} 

		
	}
	
	/**
	 * @return l'arbitre de la partie
	 */
	public Referee getReferee()
	{
		return this.referee;
	}
	
	/**
	 * Fonction appelée par un joueur en réponse au
	 * genMove appelé par le jeu
	 * @param move coup joué par le joueur
	 * @param player joueur qui a répondu
	 */
	public synchronized void moveGenerated(Move move, Player player)
	{
		// verifie que la méthode est appelée au bon moment
		if (this.internalState == InternalState.WAITING_FOR_MOVE &&
			this.getPlayer(this.playingColor) == player)
		{
			try
			{
				this.currentGameState = new GameState(this.currentGameState, move);
				// on met à jour le joueur qui doit jouer
				this.switchCurrentPlayer();

				
			}
			catch (TakenVertexException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			catch (IllegalVertexException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			finally
			{
				this.internalState = InternalState.WAITING_NOTHING;
				// efface les coups légaux
				this.legalMoves = null;
				
				this.notifyGameListeners();
				// continue la boucle évenementielle
				this.notifyAll();
								
			}
			
		}
	}

	/**
	 * Demande le score à l'arbitre
	 */
	private void queryScore()
	{
		if (this.internalState == InternalState.WAITING_NOTHING)
		{
			this.internalState = InternalState.WAITING_FOR_SCORE;
			this.referee.scoreNeeded();
		}
	}

	/**
	 * Notifie tous le écouteurs que l'etat courrant du jeu à changé
	 */
	private void notifyGameListeners()
	{
		for (GameListener l : this.gameListeners)
		{
			l.gameChanged(this);
		}
		
	}

	/**
	 * Methode appelée en réponse au scoreNeeded
	 * @param scrore score calculé
	 * @param referee référence vers l'arbitre qui réponds
	 */
	public synchronized void scoreCalculated(Score score, Referee referee)
	{
		if (this.internalState == InternalState.WAITING_FOR_SCORE
			&& this.referee == referee)
		{
			if (this.isEnded())
			{
				this.internalState = InternalState.ENDED;
				this.score = score;
				this.notifyGameEnded();
			}
			else
			{
				this.internalState = InternalState.WAITING_NOTHING;
				this.score = score;
				
			}	
			// continue la boucle évènementielle
			this.notifyAll();
		}
	}

	/**
	 * notifie à tous les écouteurs que la partie est terminée
	 */
	private void notifyGameEnded()
	{
		for (GameListener l : this.gameListeners)
		{
			l.gameEnded(this,this.score);
		}
		
	}

	/**
	 * Methode appelée en réponse au legalMoveNeeded
	 * @param legalMoves coups légaux calculés
	 * @param referee référence vers l'arbitre qui réponds
	 */
	public synchronized void legalMovesCalculated(Set<Move> legalMoves, Referee referee)
	{
		if (this.internalState == InternalState.WAITING_FOR_LEGAL_MOVES 
			&& this.referee == referee)
		{
			this.legalMoves = legalMoves;
			this.internalState = InternalState.WAITING_NOTHING;
			this.notifyAll();
		}
	}

	/**
	 * @return Returns the currentGameState.
	 */
	public GameState getCurrentGameState()
	{
		return this.currentGameState;
	}

	/**
	 * @return Returns the rootGameState.
	 */
	public GameState getRootGameState()
	{
		return this.rootGameState;
	}

	/**
	 * le légendaire toString
	 */
	public String toString()
	{
		if (this.isEnded() && this.score != null)
		{
			return this.black.getName() + " vs " + this.white.getName() + " : " 
			       + this.score;
		}
		else
		{
			return this.black.getName() + " vs " + this.white.getName();
		}
	}
	
	/**
	 * Détermine si la partie est terminée, en fonction des deux derniers coups
	 * des joueurs
	 * @return <tt>true</tt> si la partie est terminnée, <tt>false</tt> sinon
	 */
	public boolean isEnded()
	{
		if (this.currentGameState.depth > 1)
		{
			return    this.currentGameState.lastMove.isPass() 
			       && this.currentGameState.getParent().lastMove.isPass();
		}
		else
		{
			return false;
		}
	}
	
	/**
	 * Ajoute un écouteur de changement d'état du jeu
	 * @param listener ecouteur à rajouter
	 */
	public void addGameListener(GameListener listener)
	{
		this.gameListeners.add(listener);
	}
	
	/**
	 * Enleve un écouteur de changement d'état du jeu
	 * @param listener écouteur à enlever
	 */
	public void removeGameListener(GameListener listener)
	{
		this.gameListeners.remove(listener);
	}
	
	/**
	 * Boucle principale du thread associé au jeu
	 */
	public synchronized void run()
	{
		// lance la première action
		this.performActions();

		while (this.internalState != InternalState.ENDED)
		{
			try
			{
				this.wait();
				this.performActions();
			}
			catch (InterruptedException e)
			{
				// c'est normal si le programme est destroyé
			}
			
			
		}
		
		// déréférence le thread qui va mourrir
		this.gameThread = null;
	}
	
	/**
	 * Annule le dernierCoup et notifie les observeurs
	 * du nouvel l'état du jeu
	 * @TODO probleme de synchronisation si un joueur programme est en train de 
	 * jouer, voir abstractplayer.gameChanged()
	 */
	public synchronized void undo()
	{
		if (this.currentGameState.depth > 0)
		{
			// change l'état courrant
			this.currentGameState = this.currentGameState.getParent();
			
			this.internalState = InternalState.WAITING_NOTHING;
			this.legalMoves = null;
			
			// notifie du changement d'état du jeu
			this.notifyGameListeners();
		}
	}

	/**
	 * Détermine si un état de jeu fait parti d'une partie
	 * @param gs état à rechercher
	 * @return <tt>true</tt> si gs est contenu dans la partie, 
	 *         <tt>false</tt> sinon
	 */
	public boolean contains(GameState gs)
	{
		return this.rootGameState.equals(gs) || this.rootGameState.getAllChilds().contains(gs);
	}
	
	/**
	 * Permet d'avoir la liste des états entre un état de
	 * départ à un état cible dans l'arborescence du jeu et dans l'ordre
	 * (de l'état de départ à l'état d'arrivée)
	 * note : la liste contient aussi l'état de départ et l'état d'arrivée
	 * @param startState état de départ 
	 * @param endState état cible
	 * @return la liste des coups à jouer
	 */
	public List<GameState> getStatesSequenceBeetween(GameState startState, 
			                                         GameState targetState)
	{
		List<GameState> startParents = startState.getAllParents();
		List<GameState> targetParents = targetState.getAllParents();
		
		// liste des états qui permettent d'aller d'un état à l'autre
		List<GameState> interStates = new Vector<GameState>();
		
		// si les deux états sont les memes alors il n'y a pas de séquence
		// de coups entre eux
		if( startState.equals(targetState) ||
		   (startParents.isEmpty() && targetParents.isEmpty()) )
		{
			return  interStates;
		}
		// si les deux états sont sur la meme branche
		// et que l'état de départ est plus récent que l'état cible
		else if (startParents.contains(targetState))
		{
			// on ajoute l'état de départ et on enleve les parents de l'état
			// cible
			interStates.add(startState); // d'abort l'état de départ
			interStates.addAll(startParents); // puis les états entre
			interStates.removeAll(targetParents); // et enfin on enleve les états supéflus
		}
		// si les deux états sont sur la meme branche et que l'état de départ
		// est moins récent que l'état cible
		else if (targetParents.contains(startState))
		{
			// on ajoute l'état de départ et on enleve les parents de l'état
			// cible
			interStates.add(targetState); // d'abort l'état de départ
			interStates.addAll(targetParents); // puis les états entre
			interStates.removeAll(startParents); // enleve les états supéflus
			Collections.reverse(interStates); // renverse la liste des états
		}
		// si les deux états sont sur des branches différentes
		else
		{
			// cree la branche allant du départ au pere commun
			List<GameState> startBranch = new Vector<GameState>(startParents);
			startBranch.add(0,startState);
			startBranch.removeAll(targetParents);
			
			// cree la branche allant du pere commun à la cible
			List<GameState> targetBranch = new Vector<GameState>(targetParents);
			targetBranch.add(0,targetState);
			targetBranch.removeAll(startParents);
			Collections.reverse(targetBranch);
			
			// recupere le parent commun
			GameState commonFather =targetBranch.get(0).getParent();
			
			// cree la liste des coups
			interStates.addAll(startBranch);
			interStates.add(commonFather);
			interStates.addAll(targetBranch);
		}
		
		return interStates;
	}
	
	/**
	 * @return le joueur en train de jouer
	 */
	public Player getCurrentPlayer()
	{
		return this.getPlayer(this.playingColor);
	}

	/**
	 * @return Retourne la couleur du joueur qui doit jouer
	 */
	public GoColor getPlayingColor()
	{
		return this.playingColor;
	}

	/**
	 * Méthode appelée lorsque l'on veut détruire la partie
	 */
	public void destroy()
	{
		this.gameListeners.clear();
		this.internalState = InternalState.ENDED;
		if (this.gameThread != null)
		{
			this.gameThread.interrupt();
		}
		
		this.black.destroy();
		this.white.destroy();
		this.referee.destroy();
	}

	/**
	 * @return Returns the score.
	 */
	public Score getScore()
	{
		return score;
	}
}




See more files for this project here

The Geronimo Project

The Geronimo project concists of two software :\n- Geronimo Hoshigo : a playable graphical user interface to play Go\n- Geronimo Margo : a artificial intelligence program which plays Go

Project homepage: http://sourceforge.net/projects/geronimo
Programming language(s): Java,Pascal,Perl,PHP
License: gpl2

  AbstractPlayer.java
  AbstractReferee.java
  FixedHandicapGameGenerator.java
  Game.java
  GameListener.java
  GameState.java
  GoEntity.java
  HumanPlayer.java
  IllegalMoveColorException.java
  IllegalMoveVertexException.java
  InvalidHandicapException.java
  Move.java
  MoveException.java
  PassMove.java
  Player.java
  PlayerStateListener.java
  Referee.java
  RefereeActionListener.java
  RefereeStateListener.java
  Score.java
  StoneMove.java
  UnexpectedMoveTypeException.java