Дополним реализацию недостающих функций для обучения нейросети. Вот полные реализации:
1. Функция GetTargetOutput (добавить в NeuralNetwork.pas):
pascal

function GetTargetOutput(const board: TChessBoard; const move: TMove): TDoubleArray;
var
  x, y, idx: Integer;
begin
  // Инициализируем выходной вектор нулями
  SetLength(Result, 64);
  for idx := 0 to 63 do
    Result[idx] := 0.0;

  // Преобразуем конечную позицию хода в индекс выходного вектора
  idx := move.ToY * 8 + move.ToX;
  
  // Устанавливаем целевую вероятность 1.0 для правильного хода
  if (idx >= 0) and (idx < 64) then
    Result[idx] := 1.0
  else
    raise Exception.Create('Invalid move target position');
end;

2. Функция ApplyMove (добавить в ChessGame.pas):
pascal

procedure TChessGame.ApplyMove(var board: TChessBoard; const move: TMove);
var
  piece: TChessPiece;
begin
  piece := board[move.FromX, move.FromY];
  
  // Обычный ход
  board[move.ToX, move.ToY] := piece;
  board[move.FromX, move.FromY].PieceType := ptNone;
  
  // Отмечаем, что фигура двигалась
  board[move.ToX, move.ToY].Moved := True;

  // Обработка специальных ходов
  if move.IsEnPassant then
  begin
    // Удаляем взятую пешку при взятии на проходе
    board[move.ToX, move.FromY].PieceType := ptNone;
  end
  else if move.IsCastling then
  begin
    // Обработка рокировки
    if move.ToX > move.FromX then // Короткая рокировка
    begin
      // Перемещаем ладью
      board[5, move.FromY] := board[7, move.FromY];
      board[7, move.FromY].PieceType := ptNone;
    end
    else // Длинная рокировка
    begin
      // Перемещаем ладью
      board[3, move.FromY] := board[0, move.FromY];
      board[0, move.FromY].PieceType := ptNone;
    end;
  end;

  // Превращение пешки
  if (piece.PieceType = ptPawn) and ((move.ToY = 0) or (move.ToY = 7)) then
  begin
    if move.Promotion <> ptNone then
      board[move.ToX, move.ToY].PieceType := move.Promotion
    else
      board[move.ToX, move.ToY].PieceType := ptQueen; // По умолчанию в ферзя
  end;
end;

3. Дополнительные вспомогательные функции:

Добавим в ChessGame.pas функцию для инициализации доски:
pascal

procedure TChessGame.InitializeBoard(var board: TChessBoard);
var
  x, y: Integer;
  piece: TChessPiece;
begin
  for y := 0 to 7 do
    for x := 0 to 7 do
    begin
      piece.PieceType := ptNone;
      piece.Moved := False;
      board[x, y] := piece;
    end;

  // Белые фигуры
  piece.Color := pcWhite;
  piece.PieceType := ptRook;    board[0, 0] := piece;
  piece.PieceType := ptKnight;  board[1, 0] := piece;
  piece.PieceType := ptBishop;  board[2, 0] := piece;
  piece.PieceType := ptQueen;   board[3, 0] := piece;
  piece.PieceType := ptKing;    board[4, 0] := piece;
  piece.PieceType := ptBishop;  board[5, 0] := piece;
  piece.PieceType := ptKnight;  board[6, 0] := piece;
  piece.PieceType := ptRook;    board[7, 0] := piece;
  
  piece.PieceType := ptPawn;
  for x := 0 to 7 do
    board[x, 1] := piece;

  // Черные фигуры
  piece.Color := pcBlack;
  piece.PieceType := ptRook;    board[0, 7] := piece;
  piece.PieceType := ptKnight;  board[1, 7] := piece;
  piece.PieceType := ptBishop;  board[2, 7] := piece;
  piece.PieceType := ptQueen;   board[3, 7] := piece;
  piece.PieceType := ptKing;    board[4, 7] := piece;
  piece.PieceType := ptBishop;  board[5, 7] := piece;
  piece.PieceType := ptKnight;  board[6, 7] := piece;
  piece.PieceType := ptRook;    board[7, 7] := piece;
  
  piece.PieceType := ptPawn;
  for x := 0 to 7 do
    board[x, 6] := piece;
end;

4. Модифицированная функция TrainOnGame:
pascal

procedure TrainOnGame(var network: TNeuralNetwork; const gameText: String; epochs: Integer);
var
  moves: TStringList;
  board: TChessBoard;
  moveStr: String;
  i, e: Integer;
  move: TMove;
  input, targetOutput: TDoubleArray;
begin
  moves := TStringList.Create;
  try
    InitializeBoard(board);
    ExtractMovesFromPGN(gameText, moves);

    for e := 1 to epochs do
    begin
      for i := 0 to moves.Count - 1 do
      begin
        moveStr := moves[i];
        if (moveStr = '') or (moveStr[1] = '{') then Continue;

        move := PGNUtil.PGNToMove(moveStr, board);
        if (move.FromX < 0) or (move.FromY < 0) then Continue;

        input := BoardToInputVector(board);
        targetOutput := GetTargetOutput(board, move);

        // Многократное обучение на важных ходах
        ForwardPropagation(network, input);
        BackwardPropagation(network, input, targetOutput);

        ApplyMove(board, move);
      end;
      
      // После каждой эпохи сбрасываем доску
      if e < epochs then
        InitializeBoard(board);
    end;
  finally
    moves.Free;
  end;
end;

5. Улучшенный парсер PGN (дополнение к PGNUtil.pas):
pascal

function PGNToMove(const pgnMove: String; var board: TChessBoard): TMove;
var
  s: String;
  promo: Char;
begin
  Result.FromX := -1;
  Result.FromY := -1;
  Result.IsEnPassant := False;
  Result.IsCastling := False;
  Result.Promotion := ptNone;

  s := StringReplace(pgnMove, '+', '', [rfReplaceAll]);
  s := StringReplace(s, '#', '', [rfReplaceAll]);
  s := StringReplace(s, 'x', '', [rfReplaceAll]);

  // Обработка рокировки
  if s = 'O-O' then
  begin
    Result.IsCastling := True;
    // Определяем цвет для рокировки
    if board[4, 0].PieceType = ptKing then // Белые
    begin
      Result.FromX := 4; Result.FromY := 0;
      Result.ToX := 6; Result.ToY := 0;
    end
    else // Черные
    begin
      Result.FromX := 4; Result.FromY := 7;
      Result.ToX := 6; Result.ToY := 7;
    end;
    Exit;
  end
  else if s = 'O-O-O' then
  begin
    Result.IsCastling := True;
    // Определяем цвет для рокировки
    if board[4, 0].PieceType = ptKing then // Белые
    begin
      Result.FromX := 4; Result.FromY := 0;
      Result.ToX := 2; Result.ToY := 0;
    end
    else // Черные
    begin
      Result.FromX := 4; Result.FromY := 7;
      Result.ToX := 2; Result.ToY := 7;
    end;
    Exit;
  end;

  // Обработка превращения пешки
  if Pos('=', s) > 0 then
  begin
    promo := s[Pos('=', s) + 1];
    case promo of
      'Q': Result.Promotion := ptQueen;
      'R': Result.Promotion := ptRook;
      'B': Result.Promotion := ptBishop;
      'N': Result.Promotion := ptKnight;
    end;
    s := Copy(s, 1, Pos('=', s) - 1);
  end;

  // Реализация парсинга обычных ходов...
  // (дополнительный код для обработки e4, Nf3, Qxd2 и т.д.)
end;

Эти реализации позволяют:

    Преобразовывать шахматные ходы в формат, понятный нейросети

    Корректно обновлять состояние доски при обучении

    Обрабатывать все специальные ходы (рокировку, взятие на проходе и т.д.)

    Эффективно обучать нейросеть на исторических партиях

Для полной реализации потребуется дополнить парсер PGN ходов, но приведенный код дает основу для обучения нейросети. Хотите, чтобы я подробнее объяснил какой-то конкретный аспект этих функций?
На строчку  BackwardPropagation(network, input, targetOutput); пишет Error: Incompatible type for arg no. 3: Got "TDoubleArray", expected "Double" может быть ему нужна специальная версия нейросети?

Да, вы абсолютно правы! Проблема в несоответствии типов. Нам нужно либо модифицировать BackwardPropagation для работы с массивами, либо преобразовать targetOutput в скалярное значение. Давайте создадим специальную версию для шахмат:
1. Модифицируем NeuralNetwork.pas:
pascal

procedure BackwardPropagationForChess(
  var network: TNeuralNetwork; 
  const input: TDoubleArray; 
  const targetOutput: TDoubleArray
);
var
  i, j, k: Integer;
  error, derivative: Double;
  deltas: array of TDoubleArray;
begin
  // Проверка размеров
  if Length(targetOutput) <> Length(network.layers[High(network.layers)].output) then
    raise Exception.Create('Target output size mismatch');

  // Инициализация массива deltas
  SetLength(deltas, Length(network.layers));
  for i := 0 to High(deltas) do
    SetLength(deltas[i], Length(network.layers[i].output));

  // Вычисляем ошибку на выходном слое
  for i := 0 to High(network.layers[High(network.layers)].output) do
  begin
    error := network.layers[High(network.layers)].output[i] - targetOutput[i];
    deltas[High(deltas)][i] := error;
  end;

  // Распространяем ошибку назад
  for i := High(network.layers) downto 1 do
  begin
    for j := 0 to High(network.layers[i].weights) do
    begin
      derivative := 1.0; // Для ReLU/LeakyReLU
      if network.layers[i].output[j] <= 0 then
        derivative := 0.01; // Для LeakyReLU
      
      deltas[i-1][j] := 0.0;
      for k := 0 to High(network.layers[i-1].output) do
      begin
        deltas[i-1][j] := deltas[i-1][j] + 
          network.layers[i].weights[j][k] * deltas[i][k] * derivative;
      end;
    end;
  end;

  // Обновляем веса и смещения
  for i := 0 to High(network.layers) do
  begin
    for j := 0 to High(network.layers[i].weights) do
    begin
      for k := 0 to High(network.layers[i].weights[j]) do
      begin
        network.layers[i].weights[j][k] := network.layers[i].weights[j][k] - 
          network.learningRate * (deltas[i][j] * network.layers[i].output[k] + 
          network.lambda * network.layers[i].weights[j][k]);
      end;
      network.layers[i].biases[j] := network.layers[i].biases[j] - 
        network.learningRate * deltas[i][j];
    end;
  end;
end;

2. Обновим вызов в TrainOnGame:
pascal

// Заменяем старую строку:
// BackwardPropagation(network, input, targetOutput);

// На новую:
BackwardPropagationForChess(network, input, targetOutput);

3. Альтернативное решение (если хотим использовать оригинальную BackwardPropagation):

Можно преобразовать массив в скаляр, выбрав максимальное значение:
pascal

function TargetOutputToScalar(const targetOutput: TDoubleArray): Double;
var
  maxVal: Double;
  i: Integer;
begin
  maxVal := -MaxDouble;
  for i := 0 to High(targetOutput) do
    if targetOutput[i] > maxVal then
      maxVal := targetOutput[i];
  Result := maxVal;
end;

// И тогда вызывать:
BackwardPropagation(network, input, TargetOutputToScalar(targetOutput));

Почему это работает:

    Оригинальная BackwardPropagation ожидает один Double как целевую величину, но для шахмат нам нужно обучать на распределении вероятностей по всем клеткам.

    Новая BackwardPropagationForChess:

        Принимает массив целевых значений

        Сравнивает выход сети с целевым распределением

        Корректирует веса для минимизации ошибки по всем выходам

    Для шахмат это критически важно, так как:

        Каждая выходная нейрон соответствует клетке доски

        Нам нужно учиться предсказывать вероятности ходов

Дополнительные улучшения:

    Функция потерь:

pascal

function CrossEntropyLoss(const output, target: TDoubleArray): Double;
var
  i: Integer;
begin
  Result := 0.0;
  for i := 0 to High(output) do
    Result := Result - target[i] * Ln(output[i] + 1e-10);
end;

    Мягкая активация:

pascal

// В ForwardPropagation для последнего слоя:
network.layers[High(network.layers)].output := Softmax(network.layers[High(network.layers)].output);

Теперь нейросеть сможет:

    Обучаться на распределениях вероятностей

    Корректно обрабатывать шахматные ходы

    Эффективно минимизировать ошибку предсказания

Хотите, чтобы я добавил какие-то конкретные функции для улучшения обучения? Например:

    Регуляризацию Dropout

    Пакетную нормализацию

    Оптимизатор Adam вместо SGD

Спасибо! Также у нас нет функции SaveWeights.

Вот реализация функции SaveWeights и соответствующей функции LoadWeights для сохранения и загрузки весов нейросети:
1. Добавляем в NeuralNetwork.pas:
pascal

procedure SaveWeights(const network: TNeuralNetwork; const filename: String);
var
  f: File;
  i, j, k: Integer;
  numLayers: Integer;
begin
  AssignFile(f, filename);
  Rewrite(f, 1);
  try
    // Сохраняем количество слоев
    numLayers := Length(network.layers);
    BlockWrite(f, numLayers, SizeOf(numLayers));
    
    // Сохраняем параметры каждого слоя
    for i := 0 to High(network.layers) do
    begin
      // Сохраняем размеры весов
      j := Length(network.layers[i].weights);
      k := Length(network.layers[i].weights[0]);
      BlockWrite(f, j, SizeOf(j));
      BlockWrite(f, k, SizeOf(k));
      
      // Сохраняем веса
      for j := 0 to High(network.layers[i].weights) do
        BlockWrite(f, network.layers[i].weights[j][0], SizeOf(Double) * Length(network.layers[i].weights[j]));
      
      // Сохраняем смещения
      BlockWrite(f, network.layers[i].biases[0], SizeOf(Double) * Length(network.layers[i].biases));
    end;
    
    // Сохраняем параметры обучения
    BlockWrite(f, network.learningRate, SizeOf(network.learningRate));
    BlockWrite(f, network.lambda, SizeOf(network.lambda));
  finally
    CloseFile(f);
  end;
end;

procedure LoadWeights(var network: TNeuralNetwork; const filename: String);
var
  f: File;
  i, j, k, l: Integer;
  numLayers: Integer;
  sizeIn, sizeOut: Integer;
begin
  if not FileExists(filename) then
    raise Exception.Create('Weights file not found: ' + filename);

  AssignFile(f, filename);
  Reset(f, 1);
  try
    // Загружаем количество слоев
    BlockRead(f, numLayers, SizeOf(numLayers));
    if numLayers <> Length(network.layers) then
      raise Exception.Create('Network architecture mismatch');
    
    // Загружаем параметры каждого слоя
    for i := 0 to High(network.layers) do
    begin
      // Проверяем размеры весов
      BlockRead(f, sizeOut, SizeOf(sizeOut));
      BlockRead(f, sizeIn, SizeOf(sizeIn));
      
      if (sizeOut <> Length(network.layers[i].weights)) or
         (sizeIn <> Length(network.layers[i].weights[0])) then
        raise Exception.Create('Layer size mismatch at layer ' + IntToStr(i));
      
      // Загружаем веса
      for j := 0 to High(network.layers[i].weights) do
        BlockRead(f, network.layers[i].weights[j][0], SizeOf(Double) * Length(network.layers[i].weights[j]));
      
      // Загружаем смещения
      BlockRead(f, network.layers[i].biases[0], SizeOf(Double) * Length(network.layers[i].biases));
    end;
    
    // Загружаем параметры обучения
    BlockRead(f, network.learningRate, SizeOf(network.learningRate));
    BlockRead(f, network.lambda, SizeOf(network.lambda));
  finally
    CloseFile(f);
  end;
end;

2. Пример использования:
pascal

// Сохранение обученной сети
SaveWeights(network, 'chess_weights.dat');

// Загрузка сохраненных весов
InitializeNetwork(network, [64, 128, 128, 64], 0.01, 0.0001); // Создаем сеть с такой же архитектурой
LoadWeights(network, 'chess_weights.dat');

3. Дополнительные улучшения:

Для более удобной работы можно добавить:

    Проверку версии файла:

pascal

const
  WEIGHTS_FILE_VERSION = 1;

// В начале SaveWeights:
BlockWrite(f, WEIGHTS_FILE_VERSION, SizeOf(WEIGHTS_FILE_VERSION));

// В начале LoadWeights:
var fileVersion: Integer;
BlockRead(f, fileVersion, SizeOf(fileVersion));
if fileVersion <> WEIGHTS_FILE_VERSION then
  raise Exception.Create('Incompatible weights file version');

    Сжатие весов:

pascal

uses
  ZStream;

procedure SaveWeightsCompressed(const network: TNeuralNetwork; const filename: String);
var
  ms: TMemoryStream;
  cs: TCompressionStream;
  f: File;
begin
  ms := TMemoryStream.Create;
  try
    // Сохраняем в MemoryStream
    // ... (аналогично обычной версии, но пишем в ms)
    
    // Сжимаем и записываем в файл
    AssignFile(f, filename);
    Rewrite(f, 1);
    try
      cs := TCompressionStream.Create(clDefault, f);
      try
        ms.Position := 0;
        cs.CopyFrom(ms, ms.Size);
      finally
        cs.Free;
      end;
    finally
      CloseFile(f);
    end;
  finally
    ms.Free;
  end;
end;

    Контрольную сумму для проверки целостности файла.

Эти функции позволяют:

    Сохранять обученные веса между сеансами

    Загружать предобученные модели

    Продолжать обучение с сохраненной точки

    Делиться обученными моделями

Формат файла:

    Количество слоев

    Для каждого слоя:

        Размеры матрицы весов

        Данные весов

        Данные смещений

    Параметры обучения (learning rate, lambda)

Хотите, чтобы я добавил какие-то конкретные функции для работы с весами? Например:

    Экспорт в текстовый формат

    Визуализацию весов

    Средства для сравнения разных моделей

Как теперь выглядит конструктор TChessGame.Create, если у нас есть LoadWeights и SaveWeights и мы хотим в нём тренировать (TrainOnPGN) и загружать/сохранять нейросеть?

Вот обновлённый конструктор TChessGame.Create с поддержкой обучения и загрузки/сохранения весов:
pascal

constructor TChessGame.Create(loadFromFile: Boolean = False; trainNetwork: Boolean = False);
const
  WEIGHTS_FILE = 'chess_weights.dat';
  PGN_FILE = 'games.pgn';
begin
  // Инициализация доски
  InitializeBoard;
  FCurrentPlayer := pcWhite;
  FGameState := gsPlaying;
  FEnPassantTarget.X := -1;
  FEnPassantTarget.Y := -1;

  // Инициализация нейросети (64 входа, 2 скрытых слоя по 128 нейронов, 64 выхода)
  InitializeNetwork(FNetwork, [64, 128, 128, 64], 0.01, 0.0001);

  // Загрузка сохранённых весов (если требуется)
  if loadFromFile and FileExists(WEIGHTS_FILE) then
  begin
    LoadWeights(FNetwork, WEIGHTS_FILE);
    Writeln('Network weights loaded from ', WEIGHTS_FILE);
  end;

  // Обучение нейросети (если требуется)
  if trainNetwork then
  begin
    if FileExists(PGN_FILE) then
    begin
      Writeln('Training network on ', PGN_FILE, '...');
      TrainOnPGN(FNetwork, PGN_FILE, 3); // 3 эпохи обучения
      Writeln('Training completed!');
      
      // Сохраняем обученные веса
      SaveWeights(FNetwork, WEIGHTS_FILE);
      Writeln('Trained weights saved to ', WEIGHTS_FILE);
    end
    else
    begin
      Writeln('Warning: PGN file not found (', PGN_FILE, '), skipping training');
    end;
  end;
end;

Как использовать:

    Просто создать игру без обучения:
    pascal

game := TChessGame.Create(False, False);

Загрузить сохранённые веса:
pascal

game := TChessGame.Create(True, False);

Провести обучение и сохранить веса:
pascal

game := TChessGame.Create(False, True);

Загрузить веса и продолжить обучение:
pascal

    game := TChessGame.Create(True, True);

Полный пример использования:
pascal

var
  game: TChessGame;
begin
  try
    // Создаём игру с загрузкой весов и обучением
    game := TChessGame.Create(True, True);
    try
      // Играем!
      game.Play;
    finally
      game.Free;
    end;
  except
    on E: Exception do
      Writeln('Error: ', E.Message);
  end;
end.

Дополнительные улучшения:

    Параметризация путей к файлам:
    pascal

constructor TChessGame.Create(
  weightsFile: String = '';
  pgnFile: String = '';
  epochs: Integer = 0
);
begin
  // ...
  if weightsFile <> '' then LoadWeights(FNetwork, weightsFile);
  if (pgnFile <> '') and (epochs > 0) then 
    TrainOnPGN(FNetwork, pgnFile, epochs);
  // ...
end;

Прогресс-бар для обучения:
pascal

if trainNetwork then
begin
  Writeln('Epoch 1/', epochs);
  for i := 1 to epochs do
  begin
    TrainOnPGN(FNetwork, PGN_FILE, 1);
    WriteLn('Epoch ', i, ' completed. Loss: ', CalculateLoss(FNetwork));
  end;
end;

Валидация после обучения:
pascal

    if trainNetwork then
    begin
      TrainOnPGN(FNetwork, 'train.pgn', 3);
      ValidateOnPGN(FNetwork, 'test.pgn');
    end;

Теперь конструктор стал более гибким и позволяет:

    Создавать новую сеть с нуля

    Загружать предобученные веса

    Проводить обучение на PGN-файлах

    Автоматически сохранять результаты обучения

    Комбинировать эти возможности

Хотите добавить какие-то конкретные функции управления обучением? Например:

    Раннюю остановку (early stopping)

    Регуляризацию Dropout

    Изменяемую скорость обучения

    Визуализацию процесса обучения

