package GUI;
import Game.*;
import Pieces.BoardSquare;
import java.awt.event.*;
import java.awt.*;
import java.util.Scanner;
import javax.swing.*;
/**
* Controller for MVC
*/
public class Controller implements MouseListener, MouseMotionListener, ActionListener {
/**
* Reference for the view.
*/
View view;
/**
* The game instance being controlled.
*/
GameManager game;
/**
* Score of white.
*/
int whiteScore = 0;
/**
* Score of black.
*/
int blackScore = 0;
/**
* Current piece being held by the mouse.
*/
JLabel chessPiece;
int xAdjustment;
int yAdjustment;
/**
* X value of the piece being held, used to show possible moves.
*/
int currX;
/**
* Y value of the piece being held, used to show possible moves.
*/
int currY;
/**
* Flag to show possible moves when updating board.
*/
boolean showPossibleMoves;
/**
* Controller constructor.
*/
Controller(){
newWindow(GameType.STANDARD);
}
/**
* Event handler for button presses.
* @param e the ActionEvent captured.
*/
@Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("About")) {
JOptionPane.showMessageDialog(view.getWindow(),
"Chess by Kate Fayra\nVersion 1.2 for CS242\n",
"About", JOptionPane.INFORMATION_MESSAGE);
}
if (e.getActionCommand().equals("New Window")) {
int result = JOptionPane.showConfirmDialog(view.getWindow(),
"Are you sure you want to start a new game?\nThis will reset all statistics.",
"New Window", JOptionPane.YES_NO_OPTION);
if (result == JOptionPane.YES_OPTION) {
view.quit();
newWindow(GameType.STANDARD);
}
}
if (e.getActionCommand().equals("New Window (Custom)")) {
int result = JOptionPane.showConfirmDialog(view.getWindow(),
"Are you sure you want to start a new game?\nThis will reset all statistics.",
"New Window", JOptionPane.YES_NO_OPTION);
if (result == JOptionPane.YES_OPTION) {
view.quit();
newWindow(GameType.CUSTOM);
}
}
if (e.getActionCommand().equals("Exit")) {
int result = JOptionPane.showConfirmDialog(view.getWindow(),
"Are you sure you want to exit?",
"Exit", JOptionPane.YES_NO_OPTION);
if (result == JOptionPane.YES_OPTION)
view.quit();
}
if (e.getActionCommand().equals("Propose Takeback")) {
int result = JOptionPane.showConfirmDialog(view.getWindow(),
"An undo of the last move was proposed. Do both players agree?",
"Propose Takeback", JOptionPane.YES_NO_OPTION);
if (result == JOptionPane.YES_OPTION)
takeback();
}
if (e.getActionCommand().equals("Resign")) {
int result = JOptionPane.showConfirmDialog(view.getWindow(),
"Are you sure you want to resign?",
"Resign", JOptionPane.YES_NO_OPTION);
if (result == JOptionPane.YES_OPTION) {
resign();
}
}
if (e.getActionCommand().equals("Offer Draw")) {
int result = JOptionPane.showConfirmDialog(view.getWindow(),
"A draw was proposed. Do both players agree?",
"Offer Draw", JOptionPane.YES_NO_OPTION);
if (result == JOptionPane.YES_OPTION){
gameOver(Player.NONE);
}
}
}
/**
* Helper function to process resigning.
*/
private void resign() {
Player current = game.currentTurn();
view.setHistory("");
if (current == Player.WHITE) {
gameOver(Player.BLACK);
} else {
gameOver(Player.WHITE);
}
}
/**
* Helper function to process a confirmed takeback/undo.
*/
public void takeback() {
game.undo();//@todo refactor coordinates.
String history = view.getHistory();
Scanner histScanner = new Scanner(history);
String line = null;
while (histScanner.hasNextLine()) {
line = histScanner.nextLine();
System.out.println(line);
}
history = history.replace(line + "\n", "");
view.setHistory(history);
updateBoard();
}
/**
* Launches a new window, resetting all statistics.
*
* @param type type of game.
*/
void newWindow(GameType type){
game = new GameManager(type);
view = new View(1300, 1020, this);
updateBoard();
setPlayers();
}
/**
* Get player's names and set them into view.
*/
void setPlayers(){
String whiteName = JOptionPane.showInputDialog(
view.getWindow(),
"Enter White's name.",
"What is your name?",
JOptionPane.PLAIN_MESSAGE);
String blackName = JOptionPane.showInputDialog(
view.getWindow(),
"Enter Black's name.",
"What is your name?",
JOptionPane.PLAIN_MESSAGE);
view.setNames(whiteName, blackName, whiteScore, blackScore);
}
/**
* Game over, add a point to the winner and start a new game.
* @param winner the player who won the game.
*/
void gameOver(Player winner){
view.setHistory("");
if(winner == Player.WHITE)
whiteScore++;
if(winner == Player.BLACK)
blackScore++;
game = new GameManager(GameType.STANDARD);
view.setScore(whiteScore, blackScore);
updateBoard();
view.controller.winMessage(winner, view);
}
/**
* Update the board
*
* Read in the model from gameManager and call public functions in View to set up the board.
*
*/
public void updateBoard() {
view.setTurn(game.currentTurn());
view.clearAndResetBoard();
//Build the Chess Board squares
view.buildDefaultSquares();
//Add pieces to the board
for (int i = 8 * 8; i > 0; i--) {
int x = (64 - i) % 8;
int y = (i - 1) / 8;
//Highlight squares where the player can move with his currently selected piece.
if (showPossibleMoves && game.validMove(currX, currY, x, y) != GameManager.MoveStatus.ERROR) {
view.buildHighlightedSquare(i, x, y);
}
BoardSquare piece = game.gameBoard.getPiece(x, y);
Player owner = piece.owner;
view.placePiece(i, owner, piece.imagename);
}
showPossibleMoves = false;
view.setBoardVisible();
}
/**
* Helper function used to find the X and Y values representing the piece in the model of Component c.
*
* Get's model's x and y coordinates for the Component c. This function works by comparing all components to the current piece held by the mouse.
*
* @param c component to get coordinates for.
*/
void findModelXandY(Component c) {
for (int i = 8 * 8; i > 0; i--) {
int x = (64 - i) % 8;
int y = (i - 1) / 8;
Component panel = view.getPiece(i);
if (c instanceof JLabel) {
try {
if (c == ((JPanel) panel).getComponent(0)) {
currX = x;
currY = y;
return;
}
} catch (Exception e) {
//silently ignore exception caught if the piece is not layered over the square.
}
}
if (panel == c) {
currX = x;
currY = y;
}
}
}
/**
* Add the selected chess piece to the dragging layer so it can be moved.
* @param e Event captured.
*/
public void mousePressed(MouseEvent e) {
chessPiece = null;
Component c = view.findPieceAt(e.getX(), e.getY());
findModelXandY(c);
if (c instanceof JPanel) return;
Point parentLocation = c.getParent().getLocation();
xAdjustment = parentLocation.x - e.getX();
yAdjustment = parentLocation.y - e.getY();
chessPiece = (JLabel) c;
chessPiece.setLocation(e.getX() + xAdjustment, e.getY() + yAdjustment);
view.addPieceToCursor(this);
}
/**
* Move the chess piece around
* @param me Event captured.
*/
public void mouseDragged(MouseEvent me) {
if (chessPiece == null) return;
//The drag location should be within the bounds of the chess board
int x = me.getX() + xAdjustment;
int xMax = view.boardWidth() - chessPiece.getWidth();
x = Math.min(x, xMax);
x = Math.max(x, 0);
int y = me.getY() + yAdjustment;
int yMax = view.boardHeight() - chessPiece.getHeight();
y = Math.min(y, yMax);
y = Math.max(y, 0);
chessPiece.setLocation(x, y);
}
/**
* Drop the chess piece back onto the chess board
*/
public void mouseReleased(MouseEvent e) {
view.hideCursor();
if (chessPiece == null) return;
// Make sure the chess piece is no longer painted on the layered pane
chessPiece.setVisible(false);
view.removePieceGraphic(chessPiece);
chessPiece.setVisible(true);
// The drop location should be within the bounds of the chess board
int xMax = view.boardWidth() - chessPiece.getWidth();
int x = Math.min(e.getX(), xMax);
x = Math.max(x, 0);
int yMax = view.boardHeight() - chessPiece.getHeight();
int y = Math.min(e.getY(), yMax);
y = Math.max(y, 0);
Component c = view.findPieceAt(x, y);
int sourceX = currX;
int sourceY = currY;
//Set currX and currY to destination square. (The square which the player dropped the piece.)
findModelXandY(c);
//check move logic and attempt to move
checkMoveLogic(sourceX, sourceY);
}
/**
* Attempt to move by calling GameManager.Move
*
* Test if the game situation is check or checkmate, and send messages appropriately.
*
* @param sourceX source X
* @param sourceY source Y
*/
public void checkMoveLogic(int sourceX, int sourceY) {
System.out.println("Attempt move from: " + sourceX + " " + sourceY + ", to:" + currX + " " + currY);
if (game.Move(sourceX, sourceY, currX, currY) != GameManager.MoveStatus.ERROR) {
System.out.println("Move valid!");
Command cmd = (Command) (game.MoveHist.peek());
view.addToHistory(cmd.histStr + "\n");
updateBoard();
Player inCheck = game.testCheck(false);//test check
if(inCheck != Player.NONE) {
checkMessage(inCheck);
inCheck = game.testCheck(true);//test checkMate
if (inCheck == Player.WHITE) {
System.out.println("White is in checkmate.");
checkMateMessage(inCheck);
gameOver(Player.BLACK);//black wins.
} else if (inCheck == Player.BLACK) {
System.out.println("Black is in checkmate.");
checkMateMessage(inCheck);
gameOver(Player.WHITE);//white wins.
}
}
if(game.testStalemate()) {
staleMateMessage();
gameOver(Player.NONE);
}
} else if (sourceX == currX && sourceY == currY) {
showPossibleMoves = true;
updateBoard();
} else {
updateBoard();
illegalMoveMessage();
}
}
/**
* Do nothing with a click.
*/
public void mouseClicked(MouseEvent e) {
}
/**
* Do nothing with a mouse move.
*/
public void mouseMoved(MouseEvent e) {
}
/**
* Do nothing when mouse enters view.
*/
public void mouseEntered(MouseEvent e) {
}
/**
* Do nothing when mouse exits view.
*/
public void mouseExited(MouseEvent e) {
}
/**
* Send a message if the player is in check.
* @param playerInCheck the player in check
*/
public void checkMessage(Player playerInCheck){
if(playerInCheck == Player.BLACK) {
JOptionPane.showMessageDialog(view.getWindow(),
"Black is in check!",
"Check!", JOptionPane.INFORMATION_MESSAGE);
} else if (playerInCheck == Player.WHITE) {
JOptionPane.showMessageDialog(view.getWindow(),
"White is in check!",
"Check!", JOptionPane.INFORMATION_MESSAGE);
}
}
/**
* Send a message if the player is in check mate.
* @param playerInCheck the player in check mate
*/
public void checkMateMessage(Player playerInCheck){
if(playerInCheck == Player.BLACK) {
JOptionPane.showMessageDialog(view.getWindow(),
"Black is in checkmate!",
"Checkmate!", JOptionPane.INFORMATION_MESSAGE);
} else if (playerInCheck == Player.WHITE) {
JOptionPane.showMessageDialog(view.getWindow(),
"White is in checkmate!",
"Checkmate!", JOptionPane.INFORMATION_MESSAGE);
}
}
/**
* Send message if the move is illegal.
*/
public void illegalMoveMessage(){
JOptionPane.showMessageDialog(view.getWindow(),
"Move not allowed.",
"Illegal Move!", JOptionPane.INFORMATION_MESSAGE);
}
/**
* Notify user that the game has ended.
* @param winner the player who won the game.
* @param view the view.
*/
public void winMessage(Player winner, View view){
if(winner == Player.NONE){
JOptionPane.showMessageDialog(view.getWindow(),
"The game was a draw!",
"Draw", JOptionPane.INFORMATION_MESSAGE);
} else if(winner == Player.BLACK) {
JOptionPane.showMessageDialog(view.getWindow(),
"Black wins!",
"Game Over", JOptionPane.INFORMATION_MESSAGE);
} else if (winner == Player.WHITE) {
JOptionPane.showMessageDialog(view.getWindow(),
"White wins!",
"Game Over", JOptionPane.INFORMATION_MESSAGE);
}
}
private void staleMateMessage(){
JOptionPane.showMessageDialog(view.getWindow(),
"The game was a draw due to stalemate!",
"Stalemate!", JOptionPane.INFORMATION_MESSAGE);
}
}