package com.example.touchChess;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
public class MainActivity extends Activity {
private static Context sInstance;
private int h = 60;
private int dx = 26;
private int dy = 22;
private int touchX, touchY;
private int XPol, YPol;
private int selectedX = -1, selectedY = -1;
private boolean hasSelection = false;
private static final int STATE_IDLE = 0;
private static final int STATE_WAITING_CONFIRMATION = 1;
private static final int STATE_WAITING_ENGINE = 2;
private int serverState = STATE_IDLE;
private static final int SPECIAL_NONE = 0;
private static final int SPECIAL_CASTLING = 1;
private static final int SPECIAL_EN_PASSANT = 2;
private int pendingSpecial = SPECIAL_NONE;
private String pendingMove = "";
private int pendingFromNum = -1;
private Bitmap bitmapBackup;
private Bitmap[] arrayOfBitmap = new Bitmap[12];
private Bitmap bitmap1, bitmap2, bitmap3, bitmap4, bitmap5;
private Canvas canvasBmp3, canvasBmp5;
private Paint paint;
private DrawView drawView;
private String StrFen;
private Socket mSocket;
private InputStream mInputBuffer;
private OutputStream mOutputBuffer;
private Thread mReaderThread;
private volatile boolean mIsReading = false;
private String mServerPort = "8880";
private String mServerAddress = "192.168.4.1";
private TextView mTextViewContent;
private ScrollView mScrollViewContent;
private Button mButtonConnect;
private Button mButtonSend;
private EditText mEditTextCommand;
public static Context getInstance() { return sInstance; }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sInstance = this;
setContentView(R.layout.activity_main);
StrFen = getString(R.string.perft0);
FrameLayout container = findViewById(R.id.boardContainer);
drawView = new DrawView(this);
container.addView(drawView);
mScrollViewContent = findViewById(R.id.scrollViewContent);
mTextViewContent = findViewById(R.id.textViewContent);
mButtonConnect = findViewById(R.id.buttonConnect);
mButtonConnect.requestFocus();
mButtonSend = findViewById(R.id.buttonSend);
mEditTextCommand = findViewById(R.id.editTextCommand);
mButtonConnect.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) { new ConnectTask().execute(); }
});
mButtonSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String cmd = mEditTextCommand.getText().toString().trim();
if (cmd.isEmpty()) { ShowAlert("Введите команду"); return; }
new SendTask().execute(cmd);
}
});
}
// --- Нотация ---
private String squareToNotation(int numPol) {
int file = numPol % 8;
int rank = numPol / 8;
return "" + (char) ('a' + file) + (char) ('1' + rank);
}
private int notationToSquare(String notation) {
int file = notation.charAt(0) - 'a';
int rank = notation.charAt(1) - '1';
return rank * 8 + file;
}
private int[] squareToCell(int square) {
int file = square % 8;
int rank = square / 8;
return new int[]{file, 7 - rank};
}
private String extractMoveFromLine(String line) {
int movIdx = line.indexOf("mov ");
if (movIdx < 0) return null;
int start = movIdx + 4;
if (start + 4 > line.length()) return null;
return line.substring(start, start + 4);
}
// --- Распознавание фигур ---
// Распознаёт фигуру в bitmap5 (не модифицирует)
private int identifyPiece() {
if (!bitmap5HasContent()) return -1;
int bestMatch = -1;
int bestScore = 0;
for (int i = 0; i < 12; i++) {
int score = compareBitmaps(bitmap5, arrayOfBitmap[i]);
if (score > bestScore) { bestScore = score; bestMatch = i; }
}
int[] refPixels = new int[60 * 60];
int idx = (bestMatch >= 0) ? bestMatch : 0;
arrayOfBitmap[idx].getPixels(refPixels, 0, 60, 0, 0, 60, 60);
int refTotal = 0;
for (int p : refPixels) { if (p != Color.TRANSPARENT) refTotal++; }
if (refTotal > 0 && bestScore >= refTotal * 3 / 4) return bestMatch;
return -1;
}
private int compareBitmaps(Bitmap a, Bitmap b) {
int[] pa = new int[60 * 60];
int[] pb = new int[60 * 60];
a.getPixels(pa, 0, 60, 0, 0, 60, 60);
b.getPixels(pb, 0, 60, 0, 0, 60, 60);
int match = 0;
for (int i = 0; i < pa.length; i++) {
boolean aOp = pa[i] != Color.TRANSPARENT;
boolean bOp = pb[i] != Color.TRANSPARENT;
if (aOp && bOp) { if (pa[i] == pb[i]) match++; }
else if (!aOp && !bOp) { match++; }
}
return match;
}
// Распознаёт фигуру в клетке bitmap3, НЕ трогая bitmap5
private int identifyPieceAtSafe(int cellX, int cellY) {
int srcX = dx + h * cellX;
int srcY = dy + h * cellY;
if (srcX < 0 || srcY < 0 || srcX + 60 > bitmap3.getWidth() || srcY + 60 > bitmap3.getHeight()) return -1;
int[] content = new int[60 * 60];
int[] boardBg = new int[60 * 60];
bitmap3.getPixels(content, 0, 60, srcX, srcY, 60, 60);
bitmap1.getPixels(boardBg, 0, 60, srcX, srcY, 60, 60);
// Убираем фон
for (int i = 0; i < content.length; i++) {
if (content[i] == boardBg[i]) content[i] = Color.TRANSPARENT;
}
int bestMatch = -1;
int bestScore = 0;
for (int i = 0; i < 12; i++) {
int[] ref = new int[60 * 60];
arrayOfBitmap[i].getPixels(ref, 0, 60, 0, 0, 60, 60);
int match = 0;
int refTotal = 0;
for (int j = 0; j < content.length; j++) {
boolean cOp = content[j] != Color.TRANSPARENT;
boolean rOp = ref[j] != Color.TRANSPARENT;
if (cOp && rOp) {
refTotal++;
if (content[j] == ref[j]) match++;
} else if (!cOp && !rOp) {
refTotal++;
match++;
}
}
if (match > bestScore) { bestScore = match; bestMatch = i; }
}
int[] refPixels = new int[60 * 60];
int idx = (bestMatch >= 0) ? bestMatch : 0;
arrayOfBitmap[idx].getPixels(refPixels, 0, 60, 0, 0, 60, 60);
int refTotal = 0;
for (int p : refPixels) { if (p != Color.TRANSPARENT) refTotal++; }
if (refTotal > 0 && bestScore >= refTotal * 3 / 4) return bestMatch;
return -1;
}
// --- Проверка пустоты ---
private boolean isCellEmpty(int cellX, int cellY) {
int srcX = dx + h * cellX;
int srcY = dy + h * cellY;
if (srcX < 0 || srcY < 0 || srcX + 60 > bitmap3.getWidth() || srcY + 60 > bitmap3.getHeight()) return true;
int[] content = new int[60 * 60];
int[] board = new int[60 * 60];
bitmap3.getPixels(content, 0, 60, srcX, srcY, 60, 60);
bitmap1.getPixels(board, 0, 60, srcX, srcY, 60, 60);
for (int i = 0; i < content.length; i++) {
if (content[i] != board[i]) return false;
}
return true;
}
// --- Перемещение ---
private void movePiece(int fromX, int fromY, int toX, int toY) {
extractFigure(fromX, fromY);
clearCell(fromX, fromY);
clearCell(toX, toY);
canvasBmp3.drawBitmap(bitmap5, dx + h * toX, dy + h * toY, null);
canvasBmp5.drawColor(Color.TRANSPARENT);
}
// --- Применение хода движка (чёрные) ---
private void applyMove(String move) {
if (move == null || move.length() != 4) return;
int fromSquare = notationToSquare(move.substring(0, 2));
int toSquare = notationToSquare(move.substring(2, 4));
int fromFile = fromSquare % 8;
int fromRank = fromSquare / 8;
int toFile = toSquare % 8;
int toRank = toSquare / 8;
int[] fromCell = squareToCell(fromSquare);
int[] toCell = squareToCell(toSquare);
// Определяем тип фигуры ДО перемещения
int pieceType = identifyPieceAtSafe(fromCell[0], fromCell[1]);
boolean isCastling = (pieceType == 6
&& Math.abs(toFile - fromFile) == 2 && fromRank == toRank);
boolean isEnPassant = (pieceType == 11
&& fromFile != toFile && fromRank == 3
&& isCellEmpty(toCell[0], toCell[1]));
boolean isPromotion = (pieceType == 11
&& toRank == 0);
if (isCastling) {
movePiece(fromCell[0], fromCell[1], toCell[0], toCell[1]);
int rookFromNum = (toSquare > fromSquare) ? fromSquare + 3 : fromSquare - 4;
int rookToNum = (toSquare > fromSquare) ? fromSquare + 1 : fromSquare - 1;
int[] rfc = squareToCell(rookFromNum);
int[] rtc = squareToCell(rookToNum);
movePiece(rfc[0], rfc[1], rtc[0], rtc[1]);
} else if (isEnPassant) {
extractFigure(fromCell[0], fromCell[1]);
clearCell(fromCell[0], fromCell[1]);
clearCell(toCell[0], fromCell[1]);
canvasBmp3.drawBitmap(bitmap5, dx + h * toCell[0], dy + h * toCell[1], null);
canvasBmp5.drawColor(Color.TRANSPARENT);
} else if (isPromotion) {
clearCell(fromCell[0], fromCell[1]);
clearCell(toCell[0], toCell[1]);
canvasBmp3.drawBitmap(arrayOfBitmap[7], dx + h * toCell[0], dy + h * toCell[1], null);
canvasBmp5.drawColor(Color.TRANSPARENT);
} else {
movePiece(fromCell[0], fromCell[1], toCell[0], toCell[1]);
}
}
// --- Анализ ответа сервера ---
private void checkServerResponse() {
String fullText = mTextViewContent.getText().toString();
if (serverState == STATE_WAITING_CONFIRMATION) {
int hashIdx = fullText.lastIndexOf("#");
int movIdx = fullText.lastIndexOf("mov ");
if (movIdx >= 0) {
int lineEnd = fullText.indexOf('\n', movIdx);
if (lineEnd < 0) lineEnd = fullText.length();
String movLine = fullText.substring(movIdx, lineEnd).trim();
if (movLine.contains("w@")) {
serverState = STATE_WAITING_ENGINE;
mTextViewContent.setText("");
mEditTextCommand.setText("0");
new SendTask().execute("0");
return;
}
}
if (hashIdx >= 0) {
serverState = STATE_IDLE;
mTextViewContent.setText("");
if (bitmapBackup != null) canvasBmp3.drawBitmap(bitmapBackup, 0, 0, null);
if (drawView != null) drawView.invalidate();
ShowAlert("Нелегальный ход!");
return;
}
} else if (serverState == STATE_WAITING_ENGINE) {
int movIdx = fullText.lastIndexOf("mov ");
if (movIdx >= 0) {
int lineEnd = fullText.indexOf('\n', movIdx);
if (lineEnd < 0) lineEnd = fullText.length();
String movLine = fullText.substring(movIdx, lineEnd).trim();
if (movLine.contains("b@")) {
serverState = STATE_IDLE;
mTextViewContent.setText("");
String move = extractMoveFromLine(movLine);
if (move != null) applyMove(move);
if (drawView != null) drawView.invalidate();
return;
}
}
}
}
// --- Сеть ---
private boolean isConnected() {
return mSocket != null && mSocket.isConnected() && !mSocket.isClosed();
}
private class ConnectTask extends AsyncTask<Void, Void, Boolean> {
private String errorMessage = null;
@Override
protected Boolean doInBackground(Void... arg0) {
if (!isConnected()) {
try {
mSocket = new Socket();
mSocket.connect(new InetSocketAddress(mServerAddress, Integer.parseInt(mServerPort)), 5000);
mInputBuffer = mSocket.getInputStream();
mOutputBuffer = mSocket.getOutputStream();
startReaderThread();
return true;
} catch (Exception e) {
errorMessage = e.getClass().getName() + ": " + (e.getMessage() != null ? e.getMessage() : "no message");
return false;
}
} else {
stopReaderThread();
closeSocket();
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
updateConnectionStatus(result);
if (errorMessage != null) ShowAlert(errorMessage);
}
}
private class SendTask extends AsyncTask<String, Void, Boolean> {
private String errorMessage = null;
@Override
protected Boolean doInBackground(String... params) {
if (!isConnected()) { errorMessage = "Нет соединения"; return false; }
try {
mOutputBuffer.write((params[0] + "X").getBytes("UTF-8"));
mOutputBuffer.flush();
return true;
} catch (Exception e) {
errorMessage = e.getClass().getName() + ": " + (e.getMessage() != null ? e.getMessage() : "no message");
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
if (!result && errorMessage != null) ShowAlert(errorMessage);
}
}
private void startReaderThread() {
mIsReading = true;
mReaderThread = new Thread(new Runnable() {
@Override
public void run() {
while (mIsReading && mInputBuffer != null) {
try {
int available = mInputBuffer.available();
if (available > 0) {
byte[] buffer = new byte[available];
int bytesRead = mInputBuffer.read(buffer);
if (bytesRead > 0) {
final String s = new String(buffer, 0, bytesRead, "UTF-8")
.replace("\r", "").replaceAll("\n+", "\n");
runOnUiThread(new Runnable() {
@Override
public void run() {
if (mTextViewContent != null) mTextViewContent.append(s);
if (serverState != STATE_IDLE) checkServerResponse();
}
});
mScrollViewContent.post(new Runnable() {
@Override
public void run() { mScrollViewContent.fullScroll(View.FOCUS_DOWN); }
});
}
} else { Thread.sleep(50); }
} catch (IOException | InterruptedException e) { break; }
}
runOnUiThread(new Runnable() {
@Override
public void run() {
if (serverState != STATE_IDLE) {
serverState = STATE_IDLE;
if (bitmapBackup != null) canvasBmp3.drawBitmap(bitmapBackup, 0, 0, null);
if (drawView != null) drawView.invalidate();
ShowAlert("Соединение потеряно");
}
mButtonConnect.setText(R.string.stringConnect);
}
});
}
});
mReaderThread.start();
}
private void stopReaderThread() {
mIsReading = false;
if (mReaderThread != null) {
mReaderThread.interrupt();
try { mReaderThread.join(1000); } catch (InterruptedException ignored) {}
mReaderThread = null;
}
}
private void closeSocket() {
try { if (mSocket != null && !mSocket.isClosed()) mSocket.close(); } catch (IOException ignored) {}
mSocket = null;
mInputBuffer = null;
mOutputBuffer = null;
}
private void updateConnectionStatus(boolean connected) {
mButtonConnect.setText(connected ? R.string.stringDisconnect : R.string.stringConnect);
}
// --- Шахматы ---
private void prepareBoard() {
canvasBmp3.drawBitmap(bitmap1, 0, 0, null);
parsingStrFen(StrFen, canvasBmp3);
}
private void clearCell(int cellX, int cellY) {
int x = dx + h * cellX;
int y = dy + h * cellY;
if (x < 0 || y < 0 || x + 60 > bitmap3.getWidth() || y + 60 > bitmap3.getHeight()) return;
canvasBmp3.drawBitmap(bitmap1, new Rect(x, y, x + 60, y + 60), new Rect(x, y, x + 60, y + 60), null);
}
private void extractFigure(int cellX, int cellY) {
int srcX = dx + h * cellX;
int srcY = dy + h * cellY;
if (srcX < 0 || srcY < 0 || srcX + 60 > bitmap3.getWidth() || srcY + 60 > bitmap3.getHeight()) return;
canvasBmp5.drawColor(Color.TRANSPARENT);
canvasBmp5.drawBitmap(bitmap3, new Rect(srcX, srcY, srcX + 60, srcY + 60), new Rect(0, 0, 60, 60), null);
Bitmap cellMask = Bitmap.createBitmap(bitmap1, srcX, srcY, 60, 60);
int[] pixels = new int[60 * 60];
bitmap5.getPixels(pixels, 0, 60, 0, 0, 60, 60);
int[] maskPixels = new int[60 * 60];
cellMask.getPixels(maskPixels, 0, 60, 0, 0, 60, 60);
for (int i = 0; i < pixels.length; i++) {
if (pixels[i] == maskPixels[i]) pixels[i] = Color.TRANSPARENT;
}
bitmap5.setPixels(pixels, 0, 60, 0, 0, 60, 60);
}
private boolean bitmap5HasContent() {
int[] pixels = new int[60 * 60];
bitmap5.getPixels(pixels, 0, 60, 0, 0, 60, 60);
for (int p : pixels) { if (p != Color.TRANSPARENT) return true; }
return false;
}
private void initPiece() {
for (int i = 0; i < 6; i++) {
arrayOfBitmap[i] = Bitmap.createBitmap(bitmap2, i * 60, 0, 60, 60);
arrayOfBitmap[i + 6] = Bitmap.createBitmap(bitmap2, i * 60, 60, 60, 60);
}
}
public boolean parsingStrFen(String str, Canvas canvas) {
String s1 = "KQRBNPkqrbnp";
String s2 = "12345678";
int n = 0, k = 0, king = 0;
int r = str.indexOf(' ');
if (r <= 0) return false;
String sub = str.substring(0, r);
for (int i = 0; i < sub.length(); i++) {
String s = sub.substring(i, i + 1);
if (s.equals("/")) {
if (n != 8) ShowAlert("ERROR");
n = 0; k++; continue;
}
if (s2.indexOf(s) >= 0) n += 1 + s2.indexOf(s);
int z = s1.indexOf(s);
if (z >= 0) {
if (z == 0) king++;
if (z == 6) king--;
canvas.drawBitmap(arrayOfBitmap[z], dx + h * n, dy + h * k, paint);
n++;
}
}
if (king != 0) ShowAlert("ERROR");
return true;
}
// --- Диалоги ---
public static void showAlert(Context context, String title, String message, View view) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(title);
if (view == null) builder.setMessage(message);
else builder.setView(view);
builder.setNeutralButton("Ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); }
});
builder.show();
}
public void ShowAlert(String str) {
Toast.makeText(getApplicationContext(), str, Toast.LENGTH_SHORT).show();
}
@Override
public void onBackPressed() {
stopReaderThread();
closeSocket();
mButtonConnect.setText(R.string.stringConnect);
mTextViewContent.setText(R.string.waiting_esp);
mEditTextCommand.setText("");
serverState = STATE_IDLE;
pendingSpecial = SPECIAL_NONE;
pendingMove = "";
pendingFromNum = -1;
hasSelection = false;
selectedX = -1;
selectedY = -1;
ShowAlert("Выход");
super.onBackPressed();
}
@Override
protected void onDestroy() {
stopReaderThread();
closeSocket();
if (bitmapBackup != null) { bitmapBackup.recycle(); bitmapBackup = null; }
if (bitmap1 != null) { bitmap1.recycle(); bitmap1 = null; }
if (bitmap2 != null) { bitmap2.recycle(); bitmap2 = null; }
if (bitmap3 != null) { bitmap3.recycle(); bitmap3 = null; }
if (bitmap4 != null) { bitmap4.recycle(); bitmap4 = null; }
if (bitmap5 != null) { bitmap5.recycle(); bitmap5 = null; }
for (int i = 0; i < 12; i++) {
if (arrayOfBitmap[i] != null) { arrayOfBitmap[i].recycle(); arrayOfBitmap[i] = null; }
}
super.onDestroy();
}