/*
* Copyright 2010 The gwtquery plugins team.
*
* 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 gwtquery.plugins.droppable.client.draughtssample;
import static com.google.gwt.query.client.GQuery.$;
import static gwtquery.plugins.droppable.client.draughtssample.CheckerBoard.CHECKERBOARD_SELECTOR;
import static gwtquery.plugins.droppable.client.draughtssample.DraughtsSample.EVENT_BUS;
import com.google.gwt.user.client.Timer;
import gwtquery.plugins.draggable.client.StopDragException;
import gwtquery.plugins.draggable.client.events.DragStartEvent;
import gwtquery.plugins.draggable.client.events.DragStopEvent;
import gwtquery.plugins.draggable.client.events.DragStartEvent.DragStartEventHandler;
import gwtquery.plugins.draggable.client.events.DragStopEvent.DragStopEventHandler;
import gwtquery.plugins.droppable.client.draughtssample.events.PieceCapturedEvent;
import gwtquery.plugins.droppable.client.draughtssample.events.PieceMoveEvent;
import gwtquery.plugins.droppable.client.draughtssample.events.PlayerChangeEvent;
import gwtquery.plugins.droppable.client.draughtssample.events.PlayerLostEvent;
import gwtquery.plugins.droppable.client.draughtssample.events.PieceMoveEvent.PieceMoveEventHandler;
import gwtquery.plugins.droppable.client.draughtssample.resources.DraughtsResources;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* This class contains all code needed for the control of the game
*
* @author Julien Dramaix (julien.dramaix@gmail.com)
*
*/
public class GameController implements PieceMoveEventHandler,
DragStartEventHandler, DragStopEventHandler {
/**
* Player type
*
* @author Julien Dramaix (julien.dramaix@gmail.com)
*
*/
public static enum Player {
RED, WHITE;
public String getKingClassName() {
if (this == RED) {
return DraughtsResources.INSTANCE.css().redKing();
} else {
return DraughtsResources.INSTANCE.css().whiteKing();
}
}
public String getPieceClassName() {
if (this == RED) {
return DraughtsResources.INSTANCE.css().redPiece();
} else {
return DraughtsResources.INSTANCE.css().whitePiece();
}
}
public int getYDirection() {
return this == Player.RED ? 1 : -1;
}
}
/**
* Position of a piece in the checkerboard.
*
* @author Julien Dramaix (julien.dramaix@gmail.com)
*
*/
public static class Position {
private int x;
private int y;
public Position(int column, int row) {
x = column;
y = row;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Position) || obj == null) {
return false;
}
Position pos2 = (Position) obj;
return pos2.x == x && pos2.y == y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
@Override
public int hashCode() {
return x ^ y;
}
public boolean isValid() {
return x >= 0 && x < CheckerBoard.SQUARE_NUMBER && y >= 0
&& y < CheckerBoard.SQUARE_NUMBER;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
}
private static GameController INSTANCE = new GameController();
public static GameController getInstance() {
return INSTANCE;
}
private Player currentPlayer;
private Timer currentTimer;
private Map<Position, Piece> pieceByPosition;
private boolean timerIsRunning;
private GameController() {
EVENT_BUS.addHandler(PieceMoveEvent.TYPE, this);
}
public Player getPlayerAt(Position position) {
Piece pieceAtPosition = pieceByPosition.get(position);
if (pieceAtPosition != null) {
return pieceAtPosition.getPlayer();
}
return null;
}
public void onDragStart(DragStartEvent event) {
Piece draggingPiece = (Piece) event.getDraggableWidget();
if (draggingPiece.getPlayer() != currentPlayer) {
// don't start the drag process it's not the right player !!
throw new StopDragException();
}
if (currentTimer != null) {
currentTimer.cancel();
timerIsRunning = false;
}
}
public void onDragStop(DragStopEvent event) {
if (currentTimer != null && !timerIsRunning) {
// we stopped the timer when the drag operation started and not jump is
// occurred during this drag operation
playerChange();
currentTimer = null;
}
}
public void onPieceMove(PieceMoveEvent event) {
maybeStopTimer();
Piece piece = event.getPiece();
Position newPosition = event.getNewPosition();
Position oldPosition = event.getOldPosition();
pieceMove(piece, oldPosition, newPosition);
if (maybeJumpingOccurred(piece, oldPosition, newPosition)
&& maybeMultipleJumpingIsPossible(piece)) {
// other jump is possible, let one sec to do it
startTimer();
} else {
playerChange();
}
}
public void pieceMove(Piece piece, Position oldPosition, Position newPosition) {
pieceByPosition.put(newPosition, piece);
if (oldPosition != null) { // real move, not init phase
pieceByPosition.remove(oldPosition);
piece.setPosition(newPosition);
}
}
public void reset() {
pieceByPosition = new HashMap<Position, Piece>();
currentPlayer = Player.RED;
}
public void restartGame() {
getCheckerBoard().clear();
reset();
startGame();
}
public void startGame() {
CheckerBoard checkerBoard = getCheckerBoard();
checkerBoard.fillBoard();
checkerBoard.lock();
EVENT_BUS.fireEvent(new PlayerChangeEvent(currentPlayer));
calculateNextMoves();
}
private boolean calculateNextMove(Piece currentPiece) {
List<Position> nextPossibleMove = currentPiece.getPossibleMove();
for (Position p : nextPossibleMove) {
getCheckerBoard().authorizeMove(currentPiece, p);
}
return nextPossibleMove.size() > 0;
}
private boolean calculateNextMoves() {
boolean hasNextMove = false;
for (Piece currentPiece : pieceByPosition.values()) {
if (currentPiece.getPlayer() != currentPlayer) {
continue;
}
hasNextMove |= calculateNextMove(currentPiece);
}
if (!hasNextMove) {
EVENT_BUS.fireEvent(new PlayerLostEvent(currentPlayer));
}
return hasNextMove;
}
private CheckerBoard getCheckerBoard() {
return (CheckerBoard) $(CHECKERBOARD_SELECTOR).widget();
}
private boolean maybeJumpingOccurred(Piece piece, Position oldPosition,
Position newPosition) {
int oldPositionX = oldPosition.getX();
int oldPositionY = oldPosition.getY();
int newPositionX = newPosition.getX();
int newPositionY = newPosition.getY();
int maybeCapturedX = newPositionX - (newPositionX > oldPositionX ? 1 : -1);
int maybeCapturedY = newPositionY - (newPositionY > oldPositionY ? 1 : -1);
Position maybeCaptured = new Position(maybeCapturedX, maybeCapturedY);
Player player = getPlayerAt(maybeCaptured);
if (player != null && piece.getPlayer() != player) {
Piece capturedPiece = pieceByPosition.remove(maybeCaptured);
capturedPiece.die();
EVENT_BUS.fireEvent(new PieceCapturedEvent(capturedPiece));
return true;
}
return false;
}
private boolean maybeMultipleJumpingIsPossible(Piece p) {
List<Position> nextJumps = p.getNextJumps();
CheckerBoard cb = getCheckerBoard();
cb.lock();
if (!nextJumps.isEmpty()) {
for (Position position : nextJumps) {
cb.authorizeMove(p, position);
}
return true;
}
return false;
}
private void maybeStopTimer() {
if (currentTimer != null) {
currentTimer.cancel();
currentTimer = null;
timerIsRunning = false;
}
}
private void playerChange() {
tooglePlayer();
getCheckerBoard().lock();
if (calculateNextMoves()) {
EVENT_BUS.fireEvent(new PlayerChangeEvent(currentPlayer));
}
}
private void startTimer() {
currentTimer = new Timer() {
@Override
public void run() {
playerChange();
currentTimer = null;
timerIsRunning = false;
}
};
// Wait 1 sec before to change the player...
// This allows the multiple jump
currentTimer.schedule(1000);
timerIsRunning = true;
}
private void tooglePlayer() {
if (currentPlayer == Player.RED) {
currentPlayer = Player.WHITE;
} else {
currentPlayer = Player.RED;
}
}
}