STM32 Интерактивная шахматная доска.

alexlaw

✩✩✩✩✩✩✩
3 Янв 2020
110
2
Воронеж
Сформулируйте задачу для ИИ для особых ходов - рокировка, взятие на проходе, превращение только в ферзя.
Суть - автоматически делать нельзя. Т.к. на реальной физической доске это должен сделать пользователь.
Получается как бы два хода (напр. для рокировки) - переставить короля, переставить ладью.
Вот последняя версия touchChess.
Завтра приду с работы задам ИИ задачу))
 

Вложения

alexlaw

✩✩✩✩✩✩✩
3 Янв 2020
110
2
Воронеж
Пообщался с ИИ
Код:
Рассмотрим особые ходы.
Я назвал особыми ходы, которые состоят по сути из двух ходов.
1. Рокировки - нужно переместить короля на две клетки, затем ладью на соответствующее место.
ничего не проверяем, ни наличие короля на начальном поле,ни ладьи
2. Взятие на проходе - сначала пешкой стираем чужую пешку затем ставим на тто место, куда пешка в итоге должна встать.
из проверки только, что наша пешка находится на 6 горизонтали
3. Повышение в ранге - только в ферзя. (тут только один ход)
из проверок - только достижение пешкой 8 горизонтали

На время особыых ходов блокируем другие ходы пользователя, а именно
1.Для рокировки - должны поставить (скопировть) соответвующее поле для ладьи на соответствующее новое поле,
Если поле пустое - нелегальный ход сразу.
2.Длявзятие на проходе - должны сначала свою пешку поставить на чужую пешку(которую сЪедим),\
затем овое место (куда пешка в итоге должна встать)
3.Для Повышения в ранге - делаем автоматически

Только после этого передаем ход на сервер
1.Для рокировки - ход короля на две клетки
2.Для взятия на проходе - ход пешки откуда-куда
3.Для Повышения в ранге - ход пешки откуда-куда
Код:
Задача масштабная, но логика ясная. Добавляю три особых хода с детекцией и обработкой, плюс метод isCellEmpty для проверок.
Код:
Что добавлено
Метод / логика    Назначение
isCellEmpty(cellX, cellY)    Сравнивает клетку на bitmap3 с чистой доской bitmap1. Если совпадает — клетка пуста
movePiece(fromX, fromY, toX, toY)    Универсальный метод: extract → clear → clear → draw. Используется в applyMove для движка
Детекция рокировки    Math.abs(toFile - fromFile) == 2 && fromRank == toRank — горизонтальный ход на 2 клетки
Детекция взятия на проходе    Диагональный ход с 5-й горизонтали (rank 5, 0-indexed: 4) в пустую клетку
Детекция превращения    Целевая клетка на 8-й горизонтали (rank 8, 0-indexed: 7)
Особые ходы пользователя (белые)
Рокировка: двигаем короля → проверяем ладью (isCellEmpty) → если пусто, откат через bitmapBackup и Toast → если есть ладья, копируем её на новое место → отправляем только ход короля (например e1g1).

Взятие на проходе: очищаем нашу клетку → стираем чужую пешку на (XPol, selectedY) → ставим нашу пешку на целевую клетку → отправляем ход пешки (например e5d6).

Превращение: очищаем клетки → рисуем белого ферзя arrayOfBitmap[1] → отправляем ход пешки (например e7e8).

Особые ходы движка (чёрные)
В applyMove те же три типа детектируются автоматически:

Рокировка: fromRank == toRank и Math.abs(toFile - fromFile) == 2 → двигает короля и ладью
Взятие на проходе: fromRank == 3 (4-я горизонталь для чёрных), диагональ в пустую клетку → стирает белую пешку на (toCell[0], fromCell[1])
Превращение: toRank == 0 (1-я горизонталь) → рисует чёрного ферзя arrayOfBitmap[7]
Про горизонталь для взятия на проходе
Я использовал стандартные шахматные правила: белые — с 5-й горизонтали (fromRank == 4), чёрные — с 4-й (fromRank == 3). Ты говорил про 6-ю — если имелась в виду другая нумерация, поменяй константу 4 на нужную в строке isEnPassant.
Код:
Уточняю - для черных делаем автоматически, для белых в ручную должен сделать условных два хода пользователь (я указал в задаче, как именно)
Код:
Понял! Особые ходы для белых — это два ручных тапа (выбор + перемещение) каждый, а не автоматика. Код ждёт второй ход и только потом отправляет на сервер. Превращение — автоматически. Для чёрных (движка) — всё автоматически как было.

Добавил поля pendingSpecial, pendingMove, pendingFromNum и новую ветку логики в onTouchEvent. Вот полный код:

Как работают особые ходы белых
Рокировка (4 тапа):

[LIST=1]
[*]Тап на короля → тап на клетку в 2 клетки от него (e1 → g1).
[*]Код: двигает короля, проверяет ладью. Если ладьи нет — откат + Toast. Если есть — Toast «Сделайте ход ладьёй», pendingSpecial = CASTLING.
[*]Тап на ладью → тап на новое место (h1 → f1).
[*]Код: двигает ладью, отправляет ход короля e1g1 на сервер.
[/LIST]
Взятие на проходе (4 тапа):

[LIST=1]
[*]Тап на свою пешку → тап на чужую пешку (соседняя вертикаль, та же горизонталь, e5 → d5).
[*]Код: стирает чужую, ставит нашу на её место. Toast «Сделайте ход пешкой», pendingSpecial = EN_PASSANT, запоминает исходную клетку.
[*]Тап на свою пешку → тап на целевую клетку (d5 → d6).
[*]Код: двигает пешку, формирует ход e5d6 (исходная → целевая) и отправляет на сервер.
[/LIST]
Превращение (2 тапа, автоматически):

[LIST=1]
[*]Тап на пешку → тап на 8-ю горизонталь.
[*]Код: рисует ферзя, отправляет ход на сервер.
[/LIST]
Про горизонталь взятия на проходе
Я использовал fromRank == 4 (5-я горизонталь, 0-indexed) — стандартные шахматные правила. Ты говорил про «6-ю горизонталь». Если ты считаешь с 1 и твоя 6-я = rank 5 (0-indexed), поменяй fromRank == 4 на fromRank == 5 в строке детекции isEnPassant.
Я:
Ты говорил про «6-ю горизонталь» Я ошибся
Код:
Всё в порядке — код уже использует правильное значение fromRank == 4 (5-я горизонталь в шахматной нумерации), так что менять ничего не нужно.
Можешь тестировать особые ходы. Если что-то отрисуется не так — пиши, поправим.
Код:
Буду проверять
Код:
Отлично! Тестируй спокойно — если заметишь, что:

* фигура «залипает» или рисуется не там;
* особый ход не запускается/не завершается;
* сервер не получает ход или возвращает ошибку;
* откат позиции (bitmapBackup) не срабатывает,

— просто скажи, какой ход делал и что увидел (можно даже скрин логов из TextView). Я быстро подкручу логику.

Удачи с проверкой! Если всё ок — напишу пару идей, как сделать обработку ошибок сервера чуть надёжнее. 😊
Код:
Сразу обнаружил существенный косяк.
Слон на 8 горизонтали превратился в ферзя.
Давай подумаем, что можно сделать
Код:
Проблема ясна: проверка toRank == 7 срабатывает на любую фигуру, дошедшую до 8-й горизонтали, а не только на пешку. Нужно определять, что в bitmap5 именно пешка.

После extractFigure в bitmap5 лежит вырезанная фигура на прозрачном фоне. Сравним её с эталоном белой пешки arrayOfBitmap[5] — с допуском 80%, чтобы антиалиасинг не мешал.

Добавь этот метод (например, рядом с bitmap5HasContent):

Теперь превращение сработает только если в bitmap5 действительно пешка, а не слон. Аналогично взятие на проходе — только для пешки.

Для чёрных (в applyMove) можно оставить как есть: движок присылает только легальные ходы, так что если он шлёт ход на 1-ю горизонталь — это гарантированно превращение пешки.
Код:
Хорошо,дай полный код со своими улучшениями и что ты говорил про — напишу пару идей, как сделать обработку ошибок сервера чуть надёжнее.
Код:
Что добавлено по сравнению с прошлой версией
Добавление    Зачем
isWhitePawnInBitmap5()    Сравнивает bitmap5 с эталоном белой пешки (допуск 80%). Превращение и взятие на проходе срабатывают только для пешки
startServerTimeout() / stopServerTimeout()    Запускает таймер на 10 секунд при отправке хода. Если сервер не ответил — откат позиции, Toast, возврат в IDLE
Обрыв соединения в startReaderThread    Если поток чтения вылетает (socket закрылся), восстанавливаем позицию из бэкапа и показываем Toast «Соединение потеряно»
stopServerTimeout() в onBackPressed / onDestroy    Таймер не сработает после выхода
Идеи по дальнейшему улучшению обработки ошибок
1. Буферизация неполных строк. Сейчас сервер может прислать mov e2e одним пакетом и 4 w@ следующим. Код не найдёт w@ в первом и зависнет. Решение: накапливать текст в отдельной переменной lineBuffer, искать полные строки (заканчивающиеся на \n), и только их анализировать.

2. Различение w@ и b@ по контексту. Сейчас если сервер случайно пришлёт mov ... b@ во время WAITING_CONFIRMATION, код его проигнорирует. Это правильно, но можно добавить Toast «Неожиданный ответ» для отладки.

3. Авто-восстановление позиции. При разрыве соединения сейчас откатывается только последний ход. Если пользователь сделал несколько ходов без ответа (маловероятно, но возможно при рассинхроне), хорошо бы иметь полный снимок доски из FEN и перерисовать.

4. Отдельный поток для отправки. Сейчас SendTask — AsyncTask, но при бырых последовательных отправках (ход + 0) задачи могут встать в очередь. Можно использовать единый поток отправки или synchronized на mOutputBuffer.

Какие из этих идей хочешь внедрить?
Код:
Давай все эти идеи внедрим
Код:
Объединил все четыре улучшения в один код. Основные изменения:

Буферизация строк — lineBuffer накапливает текст, анализ идёт только по завершённым строкам (с \n)
Контекстный разбор — если сервер шлёт b@ во время WAITING_CONFIRMATION или w@ во время WAITING_ENGINE, показываем Toast «Неожиданный ответ»
Авто-восстановление — при разрыве соединения перерисовываем доску из bitmapBackup + сброс pendingSpecial
Единый поток отправки — SendThread вместо AsyncTask, с synchronized на mOutputBuffer

Что изменилось — сводка
1. Буферизация строк (lineBuffer + processIncomingData)

Раньше: сервер присылал mov e2e и 4 w@ двумя пакетами — код не находил w@ в первом и зависал.

Теперь: processIncomingData накапливает текст в lineBuffer, вытаскивает завершённые строки (до \n) и передаёт каждую в analyzeServerLine. Неполный остаток ждёт следующего пакета.

2. Контекстный разбор (analyzeServerLine)

Раньше: checkServerResponse искал lastIndexOf("mov ") во всём накопленном тексте.

Теперь: каждая строка анализируется отдельно. Если во время WAITING_CONFIRMATION приходит b@ — Toast «Неожиданный ответ сервера (b@)». Если во время WAITING_ENGINE приходит w@ — аналогично. Плюс # обрабатывается в обоих состояниях.

3. Авто-восстановление (restorePosition)

Раньше: при разрыве соединения откатывался только bitmap3, но pendingSpecial, hasSelection и selectedX/Y оставались в подвешенном состоянии.

Теперь: restorePosition() сбрасывает всё — bitmap3 из бэкапа, bitmap5 прозрачный, hasSelection = false, pendingSpecial = SPECIAL_NONE. Вызывается из:

таймаута
обрыва соединения
отклонения хода (#)
onBackPressed
4. Единый поток отправки (SendThread + sendToServer)

Раньше: SendTask (AsyncTask) — при быстрых последовательных отправках (ход + 0) задачи могли встать в очередь или наложиться.

Теперь: sendToServer(cmd) кладёт команду в mSendQueue и будит mSenderThread. Поток извлекает команды по очереди и пишет в mOutputBuffer под synchronized. Гарантируется порядок отправки и отсутствие наложений. mButtonSend тоже использует sendToServer напрямую.
Код:
На сегодня хватит, откатимся к версии после слов - Полный код с проверкой пешки: А то в последней много косяков, точнее моментов требющих проверки
Код:
Понял, откатываемся к версии с проверкой пешки — без буферизации строк, без единого потока отправки и без авто-восстановления. Вот тот самый код:
 
Изменено:

alexlaw

✩✩✩✩✩✩✩
3 Янв 2020
110
2
Воронеж
Неплохо))
Теперь
Код:
неплохо распознает, теперь подвигаем фигурами как и раньше, хочу посмотреть как будет работать распознавание после перемещений, никаких серверов и ограничений на ходы - просто канвасы
Код:
Отлично, давай сделаем так: убираем сервер, убираем проверки ходов — оставляем только канвасы, тапы и распознавание. А главное — добавляем возможность брать фигуру и ставить её на другую клетку, чтобы ты мог вручную двигать фигуры и проверять, как identifyPiece() отрабатывает после перемещений.

Что добавлено
Перетаскивание: тап по фигуре — она «поднимается» (рисуется отдельно в bitmap5), второй тап — ставится на новую клетку.
Позиция хранится в int[8][8]: 0 — пусто, 1–12 — фигуры. Это нужно, чтобы корректно стирать старую фигуру и рисовать новую.
Без ограничений на ходы: куда ткнул — туда и ставится.
Распознавание работает как раньше: по вырезанному фрагменту доски.
Никаких лямбд и AndroidX: чистый старый Java для надёжной сборки в Java N‑IDE.
Код:
Как это работает для твоей задачи
Тап по фигуре — она исчезает с доски (визуально), появляется «в руке» (в углу, потому что рисуется bitmap5 в 0,0).
Второй тап — фигура ставится на новую клетку, доска перерисовывается, запускается extractFigure + identifyPiece, и ты получаешь окно с результатом распознавания.
Можно двигать сколько угодно, проверять, не «плывёт» ли распознавание после перемещений, смещений, наложений теней и т.п.
Никаких серверов, никаких проверок ходов — только канвасы и логика перетаскивания.
Важные моменты под твой стек
Java N‑IDE: нет лямбд, нет AndroidX — должно собраться без ошибок.
Память: bitmap5 переиспользуется, board[][] хранит состояние — это самый простой и надёжный вариант для отладки.
Проверка распознавания: после каждого хода ты сразу видишь окно с результатом — удобно для тестов.
Если хочешь, могу добавить:

Рисование фигуры «в руке» прямо под пальцем (вместо 0,0), чтобы было похоже на Drag&Drop.
Вывод результата не в окне, а в TextView на экране, чтобы не закрывать окно каждый раз.
Логирование в Logcat для анализа порогов и баллов сравнения.
Что сейчас важнее: чтобы было максимально похоже на реальное перетаскивание или чтобы было проще для быстрой проверки распознавания?
 

Вложения

alexlaw

✩✩✩✩✩✩✩
3 Янв 2020
110
2
Воронеж
Пришли светодиоды)))
Надо переключаться на другие задачи.
Может кто продолжит Андроид проект?
Доведет до ума?
Надеюсь понятны идея и смысл чего я хотел добиться?