/
expertdaniil
/
Chess
Обзор
Документация
Войти
/
expertdaniil
/
Chess
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Chess_Visualizer.cpp
405 строк
11 KB
expertdaniil
upload files
22 янв 2026, 15:44
Верифицирован
22 янв 2026, 15:44
a081d0c
Код
Авторство
О чём код?
#include "Chess_Visualizer.h" #include <algorithm> #include <iostream> #include "Chess_MoveGen.h" #include "Chess_Apply.h" #include "Chess_Search.h" #include "Chess_Attack.h" static constexpr int TILE = 80; static constexpr int MARGIN = 20; static constexpr int BOARD_PX = TILE * 8; static constexpr int TOPBAR = 50; static constexpr int PANEL_W = 260; static sf::String Utf8ToSfString(const std::string& s) { return sf::String::fromUtf8(s.begin(), s.end()); } static sf::String PieceToSfString(const Piece& p, const PieceDefRegistry& reg) { if (p.isEmpty()) return sf::String(); const PieceDef* def = reg.find(p.kind); if (!def) return sf::String(); return Utf8ToSfString(p.side == Side::White ? def->symbolW : def->symbolB); } ChessVisualizer::ChessVisualizer() : window(sf::VideoMode(BOARD_PX + 2*MARGIN + PANEL_W, BOARD_PX + 2*MARGIN + TOPBAR), "Chess Lab (simple engine + SFML)") { reg = CreateStandardPieceDefs(); rules.enforceKingSafety = true; rules.allowCastling = true; rules.allowEnPassant = true; rules.allowPromotion = true; if (!font.loadFromFile("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf")) { font.loadFromFile("DejaVuSans.ttf"); } setupButtons(); refreshLegalMovesAndStatus(); doAIMoveIfNeeded(); } void ChessVisualizer::setupButtons() { float x = float(MARGIN); float y = float(MARGIN); float h = float(TOPBAR - 10); float w = 80.f; btnReset = sf::FloatRect(x, y + 5.f, w, h); x += w + 10.f; btnMode = sf::FloatRect(x, y + 5.f, w, h); x += w + 10.f; btnSwap = sf::FloatRect(x, y + 5.f, w, h); } int ChessVisualizer::pixelToSquare(int mx, int my) const { int x = mx - MARGIN; int y = my - (MARGIN + TOPBAR); if (x < 0 || y < 0 || x >= BOARD_PX || y >= BOARD_PX) return -1; int file = x / TILE; int screenRow = y / TILE; int r = 7 - screenRow; int c = file; return ChessState::pack(r, c); } sf::Vector2f ChessVisualizer::squareToPixelTopLeft(int sq) const { int r = ChessState::row(sq); int c = ChessState::col(sq); int screenRow = 7 - r; float x = float(MARGIN + c*TILE); float y = float(MARGIN + TOPBAR + screenRow*TILE); return {x,y}; } std::string ChessVisualizer::squareToAlg(int sq) { int r = ChessState::row(sq); int c = ChessState::col(sq); char file = char('a' + c); char rank = char('1' + r); std::string s; s += file; s += rank; return s; } std::string ChessVisualizer::moveToText(const Move& mv) { std::string s = squareToAlg(mv.from) + squareToAlg(mv.to); if (mv.isPromotion) { char p = 'Q'; if (mv.promoKind == PieceKind::Rook) p = 'R'; else if (mv.promoKind == PieceKind::Bishop) p = 'B'; else if (mv.promoKind == PieceKind::Knight) p = 'N'; s += '='; s += p; } return s; } void ChessVisualizer::pushMoveToHistory(const Move& mv) { plyHistory.push_back(moveToText(mv)); } void ChessVisualizer::refreshLegalMovesAndStatus() { if (gameOver) { legalMoves.clear(); return; } legalMoves = ChessMoveGen::GenerateLegalMoves(st, reg, rules); if (legalMoves.empty()) { gameOver = true; if (IsKingInCheck(st, reg, st.sideToMove())) { if (mode == PlayMode::HumanVsHuman) status = "Checkmate"; else status = (st.sideToMove() == humanSide) ? "You lose (checkmate)" : "You win (checkmate)"; } else { status = "Stalemate"; } return; } if (mode == PlayMode::HumanVsHuman) { status = (st.sideToMove() == Side::White) ? "White to move (HvsH)" : "Black to move (HvsH)"; } else { status = (st.sideToMove() == humanSide) ? "Your move (HvsAI)" : "AI move (HvsAI)"; } } bool ChessVisualizer::tryApplyMoveFromTo(int from, int to) { if (gameOver) return false; auto it = std::find_if(legalMoves.begin(), legalMoves.end(), [&](const Move& m){ return m.from == from && m.to == to; }); if (it == legalMoves.end()) return false; Move mv = *it; if (mv.isPromotion && mv.promoKind == PieceKind::None) mv.promoKind = PieceKind::Queen; (void)ChessApplier::ApplyMove(st, mv, rules); pushMoveToHistory(mv); selectedSq = -1; refreshLegalMovesAndStatus(); return true; } void ChessVisualizer::doAIMoveIfNeeded() { if (gameOver) return; if (mode != PlayMode::HumanVsAI) return; if (st.sideToMove() != aiSide) return; Move best = ChessSearch::FindBestMove(st, reg, rules, 3); if (!best.isValid()) { refreshLegalMovesAndStatus(); return; } if (best.isPromotion && best.promoKind == PieceKind::None) best.promoKind = PieceKind::Queen; (void)ChessApplier::ApplyMove(st, best, rules); pushMoveToHistory(best); refreshLegalMovesAndStatus(); } void ChessVisualizer::handleTopbarClick(int mx, int my) { float x = float(mx); float y = float(my); if (btnReset.contains(x, y)) { st.resetToInitialPosition(); selectedSq = -1; gameOver = false; plyHistory.clear(); refreshLegalMovesAndStatus(); doAIMoveIfNeeded(); return; } if (btnMode.contains(x, y)) { mode = (mode == PlayMode::HumanVsAI) ? PlayMode::HumanVsHuman : PlayMode::HumanVsAI; selectedSq = -1; gameOver = false; refreshLegalMovesAndStatus(); doAIMoveIfNeeded(); return; } if (btnSwap.contains(x, y)) { std::swap(humanSide, aiSide); selectedSq = -1; gameOver = false; refreshLegalMovesAndStatus(); doAIMoveIfNeeded(); return; } } void ChessVisualizer::handleClick(int mx, int my) { if (my <= (MARGIN + TOPBAR)) { handleTopbarClick(mx, my); return; } if (gameOver) return; int sq = pixelToSquare(mx, my); if (sq < 0) return; Side whoCanMove = st.sideToMove(); if (mode == PlayMode::HumanVsAI && whoCanMove != humanSide) return; if (selectedSq < 0) { const Piece& p = st.pieceAt(sq); if (!p.isEmpty() && p.side == whoCanMove) selectedSq = sq; return; } if (!tryApplyMoveFromTo(selectedSq, sq)) { const Piece& p = st.pieceAt(sq); if (!p.isEmpty() && p.side == whoCanMove) selectedSq = sq; else selectedSq = -1; } else { doAIMoveIfNeeded(); } } void ChessVisualizer::handleEvent(const sf::Event& ev) { if (ev.type == sf::Event::Closed) window.close(); if (ev.type == sf::Event::MouseButtonPressed && ev.mouseButton.button == sf::Mouse::Left) { handleClick(ev.mouseButton.x, ev.mouseButton.y); } if (ev.type == sf::Event::KeyPressed) { if (ev.key.code == sf::Keyboard::R) { st.resetToInitialPosition(); selectedSq = -1; gameOver = false; plyHistory.clear(); refreshLegalMovesAndStatus(); doAIMoveIfNeeded(); } if (ev.key.code == sf::Keyboard::M) { mode = (mode == PlayMode::HumanVsAI) ? PlayMode::HumanVsHuman : PlayMode::HumanVsAI; selectedSq = -1; gameOver = false; refreshLegalMovesAndStatus(); doAIMoveIfNeeded(); } if (ev.key.code == sf::Keyboard::S) { std::swap(humanSide, aiSide); selectedSq = -1; gameOver = false; refreshLegalMovesAndStatus(); doAIMoveIfNeeded(); } } } void ChessVisualizer::drawButton(const sf::FloatRect& r, const std::string& label) { sf::RectangleShape rect(sf::Vector2f(r.width, r.height)); rect.setPosition(r.left, r.top); rect.setFillColor(sf::Color(70,70,70)); rect.setOutlineThickness(1.f); rect.setOutlineColor(sf::Color(120,120,120)); window.draw(rect); sf::Text t; t.setFont(font); t.setCharacterSize(16); t.setFillColor(sf::Color::White); t.setString(label); t.setPosition(r.left + 12.f, r.top + 10.f); window.draw(t); } void ChessVisualizer::drawBoard() { sf::RectangleShape cell(sf::Vector2f{float(TILE), float(TILE)}); for (int r=0;r<8;++r) for (int c=0;c<8;++c) { int sq = ChessState::pack(r,c); auto pos = squareToPixelTopLeft(sq); cell.setPosition(pos); bool dark = ((r+c)%2)==1; cell.setFillColor(dark ? sf::Color(118,150,86) : sf::Color(238,238,210)); window.draw(cell); } } void ChessVisualizer::drawPieces() { sf::Text t; t.setFont(font); t.setCharacterSize(52); t.setFillColor(sf::Color::Black); for (int sq=0;sq<64;++sq) { const Piece& p = st.pieceAt(sq); if (p.isEmpty()) continue; auto pos = squareToPixelTopLeft(sq); t.setPosition(pos.x + 22.f, pos.y + 10.f); t.setString(PieceToSfString(p, reg)); window.draw(t); } } void ChessVisualizer::drawOverlay() { drawButton(btnReset, "Reset"); drawButton(btnMode, "Mode"); drawButton(btnSwap, "Swap"); if (selectedSq >= 0) { sf::RectangleShape sel(sf::Vector2f{float(TILE), float(TILE)}); sel.setPosition(squareToPixelTopLeft(selectedSq)); sel.setFillColor(sf::Color(255,255,0,80)); window.draw(sel); sf::CircleShape dot(10.f); dot.setFillColor(sf::Color(0,0,0,90)); for (const auto& mv : legalMoves) { if (mv.from != selectedSq) continue; auto p = squareToPixelTopLeft(mv.to); dot.setPosition(p.x + TILE/2.f - 10.f, p.y + TILE/2.f - 10.f); window.draw(dot); } } sf::Text s; s.setFont(font); s.setCharacterSize(16); s.setFillColor(sf::Color::White); s.setPosition(float(MARGIN) + 270.f, float(MARGIN) + 10.f); s.setString(status); window.draw(s); } void ChessVisualizer::drawRightPanel() { sf::RectangleShape panel(sf::Vector2f(float(PANEL_W - 10), float(BOARD_PX))); panel.setPosition(float(MARGIN + BOARD_PX + 10), float(MARGIN + TOPBAR)); panel.setFillColor(sf::Color(45,45,45)); panel.setOutlineThickness(1.f); panel.setOutlineColor(sf::Color(90,90,90)); window.draw(panel); sf::Text t; t.setFont(font); t.setCharacterSize(16); t.setFillColor(sf::Color::White); float x = float(MARGIN + BOARD_PX + 20); float y = float(MARGIN + TOPBAR + 10); t.setPosition(x, y); t.setString("Moves:"); window.draw(t); y += 24; int totalPly = (int)plyHistory.size(); int totalFull = (totalPly + 1) / 2; int maxLines = 14; int startFull = std::max(1, totalFull - (maxLines - 1)); for (int full = startFull; full <= totalFull; ++full) { int i = (full - 1) * 2; std::string line = std::to_string(full) + ". "; if (i < totalPly) line += plyHistory[i]; if (i + 1 < totalPly) { line += " "; line += plyHistory[i + 1]; } t.setPosition(x, y); t.setString(line); window.draw(t); y += 20; if (y > float(MARGIN + TOPBAR + BOARD_PX - 20)) break; } } void ChessVisualizer::draw() { window.clear(sf::Color(30,30,30)); drawBoard(); drawPieces(); drawOverlay(); drawRightPanel(); window.display(); } void ChessVisualizer::Run() { while (window.isOpen()) { sf::Event ev; while (window.pollEvent(ev)) handleEvent(ev); draw(); } }