Отмена поиска в отдельном потоке (шахматный движок)C#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Anonymous
 Отмена поиска в отдельном потоке (шахматный движок)

Сообщение Anonymous »

Сейчас я работаю над своим шахматным движком на C#, Unity. Мой движок выполняет поиск в отдельном потоке, поэтому основной поток Unity работает нормально при расчетах. Чтобы мой движок не вычислял слишком долго, я создал функцию тайм-аута. Эта функция устанавливает флаг «cancellationRequested» в значение true, и мой движок должен остановить все процессы, если флаг равен true.
Однако мой движок, кажется, ломается случайным образом и не останавливает процесс поиска после получения флага отмены. Я не могу понять, как это происходит, потому что не думаю, что есть шанс избежать всех проверок отмены. (Я добавил Debug.Log, чтобы выяснить, где происходит ошибка, но не смог.)
Вот весь код моего движка и сопутствующие материалы.

Код: Выделить всё

// EnginePlayer.cs

using System;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public static class EnginePlayer
{
public static Board board;
public static MoveMaker moveMaker;
static Engine engine;
public static bool isSearching = false;
static bool cancelled = false;
static CancellationTokenSource searchTimer;

public static void Initialize()
{
board = Main.mainBoard;
engine = Main.engine;
}

public static void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Debug.Log(engine.IsSearching() + " | " + engine.cancellationRequested);
}

// Get Engine Moves
if (Graphic.isInMatch &&
((EngineSettings.enableWhiteEngine && board.isWhiteTurn) || (EngineSettings.enableBlackEngine && !board.isWhiteTurn)))
{
if (EngineSettings.useThreading)
{
if (!isSearching)
{
RequestSearch();
}
}
else
{
SingleThreadedSearch();
}
}
}

public static void SearchFinished()
{
Debug.Log("EnginePlayer Search Finished Signal");

if (!cancelled)
{
Debug.Log("Search Not Cancelled - Call Get Best Move");
GetBestMove();
}

isSearching = false;
cancelled = false;

Debug.Log("-----");
}

public static void CancelSearch()
{
cancelled = true;

EndSearch();
}

public static void EndSearch()
{
engine?.TimeOut();
searchTimer?.Cancel();

isSearching = false;
}

static void RequestSearch()
{
Debug.Log("EnginePlayer Request");
// isSearching = true;
// cancelled = false;

// engine.BeforeThreadedSearch();

ThreadingManager.RequestStartSearch();

// Task.Factory.StartNew (() => engine.StartSearch(EngineSettings.searchDepth), TaskCreationOptions.LongRunning);

}

public static void SearchStarted()
{
Debug.Log("EnginePlayer Search Started Signal");

isSearching = true;
searchTimer = new CancellationTokenSource();
Task.Delay(EngineSettings.searchMs, searchTimer.Token).ContinueWith((t) =>  {TimeOutThreadedSearch();});
}

static void GetBestMove()
{
Move move = engine.GetMove();
searchTimer.Cancel();

PlayMove(move);
}

static void SingleThreadedSearch()
{
engine.StartSearch(EngineSettings.searchDepth);
Move move = engine.GetMove();

PlayMove(move);
}

static void PlayMove(Move move)
{
if (move.moveValue != 0)
{
Graphic.grabbedPieceObject = moveMaker.FindPieceObject(move.startSquare);

moveMaker.MakeGraphicalMove(move, true);
}
else
{
Debug.Log("Null Move Returned");
}
}

static void TimeOutThreadedSearch()
{
engine?.TimeOut();
}
}

Код: Выделить всё

// ThreadingManager.cs

using System.Threading.Tasks;
using TMPro;
using UnityEngine;

public static class ThreadingManager
{
static bool searchStarted = false;
static bool searchFinished = false;
static bool startSearchRequested = false;
static bool positionLoadingRequested = false;

static void ThreadedSearch()
{
// SearchStarted();
searchStarted = true;

Main.engine.StartSearch(EngineSettings.searchDepth);

// SearchFinished();
searchFinished = true;
}

public static void RequestStartSearch()
{
Debug.Log("Threading REQ");

if (startSearchRequested)
{
return;
}

startSearchRequested = true;

Debug.Log("Threading Rq St");
Task.Factory.StartNew (() => ThreadedSearch(), TaskCreationOptions.LongRunning);
}

public static void RequestPositionLoading()
{
if (positionLoadingRequested)
{
return;
}

// Not searching
if (!Main.engine.IsSearching())
{
Main.positionLoader.AfterEngineCancelled();
return;
}

positionLoadingRequested = true;

EnginePlayer.CancelSearch();
}

// Called outside of the main thread
// public static void SearchFinished()
// {
//     searchFinished = true;
// }

// public static void SearchStarted()
// {
//     searchStarted = true;
// }

// Main thread
public static void Update()
{
if (searchStarted)
{
EnginePlayer.SearchStarted();
// if (startSearchRequested)
// {

//     startSearchRequested = false;
// }

startSearchRequested = false;
searchStarted = false;
}

if (searchFinished)
{
EnginePlayer.SearchFinished();

if (positionLoadingRequested)
{
Main.positionLoader.AfterEngineCancelled();
positionLoadingRequested = false;
}

searchFinished = false;
}
}
}

Код: Выделить всё

// Engine.cs

using System;
using System.Collections.Generic;
using UnityEngine;

public class Engine
{
Board board;
public TranspositionTable tt;

readonly int ttSize = EngineSettings.ttSize;

public Move bestMove;
bool isSearching;
public bool cancellationRequested;

public Engine()
{
board = Main.mainBoard;
tt = new TranspositionTable(board, ttSize);
isSearching = false;
cancellationRequested = false;
}

public void StartSearch(int maxDepth)
{
Debug.Log("Engine StartSearch");
isSearching = true;
cancellationRequested = false;

bestMove = MoveGen.GenerateMoves(board)[0];

// Return Null Move
if (maxDepth = beta)
{
tt.StoreEvaluation (depth, plyFromRoot, beta, TranspositionTable.LowerBound, move);
return beta;
}

if (eval > alpha)
{
alpha = eval;
evalType = TranspositionTable.Exact;

if (plyFromRoot == 0)
{
bestMove = move;
}
}

tt.StoreEvaluation (depth, plyFromRoot, alpha, evalType, bestMove);

// if (plyFromRoot == 0 && cancellationRequested)
// {
//     break;
// }
}

return alpha;
}

int QuiescenceSearch(int alpha, int beta)
{
// if (cancellationRequested)
// {
//     return alpha;
// }
if (cancellationRequested)
{

Debug.Log("QSEARCH HEAD");
return 0;
}

int ttVal = tt.LookupEvaluation (0, 0, alpha, beta);
if (ttVal != TranspositionTable.lookupFailed)
{
return ttVal;
}

int standPat = Evaluation.Evaluate(board);

if (standPat >= beta)
{
return beta;
}
if (alpha <  standPat)
{
alpha = standPat;
}

List moves = MoveGen.GenerateMoves(board, true);
MoveOrder.GetOrderedList(moves);

foreach (Move move in moves)
{
board.MakeMove(move);

int eval = -QuiescenceSearch(-beta, -alpha);

board.UnmakeMove(move);

if (eval >= beta)
{
return beta;
}
if (eval > alpha)
{
alpha = eval;
}
}

return alpha;
}

public Move GetMove()
{
return bestMove;
}

void EndSearch()
{
isSearching = false;
cancellationRequested = true;

// AfterThreadedSearch();
}

public void TimeOut()
{
Debug.Log("TimeOut");
cancellationRequested = true;
}

public bool IsSearching()
{
return isSearching;
}

void AfterThreadedSearch()
{
// ThreadingManager.SearchFinished();
}
}
Кроме того, я получил скриншот журналов отладки сразу после возникновения ошибки:
Изображение

Как видите, после лога "TimeOut" движок как будто где-то застрял. Он даже не отправляет журналы, такие как «Сигнал завершения поиска EnginePlayer».
Мой вопрос: нет абсолютно никакой возможности, чтобы движок зависал без хотя бы одного подготовленного сообщения журнала. Я не могу найти, где это происходит. Кроме того, эта ошибка возникает случайным образом при сопоставлении движка с движком.
Я пытался выяснить, где мой код пошёл не так, но ничего не смог получить.

Подробнее здесь: https://stackoverflow.com/questions/786 ... ess-engine
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «C#»