public void validateMove(Team movingTeam, int fromX, int fromY, int toX, int toY)
    throws IllegalMoveException
  {
    // Check that inputs make sense at all
    validateCoordsOnBoard(fromX, fromY);
    validateDestination(toX, toY);
    if (Team.NEUTRAL.equals(movingTeam)) {
      throw new IllegalArgumentException("Moving team can't be neutral.");
    }
    
    // Check there is a tile to move
    if (getTile(fromX, fromY) == null)
      throw new IllegalMoveException("Can't move a tile that isn't there.");
    
    // Check the moving tile is face-up
    if (!getTile(fromX, fromY).isFaceUp())
      throw new IllegalMoveException("Can't move a face-down tile.");
    
    // Check the tile is moving to another square
    if (fromX == toX && fromY == toY)
      throw new IllegalMoveException("Tile must move at least one square.");
    
    // Check the move is in the same rank or file (i.e. straight)
    // N.B. some of the subsequent checks depend on this assumption
    if (fromX != toX && fromY != toY)
      throw new IllegalMoveException("Tiles can't move diagonally.");

    // Check the tile has sufficient range
    validateRange(fromX, fromY, toX, toY);

    // Check there's no tiles in the way
    validateNoInterveningTiles(fromX, fromY, toX, toY);
    
    // Check the destination tile is valid prey
    validatePrey(fromX, fromY, toX, toY);
    
    // Check the tile doesn't belong to the other player
    validateNotOtherTeamsTile(movingTeam, fromX, fromY);
    
    // Check the tile isn't a neutral tile that was just flipped
    validateNotJustFlippedNeutralTile(fromX, fromY);
    
    // Check the player isn't reversing a previous move of their own tile
    validateNotReversingPreviousOwnTile(movingTeam, fromX, fromY, toX, toY);
    
    // Check the player isn't trying to rescue a neutral tile
    validateNotRescuingNeutralTile(fromX, fromY, toX, toY);
  }