В настоящее время я занимаюсь разработкой шахматного движка на С#, и я натолкнулся на кирпичную стену при разработке кода, чтобы определить будущую мобильность любой данной шахматной фигуры в 1, 2 и 3 ходах. Основная идея состоит в том, чтобы вознаграждать части бонусом за повышенную мобильность и наказывать части с меньшей мобильностью.
Шахматная доска представлена в виде массива из 64 квадратов, начиная с 0 (a8) до 63 (h1), например
Piece[] _chessboard = new Piece[64];
Я использую эту позицию шахматной доски в качестве примера:
Black Rooks on squares 3 & 19 (d8 & d6) Black King on square 5 (f8) Black Knight on squares 11 & 12 (d7 & e7) Black Queen on square 16 (a6) Black Pawns on squares 13, 14, 17, 18, 19 (f7, g7, b6 & c6) White Rook on squares 58 & 42 (c1 & c3) White King on square 53 (f2) White Knight on square 40 (a3) White Bishop on square 41 (b3) White Pawns on squares 32, 35, 36, 37, 38 & 31 (a4, d4, e4, f4, g4 & h5)
Вот строка FEN для той же позиции: 3r1k2/3nnpp1/qppr3P/P6P/P2PPPP1/NBR5/5K2/2R5
После нескольких неудачных попыток я придумал следующую структуру данных (Linked List?), которая, я надеюсь, является лучшим способом отслеживания мобильности через квадраты.
+--------+-------------+-----------+-------+ | Square | Predecessor | Successor | Depth | +--------+-------------+-----------+-------+ | 41 | NULL | 34 | 1 | | 34 | 41 | 25 | 2 | | 25 | 34 | 16 | 3 | +--------+-------------+-----------+-------+
Какая эта структура говорит мне, что Белый Епископ на площади 41 идет на квадрат 34 в 1 ход, затем квадрат 25 в 2 ходах и квадрат 16 в 3 ходах. Вышеупомянутая структура заполняется с помощью рекурсивной функции, которая пересекает все возможные квадраты, которые епископ может перемещать в 1, 2 и 3 шага. Проблема заключается в том, что все неэффективные перемещения будут записаны, и их необходимо будет обнаружить и удалить, прежде чем их заменят более эффективными ходами.
Например, перемещение с квадрата 41 на 16 в 3 перемещения по квадратам 34 и 25 неэффективно, поскольку можно перемещаться в квадрат 16 в 2 ходах; 41 - 34 в 1 ход, затем 34 - 16 в 2 ходах. Я требую, чтобы рекурсивная функция обнаруживала эти неэффективные перемещения и удаляла их перед добавлением нового эффективного перехода к структуре данных.
Мне нужна рекурсивная функция для выполнения очень быстро, так как она будет использоваться функцией оценки для поиска наилучшего движения в заданной позиции.
То, что я ищу, - это код, который будет запрашивать (возможно, используя LINQ?) структуру данных выше, чтобы вернуть список неэффективных ходов из вышеуказанной структуры данных, чтобы их можно было удалить, например.
IEnumerable<MoveNode> _moves = new List<MoveNode>();
function void AddMove( int from, int to, int depth )
{
// locate the inefficient moves that need to be deleted
IEnumerable<MoveNode> list_of_moves_to_delete = find_moves( from, to, depth );
if ( list_of_moves_to_delete.Any() )
{
_moves.RemoveAll( list_of_moves_to_delete );
}
// then add the more efficient move
_moves.Add( new MoveNode( from, to, depth ) );
}
function IEnumerable<MoveNode> find_moves( int from, int to, int depth )
{
// TODO: return a list of moves that are inefficient; moves
// that need to be deleted and replaced by efficient
// moves.
}
// Sample calling code (adds the inefficient moves)...
AddMove( 41, 34, 1 );
AddMove( 34, 25, 2 );
AddMove( 25, 16, 3 );
// This one is for the efficient moves...
AddMove( 41, 34, 1 );
AddMove( 34, 16, 2 ); // when this is called it should find the inefficient moves
// and remove them first before adding this move
Это просто образец, и он, вероятно, не будет компилироваться; Я надеюсь, что есть какой-то волшебник, который может помочь мне здесь и закодировать функцию find_moves
, чтобы правильно возвращать неэффективные ходы, поскольку я не уверен, как это сделать.
Надеюсь, мне удалось четко объяснить все здесь.
Спасибо!
** РЕДАКТИРОВАТЬ **
Учитывая, что никто не опубликовал никаких предложений, я попытаюсь немного упростить ситуацию. Я ищу алгоритм, который будет использоваться для обновления структуры данных (аналогично приведенной выше), которая содержит наиболее эффективные перемещения между квадратами на шахматной доске, и это все, что я ищу.
Например:
Скажем, что эти ходы были рекурсивно созданы для Белого епископа на квадрате 41 (b3); в 1 ход он может идти от 41 до 34 (b3-c4), затем в 2 ходах от 34 до 27 (c4-d5) и, наконец, от 27 до 20 (d5-e6) с тремя ходами.
Это означает, что он сделал 3 шага, чтобы получить от квадрата 41 до 20 через 34 и 27, однако, как только рекурсивная функция начнет обрабатывать более эффективные ходы, ей нужно будет искать структуру данных для неэффективных ходов и удалять их.
Было бы здорово, если бы можно было сделать что-то вроде этого:
Replace these entries: +--------+-------------+-----------+-------+ | Square | Predecessor | Successor | Depth | +--------+-------------+-----------+-------+ | 41 | NULL | 34 | 1 | | 34 | 41 | 25 | 2 | | 25 | 34 | 16 | 3 | +--------+-------------+-----------+-------+ With this: +--------+-------------+-----------+-------+ | Square | Predecessor | Successor | Depth | +--------+-------------+-----------+-------+ | 41 | NULL | 34 | 1 | | 34 | 41 | 16 | 2 | +--------+-------------+-----------+-------+ After processing 41-34-16 in 2 moves.
** Редактировать 2 **
После некоторого анализа и разработки возможного решения, я думаю, что, возможно, я взломал его, применив другую структуру данных к приведенной выше.
Вот решение до сих пор - всякая критика приветствуется, чтобы попытаться улучшить эту версию как можно больше.
public class MoveNode
{
public Guid Id;
public int DepthLevel;
public int Node0Ref;
public int Node1Ref;
public int Node2Ref;
public int Node3Ref;
public MoveNode()
{
Id = Guid.NewGuid();
}
// Copy constructor
public MoveNode( MoveNode node )
: this()
{
if ( node != null )
{
this.Node0Ref = node.Node0Ref;
this.Node1Ref = node.Node1Ref;
this.Node2Ref = node.Node2Ref;
this.Node3Ref = node.Node3Ref;
}
}
}
class Program
{
static List<MoveNode> _nodes = new List<MoveNode>();
static IQueryable<MoveNode> getNodes()
{
return _nodes.AsQueryable();
}
static void Main( string[] args )
{
MoveNode parent = null;
// Simulates a recursive pattern for the following moves:
//
// 41 -> 34 (1)
// 34 -> 27 (2)
// 27 -> 20 (3)
// 27 -> 13 (3)
// 34 -> 20 (2)
// 34 -> 13 (2)
// 41 -> 27 (1)
// 27 -> 20 (2)
// 20 -> 13 (3)
// 41 -> 20 (1)
// 20 -> 13 (2)
// 41 -> 13 (1)
//
parent = addMove( null, 41, 34, 1 );
parent = addMove( parent, 34, 27, 2 );
parent = addMove( parent, 27, 20, 3 );
parent = addMove( parent, 27, 13, 3 );
parent = addMove( _nodes[ 0 ], 34, 20, 2 );
parent = addMove( _nodes[ 0 ], 34, 13, 2 );
parent = addMove( null, 41, 27, 1 );
parent = addMove( parent, 27, 20, 2 );
parent = addMove( parent, 20, 13, 3 );
parent = addMove( null, 41, 20, 1 );
parent = addMove( parent, 20, 13, 2 );
parent = addMove( null, 41, 13, 1 );
StringBuilder validMoves = new StringBuilder();
StringBuilder sb = new StringBuilder();
sb.Append( "+--------+---------+---------+---------+---------+\n" );
sb.Append( "| Depth | Node 0 | Node 1 | Node 2 | Node 3 |\n" );
sb.Append( "+--------+---------+---------+---------+---------+\n" );
foreach ( MoveNode node in getNodes() )
{
sb.AppendFormat( "| {0,2} | {1,3} | {2,3} | {3,3} | {4,3} |\n", node.DepthLevel, node.Node0Ref, node.Node1Ref, node.Node2Ref, node.Node3Ref );
if ( node.DepthLevel == 1 )
validMoves.AppendFormat( "{0}\n", convertToBoardPosition( node.Node0Ref, node.Node1Ref ) );
else if ( node.DepthLevel == 2 )
validMoves.AppendFormat( "{0}\n", convertToBoardPosition( node.Node1Ref, node.Node2Ref ) );
else if ( node.DepthLevel == 3 )
validMoves.AppendFormat( "{0}\n", convertToBoardPosition( node.Node2Ref, node.Node3Ref ) );
}
sb.Append( "+--------+---------+---------+---------+---------+\n" );
Console.WriteLine( sb.ToString() );
Console.WriteLine( "List of efficient moves:" );
Console.WriteLine( validMoves.ToString() );
Console.WriteLine( "Press any key to exit." );
Console.ReadKey();
}
static MoveNode addMove( MoveNode parent, int from, int to, int depthLevel )
{
MoveNode node = null;
var inefficientMoves = getNodesToBeRemoved( from, to, depthLevel );
if ( inefficientMoves.Any() )
{
// remove them...
HashSet<Guid> ids = new HashSet<Guid>( inefficientMoves.Select( x => x.Id ) );
_nodes.RemoveAll( x => ids.Contains( x.Id ) );
}
node = new MoveNode( parent );
node.DepthLevel = depthLevel;
if ( depthLevel == 1 )
{
node.Node0Ref = from;
node.Node1Ref = to;
}
else if ( depthLevel == 2 )
{
node.Node1Ref = from;
node.Node2Ref = to;
}
else if ( depthLevel == 3 )
{
node.Node2Ref = from;
node.Node3Ref = to;
}
_nodes.Add( node );
return node;
}
static IEnumerable<MoveNode> getNodesToBeRemoved( int from, int to, int depth )
{
var predicate = PredicateBuilder.True<MoveNode>();
if ( depth == 1 )
predicate = predicate.And( p => p.Node0Ref == from );
else if ( depth == 2 )
predicate = predicate.And( p => p.Node1Ref == from );
else if ( depth == 3 )
predicate = predicate.And( p => p.Node2Ref == from );
predicate = predicate
.And( a => a.Node1Ref == to )
.Or( a => a.Node2Ref == to )
.Or( a => a.Node3Ref == to );
return getNodes().Where( predicate );
}
static string convertToBoardPosition( int from, int to )
{
string a = Convert.ToChar( 97 + file( from ) ) + Convert.ToString( rank( from ) );
string b = Convert.ToChar( 97 + file( to ) ) + Convert.ToString( rank( to ) );
return a + '-' + b;
}
static int file( int x )
{
return ( x & 7 );
}
static int rank( int x )
{
return 8 - ( x >> 3 );
}
}
Я не уверен в правилах об авторском праве, касающихся копирования и вставки кода другого пользователя, поэтому вам нужно загрузить исходный код PredicateBuilder
из здесь, чтобы запустить мой код.
В приведенном выше коде будет показан следующий результат:
+--------+---------+---------+---------+---------+ | Depth | Node 0 | Node 1 | Node 2 | Node 3 | +--------+---------+---------+---------+---------+ | 1 | 41 | 34 | 0 | 0 | | 1 | 41 | 27 | 0 | 0 | | 1 | 41 | 20 | 0 | 0 | | 1 | 41 | 13 | 0 | 0 | +--------+---------+---------+---------+---------+ List of efficient moves: b3-c4 b3-d5 b3-e6 b3-f7 Press any key to exit.