Not logged inCSS-Forum
Forum CSS-Online Help Search Login
CSS-Shop Impressum Datenschutz
Up Topic Hauptforen / CSS-Forum / Schachprogrammmodule mit ChatGPT
- By Lothar Jung Date 2023-02-06 10:38 Edited 2023-02-06 11:37
Hier Module für UCI sowie Bewertungsfunktionen mit Königssicherheit, Bauernstruktur, Türmen, Springern und Läufern:

Die anderen Module für Ein-Ausgabe, Bitboard , A/B Suche, MCTS, Transposition Tabelle, NNUE habe ich im folgenden Link/Thread übermittelt:

https://forum.computerschach.de/cgi-bin/mwf/topic_show.pl?pid=161453#pid161453

Die Kapazität für den Code ist derzeit begrenzt, sodass man die einzelnen Module schrittweise anfragen muss.

ChatGPT erläutert den Algorithmus und die Implementierung.

Das Muster der Anfrage ist wie folgt: „Programmiere ein XFunktion für Schach mit X in C++“

Mit der kostenpflichtigen AI „Copilot“ dürfte man noch tiefer differenzieren können.

Einfache UCI-Schnittstelle:

#include <iostream>
#include <string>
#include <vector>

using namespace std;

vector<string> split(const string &s, char delim) {
    vector<string> tokens;
    string token;
    for (char c : s) {
        if (c == delim) {
            if (!token.empty()) {
                tokens.push_back(token);
            }
            token.clear();
        } else {
            token += c;
        }
    }
    if (!token.empty()) {
        tokens.push_back(token);
    }
    return tokens;
}

void process_command(const string &command) {
    auto tokens = split(command, ' ');
    if (tokens[0] == "uci") {
        cout << "id name Example UCI Chess Engine" << endl;
        cout << "id author John Doe" << endl;
        cout << "uciok" << endl;
    } else if (tokens[0] == "isready") {
        cout << "readyok" << endl;
    } else if (tokens[0] == "position") {
        // Parse the position command and update the internal board representation
        // ...
    } else if (tokens[0] == "go") {
        // Parse the go command and start searching for the best move
        // ...
        cout << "bestmove e2e4" << endl;
    }
}

int main() {
    string command;
    while (true) {
        getline(cin, command);
        if (command == "quit") {
            break;
        }
        process_command(command);
    }
    return 0;
}

Bewertungsfunktion für Königssicherheit:

#include <map>
#include <vector>

const int PAWN_VALUE = 100;
const int KNIGHT_VALUE = 300;
const int BISHOP_VALUE = 300;
const int ROOK_VALUE = 500;
const int QUEEN_VALUE = 900;
const int KING_VALUE = 10000;

const int KING_SAFETY_SCORE = 30;

const map<char, int> piece_values = {
    {'P', PAWN_VALUE},
    {'N', KNIGHT_VALUE},
    {'B', BISHOP_VALUE},
    {'R', ROOK_VALUE},
    {'Q', QUEEN_VALUE},
    {'K', KING_VALUE},
};

const int dx[8] = {1, 1, 1, 0, 0, -1, -1, -1};
const int dy[8] = {1, 0, -1, 1, -1, 1, 0, -1};

bool is_valid(int x, int y) {
    return x >= 0 && x < 8 && y >= 0 && y < 8;
}

int evaluate_king_safety(const vector<string> &board, char color) {
    int score = 0;
    int king_x, king_y;
    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
            char piece = board[j];
            if (piece == 'K' && color == 'w') {
                king_x = i;
                king_y = j;
            }
            if (piece == 'k' && color == 'b') {
                king_x = i;
                king_y = j;
            }
        }
    }
    for (int i = 0; i < 8; i++) {
        int nx = king_x + dx;
        int ny = king_y + dy;
        if (is_valid(nx, ny)) {
            char piece = board[nx][ny];
            if (piece != '.' && (color == 'w' ? islower(piece) : isupper(piece))) {
                score -= KING_SAFETY_SCORE;
            }
        }
    }
    return score;
}

int evaluate_position(const vector<string> &board) {
    int score = 0;
    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
            char piece = board[j];
            if (piece == '.') continue;
            score += (piece < 'a') ? piece_values.at(piece) : -piece_values.at(toupper(piece));
        }
    }
    return score;
}

Hier mit der Bewertung der Bauernstruktur:

#include <iostream>
#include <vector>

const int PAWN_VALUE = 100;
const int KNIGHT_VALUE = 300;
const int BISHOP_VALUE = 300;
const int ROOK_VALUE = 500;
const int QUEEN_VALUE = 900;

// Bewertung der Bauernstruktur
int pawn_structure_evaluation(const std::vector<int> &pawns) {
    int pawn_structure_value = 0;
    for (int pawn : pawns) {
        if (pawn > 5) {
            pawn_structure_value += 20;
        }
        if (pawn < 5) {
            pawn_structure_value -= 20;
        }
    }
    return pawn_structure_value;
}

// Hauptbewertungsfunktion
int evaluate_board(const std::vector<int> &pawns, int knights, int bishops, int rooks, int queens) {
    int material_value = pawns.size() * PAWN_VALUE + knights * KNIGHT_VALUE + bishops * BISHOP_VALUE + rooks * ROOK_VALUE + queens * QUEEN_VALUE;
    int pawn_structure = pawn_structure_evaluation(pawns);
    return material_value + pawn_structure;
}

int main() {
    std::vector<int> pawns = {3, 4, 5, 6};
    int knights = 2;
    int bishops = 2;
    int rooks = 2;
    int queens = 1;
    std::cout << evaluate_board(pawns, knights, bishops, rooks, queens) << std::endl;
    return 0;
}

Bewertungsfunktion mit Türmen:

#include <iostream>

const int WHITE = 1;
const int BLACK = -1;
const int ROOK_VALUE = 5;

int evaluateRookPosition(int rookX, int rookY, int color) {
  int score = 0;
  int rank = (color == WHITE) ? rookY : 7 - rookY;

  // Rook on an open file (a file without any pieces) is more valuable
  if (rookX == 0 || rookX == 7) {
    score += ROOK_VALUE + 2;
  } else if (rookX == 1 || rookX == 6) {
    score += ROOK_VALUE + 1;
  } else {
    score += ROOK_VALUE;
  }

  // Bonus for Rooks on the 7th rank
  if (rank == 7) {
    score += 2;
  }

  return score * color;
}

int main() {
  int rookX = 0;
  int rookY = 7;
  int color = WHITE;

  std::cout << evaluateRookPosition(rookX, rookY, color) << std::endl;
  return 0;
}

Für Springer:

#include <iostream>

const int WHITE = 1;
const int BLACK = -1;
const int KNIGHT_VALUE = 3;

int evaluateKnightPosition(int knightX, int knightY, int color) {
  int score = 0;
  int rank = (color == WHITE) ? knightY : 7 - knightY;

  // Knights in the center of the board are more valuable
  if (knightX >= 2 && knightX <= 5 && knightY >= 2 && knightY <= 5) {
    score += KNIGHT_VALUE + 2;
  } else {
    score += KNIGHT_VALUE;
  }

  // Bonus for Knights on the 3rd and 6th ranks
  if (rank == 3 || rank == 6) {
    score += 1;
  }

  return score * color;
}

int main() {
  int knightX = 2;
  int knightY = 3;
  int color = BLACK;

  std::cout << evaluateKnightPosition(knightX, knightY, color) << std::endl;
  return 0;
}

Für Läufer:

#include <map>

int evaluatePosition(std::map<std:air<int, int>, char> board) {
  int score = 0;

  // Iterate through the board and add up the values of each piece
  for (const auto &square : board) {
    int x = square.first.first;
    int y = square.first.second;
    char piece = square.second;

    switch (piece) {
      case 'P': score += 1; break;
      case 'B': score += 3; break;
      case 'N': score += 3; break;
      case 'R': score += 5; break;
      case 'Q': score += 9; break;
    }

    // Add a bonus for bishops on open or semi-open diagonals
    if (piece == 'B') {
      int diagonal = x + y;
      int anti_diagonal = 7 - x + y;
      bool open = true;
      bool semi_open = false;

      for (int i = 0; i < 8; ++i) {
        if (board[{i, diagonal - i}] != ' ') {
          open = false;
          break;
        }
      }

      if (!open) {
        semi_open = true;
        for (int i = 0; i < 8; ++i) {
          if (board[{i, diagonal - i}] != ' ' && board[{i, diagonal - i}] != 'P') {
            semi_open = false;
            break;
          }
        }
      }

      if (open) {
        score += 2;
      } else if (semi_open) {
        score += 1;
      }

      open = true;
      semi_open = false;

      for (int i = 0; i < 8; ++i) {
        if (board[{i, anti_diagonal - i}] != ' ') {
          open = false;
          break;
        }
      }

      if (!open) {
        semi_open = true;
        for (int i = 0; i < 8; ++i) {
          if (board[{i, anti_diagonal - i}] != ' ' && board[{i, anti_diagonal - i}] != 'P') {
            semi_open = false;
            break;
          }
        }
      }

      if (open) {
        score += 2;
      } else if (semi_open) {
        score += 1;
      }
    }
  }

  return score;
}
- By Lothar Jung Date 2023-02-06 12:46
Die Code Module für den Zugriff auf Tablebases und Eröffnungsbücher:

#include <fstream>
#include <map>
#include <string>
#include <vector>

struct Position {
    std::string state;

    bool operator<(const Position &other) const {
        return state < other.state;
    }
};

typedef std::map<Position, int> Tablebases;

Tablebases load_tablebases(const std::vector<std::string> &filenames) {
    Tablebases tablebases;
    for (const auto &filename : filenames) {
        std::ifstream file(filename);
        std::string line;
        while (std::getline(file, line)) {
            auto space = line.find(' ');
            Position position = {line.substr(0, space)};
            int result = std::stoi(line.substr(space + 1));
            tablebases[position] = result;
        }
    }
    return tablebases;
}

int lookup_result(const Position &position, const Tablebases &tablebases) {
    auto it = tablebases.find(position);
    if (it != tablebases.end())
        return it->second;
    return 0;
}

int main() {
    std::vector<std::string> filenames = {"KPK.tb", "KNNK.tb", "KRKP.tb"};
    Tablebases tablebases = load_tablebases(filenames);
    // ...
    // Hier können Sie das Schachbrett implementieren und den Zugriff auf die Tabellebässe verwenden, um einen bestimmten Zug auszuwählen
    // ...
    int max_depth = 5;
    for (int depth = 0; depth < max_depth; ++depth) {
        // Berechnen Sie alle möglichen Züge und speichern Sie die Zugfolge in einem vector
        // ...
        int best_result = 0;
        Position best_move;
        for (const auto &move : moves) {
            int result = lookup_result(move, tablebases);
            if (result > best_result) {
                best_result = result;
                best_move = move;
            }
        }
        // Führen Sie den besten Zug aus
        // ...
    }
    return 0;
}

#include <fstream>
#include <map>
#include <string>
#include <vector>

struct Move {
    std::string from, to;

    bool operator<(const Move &other) const {
        if (from != other.from)
            return from < other.from;
        return to < other.to;
    }
};

typedef std::map<Move, int> Book;

Book load_book(const std::string &filename) {
    Book book;
    std::ifstream file(filename);
    std::string line;
    while (std::getline(file, line)) {
        auto space = line.find(' ');
        Move move = {line.substr(0, space), line.substr(space + 1, 4)};
        int frequency = std::stoi(line.substr(space + 5));
        book[move] = frequency;
    }
    return book;
}

Move select_move(const std::vector<Move> &moves, const Book &book) {
    Move best_move;
    int best_frequency = 0;
    for (const auto &move : moves) {
        auto it = book.find(move);
        if (it != book.end() && it->second > best_frequency) {
            best_move = move;
            best_frequency = it->second;
        }
    }
    return best_move;
}

int main() {
    Book book = load_book("book.txt");
    // Verwenden Sie select_move(), um die beste Eröffnungsbewegung auszuwählen, basierend auf den momentanen legalen Zügen
    // ...
    return 0;
}
- Date 2023-02-06 18:50
[topic branch moved]
- By Lothar Jung Date 2023-02-06 20:07 Edited 2023-02-06 20:30 Upvotes 1
Brettdarstellung in UniCode mit Ausgabefunktionalität:

#include <iostream>
#include <string>

const std::string board[8][8] = {
    {"♖", "♘", "♗", "♔", "♕", "♗", "♘", "♖"},
    {"♙", "♙", "♙", "♙", "♙", "♙", "♙", "♙"},
    {" ", " ", " ", " ", " ", " ", " ", " "},
    {" ", " ", " ", " ", " ", " ", " ", " "},
    {" ", " ", " ", " ", " ", " ", " ", " "},
    {" ", " ", " ", " ", " ", " ", " ", " "},
    {"♟", "♟", "♟", "♟", "♟", "♟", "♟", "♟"},
    {"♜", "♞", "♝", "♚", "♛", "♝", "♞", "♜"},
};

void print_board() {
  for (int i = 0; i < 8; i++) {
    std::cout << 8 - i << " ";
    for (int j = 0; j < 8; j++) {
      std::cout << board[j] << " ";
    }
    std::cout << std::endl;
  }
  std::cout << "  a b c d e f g h" << std::endl;
}

int main() {
  print_board();
  return 0;
}

#include <bitset>
#include <iostream>
#include <map>

using namespace std;

bitset<64> pawns;
bitset<64> knights;
bitset<64> bishops;
bitset<64> rooks;
bitset<64> queens;
bitset<64> kings;

map<bitset<64>, char> piece_map = {
  {pawns, '\u265F'},
  {knights, '\u265E'},
  {bishops, '\u265D'},
  {rooks, '\u265C'},
  {queens, '\u265B'},
  {kings, '\u265A'}
};

void displayBoard() {
  for (int i = 0; i < 8; i++) {
    for (int j = 0; j < 8; j++) {
      int index = 8 * i + j;
      bool empty = true;
      for (auto& [pieces, piece_char] : piece_map) {
        if (pieces[index]) {
          cout << piece_char << " ";
          empty = false;
          break;
        }
      }
      if (empty) {
        cout << "- ";
      }
    }
    cout << endl;
  }
}

int main() {
  pawns[0] = 1;
  pawns[1] = 1;
  knights[2] = 1;
  bishops[3] = 1;
  rooks[4] = 1;
  queens[5] = 1;
  kings[6] = 1;
  displayBoard();
  return 0;
}
- By Lothar Jung Date 2023-02-06 20:18 Upvotes 1
Alpha Beta Suche mit Move Ordering:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

const int INF = 1e9;

int alphaBetaSearch(int depth, int alpha, int beta, bool maximizingPlayer) {
  // Überprüfen Sie, ob die maximale Tiefe erreicht ist oder das Spiel beendet ist
  if (depth == 0 || isGameOver()) {
    return evaluatePosition();
  }

  vector<int> moves = getPossibleMoves();
  sort(moves.begin(), moves.end(), sortMoves);

  if (maximizingPlayer) {
    int bestValue = -INF;
    for (int move : moves) {
      makeMove(move);
      bestValue = max(bestValue, alphaBetaSearch(depth - 1, alpha, beta, false));
      undoMove(move);
      alpha = max(alpha, bestValue);
      if (beta <= alpha) {
        break;
      }
    }
    return bestValue;
  } else {
    int bestValue = INF;
    for (int move : moves) {
      makeMove(move);
      bestValue = min(bestValue, alphaBetaSearch(depth - 1, alpha, beta, true));
      undoMove(move);
      beta = min(beta, bestValue);
      if (beta <= alpha) {
        break;
      }
    }
    return bestValue;
  }
}

int main() {
  int bestMove = alphaBetaSearch(5, -INF, INF, true);
  makeMove(bestMove);
  return 0;
}
- By Lothar Jung Date 2023-02-08 09:52
Hier ein von mir generiertes Schachprogramm, die fehlenden Codemodule können angefordert und implementiert werden.

Hier ist ein grundlegender Code für ein Schachprogramm in C++, das eine Brettdarstellung in Unicode verwendet, Alpha-Beta-Suche und eine Bewertungsfunktion implementiert:

#include <iostream>
#include <vector>
#include <algorithm>

const int BOARD_SIZE = 8;
const int INF = 0x3f3f3f3f;
const int MAX_PLY = 5;

enum Piece {
    EMPTY,
    PAWN,
    KNIGHT,
    BISHOP,
    ROOK,
    QUEEN,
    KING
};

struct Square {
    int row, col;
    Piece piece;

    Square() : row(-1), col(-1), piece(EMPTY) {}
    Square(int row, int col, Piece piece) : row(row), col(col), piece(piece) {}
};

struct Move {
    Square from, to;
    Piece captured;

    Move() : from(), to(), captured(EMPTY) {}
    Move(Square from, Square to, Piece captured) : from(from), to(to), captured(captured) {}
};

class Board {
private:
    Square board[BOARD_SIZE][BOARD_SIZE];

public:
    Board() {
        for (int row = 0; row < BOARD_SIZE; row++) {
            for (int col = 0; col < BOARD_SIZE; col++) {
                board[row][col] = Square(row, col, EMPTY);
            }
        }
    }

    Piece get_piece(int row, int col) const {
        return board[row][col].piece;
    }

    void set_piece(int row, int col, Piece piece) {
        board[row][col].piece = piece;
    }

    std::vector<Move> get_moves(Piece piece, int row, int col) const {
        std::vector<Move> moves;

        switch (piece) {
        case PAWN:
            // Implement pawn moves here
            break;
        case KNIGHT:
            // Implement knight moves here
            break;
        case BISHOP:
            // Implement bishop moves here
            break;
        case ROOK:
            // Implement rook moves here
            break;
        case QUEEN:
            // Implement queen moves here
            break;
        case KING:
            // Implement king moves here
            break;
        default:
            break;
        }

        return moves;
    }

    int evaluate() const {
        int score = 0;

        // Implement evaluation function here

        return score;
    }

    void print() const {
        std::cout << "  A B C D E F G H" << std::endl;

        for (int row = 0; row < BOARD_SIZE; row++) {
            std::cout << BOARD_SIZE - row << " ";

            for (int col = 0; col < BOARD_SIZE; col++) {
                Piece piece = board[row][col].piece;
                char symbol;

                switch (piece) {
                case PAWN:
                    symbol = '♙';

                    break;
                case KNIGHT:
                    symbol = '♘';
                    break;
                case BISHOP:
                    symbol = '♗';
                    break;
                case ROOK:
                    symbol = '♖';
                    break;
                case QUEEN:
                    symbol = '♕';
                    break;
                case KING:
                    symbol = '♔';
                    break;
                default:
                    symbol = ' ';
                    break;
                }

                std::cout << symbol << " ";
            }

            std::cout << std::endl;
        }

        std::cout << std::endl;
    }
};

int minimax(Board board, int depth, int alpha, int beta, bool maximizingPlayer) {
    if (depth == MAX_PLY) {
        return board.evaluate();
    }

    if (maximizingPlayer) {
        int maxEval = -INF;

        for (int row = 0; row < BOARD_SIZE; row++) {
            for (int col = 0; col < BOARD_SIZE; col++) {
                Piece piece = board.get_piece(row, col);

                if (piece == EMPTY) {
                    continue;
                }

                std::vector<Move> moves = board.get_moves(piece, row, col);

                for (const auto& move : moves) {
                    Board newBoard = board;
                    newBoard.set_piece(move.to.row, move.to.col, piece);
                    newBoard.set_piece(move.from.row, move.from.col, EMPTY);

                    int eval = minimax(newBoard, depth + 1, alpha, beta, false);
                    maxEval = std::max(maxEval, eval);
                    alpha = std::max(alpha, eval);

                    if (beta <= alpha) {
                        break;
                    }
                }
            }
        }

        return maxEval;
    } else {
        int minEval = INF;

        for (int row = 0; row < BOARD_SIZE; row++) {
            for (int col = 0; col < BOARD_SIZE; col++) {
                Piece piece = board.get_piece(row, col);

                if (piece == EMPTY) {
                    continue;
                }

                std::vector<Move> moves = board.get_moves(piece, row, col);

                for (const auto& move : moves) {
                    Board newBoard = board;
                    newBoard.set_piece(move.to.row, move.to.col, piece);
                    newBoard.set_piece(move.from.row, move.from.col, EMPTY);

                    int eval = minimax(newBoard, depth + 1, alpha, beta, true);
                    minEval = std::min(minEval, eval);
                    beta = std::min(beta, eval);

                    if (beta <= alpha) {
                        break;
                    }
                }
            }
        }

        return minEval;
    }
}

int main() {
    Board board;

    // Initialize board with starting position

    while (true)

    {
        board.display();

        int maxEval = -INF;
        Move bestMove;

        for (int row = 0; row < BOARD_SIZE; row++) {
            for (int col = 0; col < BOARD_SIZE; col++) {
                Piece piece = board.get_piece(row, col);

                if (piece == EMPTY) {
                    continue;
                }

                std::vector<Move> moves = board.get_moves(piece, row, col);

                for (const auto& move : moves) {
                    Board newBoard = board;
                    newBoard.set_piece(move.to.row, move.to.col, piece);
                    newBoard.set_piece(move.from.row, move.from.col, EMPTY);

                    int eval = minimax(newBoard, 0, -INF, INF, false);

                    if (eval > maxEval) {
                        maxEval = eval;
                        bestMove = move;
                    }
                }
            }
        }

        board.set_piece(bestMove.to.row, bestMove.to.col, board.get_piece(bestMove.from.row, bestMove.from.col));
        board.set_piece(bestMove.from.row, bestMove.from.col, EMPTY);
    }

    return 0;
}

Dies ist ein einfaches Schachprogramm, das die Alpha-Beta-Suche verwendet, um die beste Zugwahl zu treffen. Die Bewertungsfunktion bewertet das Brett basierend auf der Anzahl der Stücke jeder Farbe auf dem Brett. Dies ist jedoch nur ein sehr einfaches Beispiel, es gibt viele Wege, wie man eine Bewertungsfunktion verbessern und optimieren kann.

Lothar
- By Lothar Jung Date 2023-02-09 10:38 Edited 2023-02-09 10:45
Hier noch einige Einzelheiten zur Generierung von Schachprogrammen:

1. Man kann ein einfaches Schachprogramm schreiben lassen: „Schreibe Schachprogramm mit XFunktion in C++“. XFunktion ist z.B. A/B Suche, Bewertungsfunktion, Bitboards, UCI Schnittstelle etc.

2. Es ist möglich in einem bereits generierten Programm eine Funktion einzufügen „Füge XFunktion ein“.

3. Falls die Generierung stoppt: „Fahre fort“.

4. Den Code in einen Editor abspeichern, damit man später Funktionsteile einpflegen kann.

5. Es ist möglich Variablen zu erhöhen, z.B. Rechentiefe.

6. Ponder On/Off und MultiThreading können auch angefordert werden.

7. Auch die Abfrage von Eröffungsbücher und Tablebases kann generiert werden.

8. Bemerkungen im Programmtext weisen auf fehlende/optionale Funktionen hin.

9. Es gibt auch Hinweise für die Compilierung z.b. mit G++: „Compiliere das Programm“.

10. Die KI generiert und erläutert den Programmtext in Deutsch.

Wie immer und sehr wichtig: Selbst ausprobieren!!!

Lothar
Up Topic Hauptforen / CSS-Forum / Schachprogrammmodule mit ChatGPT

Powered by mwForum 2.29.3 © 1999-2014 Markus Wichitill