|
package com.tacticmaster.board; |
|
|
|
import android.content.Context; |
|
import android.graphics.Bitmap; |
|
import android.graphics.BitmapFactory; |
|
import android.graphics.Canvas; |
|
import android.graphics.Color; |
|
import android.graphics.Paint; |
|
import android.os.Handler; |
|
import android.os.Looper; |
|
import android.util.AttributeSet; |
|
import android.view.MotionEvent; |
|
import android.view.View; |
|
import android.widget.ImageView; |
|
import android.widget.Toast; |
|
|
|
import androidx.annotation.NonNull; |
|
|
|
import com.tacticmaster.R; |
|
import com.tacticmaster.puzzle.Puzzle; |
|
|
|
public class ChessboardView extends View { |
|
|
|
public interface PuzzleFinishedListener { |
|
void onPuzzleSolved(Puzzle puzzle); |
|
|
|
void onPuzzleNotSolved(Puzzle puzzle); |
|
} |
|
|
|
private static final int NEXT_PUZZLE_DELAY = 3000; |
|
private Puzzle puzzle; |
|
private Chessboard chessboard; |
|
private static final int BOARD_SIZE = 8; |
|
private Paint lightBrownPaint; |
|
private Paint darkBrownPaint; |
|
private Paint bitmapPaint; |
|
private Paint selectionPaint; |
|
private Bitmap whiteKing, blackKing, whiteQueen, blackQueen, whiteRook, blackRook, whiteBishop, blackBishop, whiteKnight, blackKnight, whitePawn, blackPawn; |
|
private Bitmap scaledWhiteKing, scaledBlackKing, scaledWhiteQueen, scaledBlackQueen, scaledWhiteRook, scaledBlackRook, scaledWhiteBishop, scaledBlackBishop, scaledWhiteKnight, scaledBlackKnight, scaledWhitePawn, scaledBlackPawn; |
|
private ImageView playerTurnIcon; |
|
private int selectedRow = -1; |
|
private int selectedCol = -1; |
|
private Paint textPaint; |
|
private final Handler handler = new Handler(Looper.getMainLooper()); |
|
private PuzzleFinishedListener puzzleFinishedListener; |
|
private boolean puzzleSolved = false; |
|
|
|
public ChessboardView(Context context, AttributeSet attrs) { |
|
super(context, attrs); |
|
init(); |
|
} |
|
|
|
private void init() { |
|
lightBrownPaint = new Paint(); |
|
lightBrownPaint.setColor(Color.parseColor("#D2B48C")); |
|
darkBrownPaint = new Paint(); |
|
darkBrownPaint.setColor(Color.parseColor("#8B4513")); |
|
textPaint = new Paint(); |
|
textPaint.setColor(Color.BLACK); |
|
textPaint.setTextSize(30); |
|
textPaint.setAntiAlias(true); |
|
|
|
bitmapPaint = new Paint(); |
|
bitmapPaint.setAntiAlias(true); |
|
bitmapPaint.setFilterBitmap(true); |
|
bitmapPaint.setDither(true); |
|
|
|
selectionPaint = new Paint(); |
|
selectionPaint.setColor(Color.YELLOW); |
|
selectionPaint.setStyle(Paint.Style.STROKE); |
|
selectionPaint.setStrokeWidth(5); |
|
|
|
whiteKing = BitmapFactory.decodeResource(getResources(), R.drawable.wk); |
|
blackKing = BitmapFactory.decodeResource(getResources(), R.drawable.bk); |
|
whiteQueen = BitmapFactory.decodeResource(getResources(), R.drawable.wq); |
|
blackQueen = BitmapFactory.decodeResource(getResources(), R.drawable.bq); |
|
whiteRook = BitmapFactory.decodeResource(getResources(), R.drawable.wr); |
|
blackRook = BitmapFactory.decodeResource(getResources(), R.drawable.br); |
|
whiteBishop = BitmapFactory.decodeResource(getResources(), R.drawable.wb); |
|
blackBishop = BitmapFactory.decodeResource(getResources(), R.drawable.bb); |
|
whiteKnight = BitmapFactory.decodeResource(getResources(), R.drawable.wn); |
|
blackKnight = BitmapFactory.decodeResource(getResources(), R.drawable.bn); |
|
whitePawn = BitmapFactory.decodeResource(getResources(), R.drawable.wp); |
|
blackPawn = BitmapFactory.decodeResource(getResources(), R.drawable.bp); |
|
} |
|
|
|
private void updatePlayerTurnIcon(boolean isWhiteTurn) { |
|
if (isWhiteTurn) { |
|
playerTurnIcon.setImageResource(R.drawable.ic_white_turn); |
|
} else { |
|
playerTurnIcon.setImageResource(R.drawable.ic_black_turn); |
|
} |
|
} |
|
|
|
public void setPlayerTurnIcon(ImageView playerTurnIcon) { |
|
this.playerTurnIcon = playerTurnIcon; |
|
} |
|
|
|
public void setPuzzle(Puzzle puzzle) { |
|
this.puzzle = puzzle; |
|
this.chessboard = new Chessboard(puzzle); |
|
this.selectedRow = -1; |
|
this.selectedCol = -1; |
|
this.puzzleSolved = false; |
|
|
|
invalidate(); |
|
handler.postDelayed(() -> { |
|
this.chessboard.makeFirstMove(); |
|
invalidate(); |
|
}, 2000); |
|
} |
|
|
|
public void setPuzzleSolvedListener(PuzzleFinishedListener listener) { |
|
this.puzzleFinishedListener = listener; |
|
} |
|
|
|
public Puzzle getPuzzle() { |
|
return puzzle; |
|
} |
|
|
|
public Chessboard getChessboard() { |
|
return chessboard; |
|
} |
|
|
|
public int getSelectedCol() { |
|
return selectedCol; |
|
} |
|
|
|
public int getSelectedRow() { |
|
return selectedRow; |
|
} |
|
|
|
@Override |
|
public boolean onTouchEvent(MotionEvent event) { |
|
if (event.getAction() == MotionEvent.ACTION_DOWN) { |
|
int width = getWidth(); |
|
int height = getHeight(); |
|
int tileSize = Math.min(width, height) / BOARD_SIZE; |
|
|
|
int col = (int) (event.getX() / tileSize); |
|
int row = (int) (event.getY() / tileSize); |
|
|
|
char piece = chessboard.getBoard()[row][col]; |
|
boolean isWhiteToMove = chessboard.isWhiteToMove(); |
|
|
|
if (selectedRow == -1 && selectedCol == -1) { |
|
if (piece != ' ' && ((isWhiteToMove && Character.isUpperCase(piece)) || (!isWhiteToMove && Character.isLowerCase(piece)))) { |
|
selectedRow = row; |
|
selectedCol = col; |
|
} |
|
} else if (chessboard.isFirstMoveDone()) { |
|
if (chessboard.isCorrectMove(selectedRow, selectedCol, row, col)) { |
|
|
|
if (chessboard.movePiece(selectedRow, selectedCol, row, col)) { |
|
selectedRow = -1; |
|
selectedCol = -1; |
|
|
|
|
|
handler.postDelayed(() -> { |
|
chessboard.makeNextMove(); |
|
invalidate(); |
|
}, 1300); |
|
} |
|
} else { |
|
Toast.makeText(getContext(), "Wrong solution", Toast.LENGTH_SHORT).show(); |
|
|
|
handler.postDelayed(() -> { |
|
puzzleFinishedListener.onPuzzleNotSolved(this.puzzle); |
|
}, NEXT_PUZZLE_DELAY); |
|
} |
|
} |
|
invalidate(); |
|
performClick(); |
|
return true; |
|
} |
|
return super.onTouchEvent(event); |
|
} |
|
|
|
@Override |
|
public boolean performClick() { |
|
return super.performClick(); |
|
} |
|
|
|
@Override |
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) { |
|
super.onSizeChanged(w, h, oldw, oldh); |
|
int tileSize = (int) ((Math.min(w, h) * 0.95f / BOARD_SIZE)); |
|
|
|
scaledWhiteKing = Bitmap.createScaledBitmap(whiteKing, tileSize, tileSize, true); |
|
scaledBlackKing = Bitmap.createScaledBitmap(blackKing, tileSize, tileSize, true); |
|
scaledWhiteQueen = Bitmap.createScaledBitmap(whiteQueen, tileSize, tileSize, true); |
|
scaledBlackQueen = Bitmap.createScaledBitmap(blackQueen, tileSize, tileSize, true); |
|
scaledWhiteRook = Bitmap.createScaledBitmap(whiteRook, tileSize, tileSize, true); |
|
scaledBlackRook = Bitmap.createScaledBitmap(blackRook, tileSize, tileSize, true); |
|
scaledWhiteBishop = Bitmap.createScaledBitmap(whiteBishop, tileSize, tileSize, true); |
|
scaledBlackBishop = Bitmap.createScaledBitmap(blackBishop, tileSize, tileSize, true); |
|
scaledWhiteKnight = Bitmap.createScaledBitmap(whiteKnight, tileSize, tileSize, true); |
|
scaledBlackKnight = Bitmap.createScaledBitmap(blackKnight, tileSize, tileSize, true); |
|
scaledWhitePawn = Bitmap.createScaledBitmap(whitePawn, tileSize, tileSize, true); |
|
scaledBlackPawn = Bitmap.createScaledBitmap(blackPawn, tileSize, tileSize, true); |
|
|
|
invalidate(); |
|
} |
|
|
|
@Override |
|
protected void onDraw(@NonNull Canvas canvas) { |
|
super.onDraw(canvas); |
|
|
|
int width = getWidth(); |
|
int height = getHeight(); |
|
int tileSize = Math.min(width, height) / BOARD_SIZE; |
|
|
|
boolean isWhiteToMove = chessboard.isWhiteToMove(); |
|
updatePlayerTurnIcon(isWhiteToMove); |
|
|
|
for (int row = 0; row < BOARD_SIZE; row++) { |
|
for (int col = 0; col < BOARD_SIZE; col++) { |
|
var colorChoice = (col + row) % 2 == 0; |
|
if (!isWhiteToMove) |
|
colorChoice = !colorChoice; |
|
Paint paint = colorChoice ? lightBrownPaint : darkBrownPaint; |
|
int left = col * tileSize; |
|
int top = row * tileSize; |
|
int right = left + tileSize; |
|
int bottom = top + tileSize; |
|
canvas.drawRect(left, top, right, bottom, paint); |
|
} |
|
} |
|
|
|
if (selectedRow != -1 && selectedCol != -1) { |
|
int left = selectedCol * tileSize; |
|
int top = selectedRow * tileSize; |
|
int right = left + tileSize; |
|
int bottom = top + tileSize; |
|
canvas.drawRect(left, top, right, bottom, selectionPaint); |
|
} |
|
|
|
for (int col = 0; col < BOARD_SIZE; col++) { |
|
String label = String.valueOf((char) ('a' + col)); |
|
float x = col * tileSize + ((float) tileSize / 2 - textPaint.measureText(label) / 2) * 1.9f; |
|
float y = height - 10; |
|
canvas.drawText(label, x, y, textPaint); |
|
} |
|
|
|
for (int row = 0; row < BOARD_SIZE; row++) { |
|
String label = isWhiteToMove ? String.valueOf(BOARD_SIZE - row) : String.valueOf(row + 1); |
|
float x = 10; |
|
float y = row * tileSize + ((float) tileSize / 2 + textPaint.getTextSize() / 2) * .4f; |
|
canvas.drawText(label, x, y, textPaint); |
|
} |
|
|
|
if (chessboard != null) { |
|
for (int row = 0; row < BOARD_SIZE; row++) { |
|
for (int col = 0; col < BOARD_SIZE; col++) { |
|
char piece = chessboard.getBoard()[row][col]; |
|
if (piece != ' ') { |
|
Bitmap pieceBitmap = getPieceBitmap(piece); |
|
if (pieceBitmap != null) { |
|
float left = col * tileSize; |
|
float top = row * tileSize; |
|
canvas.drawBitmap(pieceBitmap, left, top, bitmapPaint); |
|
} |
|
} |
|
} |
|
} |
|
if (chessboard.solved() && puzzleFinishedListener != null && !puzzleSolved) { |
|
puzzleSolved = true; |
|
handler.postDelayed(() -> puzzleFinishedListener.onPuzzleSolved(this.puzzle), NEXT_PUZZLE_DELAY); |
|
} |
|
} |
|
} |
|
|
|
private Bitmap getPieceBitmap(char piece) { |
|
return switch (piece) { |
|
case 'K' -> scaledWhiteKing; |
|
case 'k' -> scaledBlackKing; |
|
case 'Q' -> scaledWhiteQueen; |
|
case 'q' -> scaledBlackQueen; |
|
case 'R' -> scaledWhiteRook; |
|
case 'r' -> scaledBlackRook; |
|
case 'B' -> scaledWhiteBishop; |
|
case 'b' -> scaledBlackBishop; |
|
case 'N' -> scaledWhiteKnight; |
|
case 'n' -> scaledBlackKnight; |
|
case 'P' -> scaledWhitePawn; |
|
case 'p' -> scaledBlackPawn; |
|
default -> null; |
|
}; |
|
} |
|
} |