Кратчайший путь рыцаря на шахматной доске - PullRequest
87 голосов
/ 26 февраля 2010

Я готовился к предстоящему соревнованию по программированию, и я наткнулся на вопрос, который меня просто смутил. Тем не менее, я чувствую, что это концепция, которую я должен изучить сейчас, а не скрещивать пальцы, что она никогда не подходит.

В основном это касается фигуры рыцаря на шахматной доске. Вам дают два входа: начальное местоположение и конечное местоположение. Цель состоит в том, чтобы затем рассчитать и распечатать кратчайший путь, по которому рыцарь может добраться до цели.

Я никогда не имел дело с вещами типа кратчайшего пути, и я даже не знаю, с чего начать. Какую логику я использую для решения этой проблемы?

P.S. Если это имеет какое-либо отношение, они хотят, чтобы вы дополнили обычные движения рыцаря, также позволив ему перейти к четырем углам квадрата, образованного (потенциально) восемью ходами, которые может сделать рыцарь, учитывая, что центр квадрата является расположение рыцаря.

Ответы [ 16 ]

47 голосов
/ 08 января 2012

РЕДАКТИРОВАТЬ: См. Ответ Саймона , где он исправил формулу, представленную здесь.

На самом деле есть формула O (1)

Это изображение, которое я сделал, чтобы визуализировать его (квадраты, которые может достичь конь при ходе N th , окрашены в тот же цвет). Knight's Move

Можете ли вы заметить образец здесь?

Несмотря на то, что мы можем видеть шаблон, действительно трудно найти функцию f( x , y ), которая возвращает количество ходов, необходимое для перехода от квадрата ( 0 , 0 ) к квадрату ( x , y )

Но вот формула, которая работает, когда 0 <= y <= x

int f( int x , int y )
{
    int delta = x - y;

    if( y > delta )
        return 2 * ( ( y - delta ) / 3 ) + delta;
    else
        return delta - 2 * ( ( delta - y ) / 4 );
}

Примечание. Этот вопрос был задан SACO 2007 Day 1
И решения здесь

40 голосов
/ 13 марта 2016

Вот правильное решение O (1), но для случая, когда конь движется только как шахматный конь, и на бесконечной шахматной доске:

https://jsfiddle.net/graemian/5qgvr1ba/11/

Ключом к обнаружению этого является замечание паттернов, которые появляются, когда вы рисуете доску. На диаграмме ниже число в квадрате - это минимальное количество ходов, необходимое для достижения этого квадрата (вы можете использовать поиск в ширину, чтобы найти это):

Patterns

Поскольку решение симметрично по осям и диагоналям, я нарисовал только случай x> = 0 и y> = x.

Нижний левый блок - это начальная позиция, а цифры в блоках представляют минимальное количество ходов для достижения этих блоков.

Обратите внимание на 3 модели:

  • Приращения синих вертикальных групп по 4
  • «Основные» красные диагонали (они идут сверху вниз, справа налево, как обратный слеш)
  • «вторичные» зеленые диагонали (той же ориентации, что и красные)

(Убедитесь, что вы видите оба набора диагоналей сверху-слева-снизу-справа. Они имеют постоянное число ходов. Слева внизу справа и справа диагонали намного сложнее.)

Вы можете получить формулы для каждого. Желтые блоки - это особые случаи. Таким образом, решение становится:

function getMoveCountO1(x, y) {

    var newXY = simplifyBySymmetry(x, y);

    x = newXY.x;
    y = newXY.y;

    var specialMoveCount = getSpecialCaseMoveCount(x ,y);

    if (specialMoveCount !== undefined)
        return specialMoveCount;

    else if (isVerticalCase(x, y))
        return getVerticalCaseMoveCount(x ,y);

    else if (isPrimaryDiagonalCase(x, y))
        return getPrimaryDiagonalCaseMoveCount(x ,y);

    else if (isSecondaryDiagonalCase(x, y))
        return getSecondaryDiagonalCaseMoveCount(x ,y);

}

с самыми сложными из вертикальных групп:

function isVerticalCase(x, y) {

    return y >= 2 * x;

}

function getVerticalCaseMoveCount(x, y) {

    var normalizedHeight = getNormalizedHeightForVerticalGroupCase(x, y);

    var groupIndex = Math.floor( normalizedHeight / 4);

    var groupStartMoveCount = groupIndex * 2 + x;

    return groupStartMoveCount + getIndexInVerticalGroup(x, y);

}

function getIndexInVerticalGroup(x, y) {

    return getNormalizedHeightForVerticalGroupCase(x, y) % 4;

}

function getYOffsetForVerticalGroupCase(x) {

    return x * 2;

}

function getNormalizedHeightForVerticalGroupCase(x, y) {

    return y - getYOffsetForVerticalGroupCase(x);

}

См. Скрипку для других случаев.

Может быть, есть более простые или более элегантные шаблоны, которые я пропустил? Если это так, я хотел бы увидеть их. В частности, я заметил некоторые диагональные узоры в синих вертикальных корпусах, но я не исследовал их. Несмотря на это, это решение все еще удовлетворяет ограничению O (1).

26 голосов
/ 26 февраля 2010

У вас есть график, где все доступные ходы связаны (значение = 1), а недоступные ходы отключены (значение = 0), разреженная матрица будет выглядеть так:

(a1,b3)=1,
(a1,c2)=1,
  .....

А кратчайший путь из двух точек на графике можно найти с помощью http://en.wikipedia.org/wiki/Dijkstra's_algorithm

Псевдокод с википедии-страницы:

function Dijkstra(Graph, source):
   for each vertex v in Graph:           // Initializations
       dist[v] := infinity               // Unknown distance function from source to v
       previous[v] := undefined          // Previous node in optimal path from source
   dist[source] := 0                     // Distance from source to source
   Q := the set of all nodes in Graph
   // All nodes in the graph are unoptimized - thus are in Q
   while Q is not empty:                 // The main loop
       u := vertex in Q with smallest dist[]
       if dist[u] = infinity:
          break                         // all remaining vertices are inaccessible from source
       remove u from Q
       for each neighbor v of u:         // where v has not yet been removed from Q.
           alt := dist[u] + dist_between(u, v) 
           if alt < dist[v]:             // Relax (u,v,a)
               dist[v] := alt
               previous[v] := u
   return dist[]

EDIT:

  1. как придурок, сказал, используя http://en.wikipedia.org/wiki/A*_algorithm может быть быстрее.
  2. самый быстрый способ, это предварительно рассчитать все расстояния и сохраните его в полной матрице 8x8. ну, я бы назвал это обманом, и работает только потому, что проблема маленький. Но иногда соревнования проверим, насколько быстро ваша программа пробеги.
  3. Суть в том, что если вы готовите для соревнования по программированию вы должны знать общие алгоритмы, в том числе Дейкстры. Хорошей отправной точкой является чтение Introduction to Algorithms ISBN 0-262-03384-4. Или вы можете попробовать википедию, http://en.wikipedia.org/wiki/List_of_algorithms
17 голосов
/ 17 января 2017

Очень интересная проблема, с которой я недавно столкнулся. После поиска некоторых решений я попытался восстановить аналитическую формулу (O(1) time and space complexity), приведенную для SACO 2007 Day 1 растворы .

Прежде всего я хочу поблагодарить Грэма Пайла за очень приятную визуализацию, которая помогла мне исправить формулу.

По какой-то причине (возможно, для упрощения, красоты или просто ошибки) они переместили знак minus в оператор floor, в результате чего они получили неправильную формулу floor(-a) != -floor(a) for any a.

Вот правильная аналитическая формула:

var delta = x-y;
if (y > delta) {
    return delta - 2*Math.floor((delta-y)/3);
} else {
    return delta - 2*Math.floor((delta-y)/4);
}

Формула работает для всех пар (x, y) (после применения осей и диагональной симметрии), за исключением (1,0) и (2,2) угловых случаев, которые не соответствуют шаблону и жестко закодированы в следующем фрагменте:

function distance(x,y){
     // axes symmetry 
     x = Math.abs(x);
     y = Math.abs(y);
     // diagonal symmetry 
     if (x < y) {
        t = x;x = y; y = t;
     }
     // 2 corner cases
     if(x==1 && y == 0){
        return 3;
     }
     if(x==2 && y == 2){
        return 4;
     }
    
    // main formula
    var delta = x-y;
		if(y>delta){
  		return delta - 2*Math.floor((delta-y)/3);
  	}
  	else{
  		return delta - 2*Math.floor((delta-y)/4);
  	}
}


$body = $("body");
var html = "";
for (var y = 20; y >= 0; y--){
	html += '<tr>';
	for (var x = 0; x <= 20; x++){
  	html += '<td style="width:20px; border: 1px solid #cecece" id="'+x+'_'+y+'">'+distance(x,y)+'</td>';
  }
  html += '</tr>';
}

html = '<table>'+html+'</table>';
$body.append(html);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Примечание: jQuery используется только для иллюстрации, код см. В функции distance.

17 голосов
/ 26 февраля 2010

Да, Дейкстра и BFS дадут вам ответ, но я думаю, что шахматный контекст этой проблемы дает знания, которые могут дать решение, которое намного быстрее, чем универсальный алгоритм кратчайшего пути, особенно на бесконечной шахматной доске.

Для простоты давайте опишем шахматную доску как плоскость (x, y). Цель состоит в том, чтобы найти кратчайший путь от (x0, y0) до (x1, y1), используя только возможные шаги (+ -1, + -2), (+ -2, + -1) и (+ -2) , + -2), как описано в вопросе PS

Вот новое наблюдение: нарисуйте квадрат с углами (x-4, y-4), (x-4, y + 4), (x + 4, y-4), (x + 4, y +4). Этот набор (назовите его S4) содержит 32 очка. Кратчайший путь от любой из этих 32 точек до (x, y) требует ровно двух ходов .

Кратчайший путь из любой из 24 точек в наборе S3 (определяется аналогично) до (x, y) требует не менее двух ходов .

Следовательно, если | x1-x0 |> 4 или | y1-y0 |> 4, кратчайший путь из (x0, y0) в (x1, y1) ровно на два хода больше, чем кратчайший путь из (x0, y0) до S4. И последняя проблема может быть быстро решена с помощью простой итерации.

Пусть N = max (| x1-x0 |, | y1-y0 |). Если N> = 4, то кратчайший путь от (x0, y0) до (x1, y1) имеет ceil (N / 2) шагов.

12 голосов
/ 12 ноября 2014

Ответ O (1) выше [https://stackoverflow.com/a/8778592/4288232 от Мустафы Сердара Шанлы] на самом деле не работает.(Отметьте (1,1) или (3,2) или (4,4), за исключением очевидных краевых случаев (1,0) или (2,2)).

Ниже приведеноболее уродливое решение (python), которое работает (с добавленными «тестами»):

def solve(x,y):
        x = abs(x)
        y = abs(y)
        if y > x:
            temp=y
            y=x
            x=temp  
        if (x==2 and y==2):
            return 4
        if (x==1 and y==0):
            return 3

    if(y == 0 or float(y) / float(x) <= 0.5):
        xClass = x % 4
        if (xClass == 0):
            initX = x/2
        elif(xClass == 1):
            initX = 1 + (x/2)
        elif(xClass == 2):
            initX = 1 + (x/2)
        else:
            initX = 1 + ((x+1)/2)

        if (xClass > 1):
            return initX - (y%2)
        else:
            return initX + (y%2)
    else:
        diagonal = x - ((x-y)/2)
        if((x-y)%2 == 0):
            if (diagonal % 3 == 0):
                return (diagonal/3)*2
            if (diagonal % 3 == 1):
                return ((diagonal/3)*2)+2
            else:
                return ((diagonal/3)*2)+2
        else:
            return ((diagonal/3)*2)+1


def test():
    real=[
    [0,3,2,3,2,3,4,5,4,5,6,7,6,7],
    [3,2,1,2,3,4,3,4,5,6,5,6,7,8],
    [2,1,4,3,2,3,4,5,4,5,6,7,6,7],
    [3,2,3,2,3,4,3,4,5,6,5,6,7,8],
    [2,3,2,3,4,3,4,5,4,5,6,7,6,7],
    [3,4,3,4,3,4,5,4,5,6,5,6,7,8],
    [4,3,4,3,4,5,4,5,6,5,6,7,6,7],
    [5,4,5,4,5,4,5,6,5,6,7,6,7,8],
    [4,5,4,5,4,5,6,5,6,7,6,7,8,7],
    [5,6,5,6,5,6,5,6,7,6,7,8,7,8],
    [6,5,6,5,6,5,6,7,6,7,8,7,8,9],
    [7,6,7,6,7,6,7,6,7,8,7,8,9,8]]

    for x in range(12):
        for y in range(12):
            res = solve(x,y)
            if res!= real[x][y]:
                print (x, y), "failed, and returned", res, "rather than", real[x][y]
            else:
               print (x, y), "worked. Cool!"

test()
9 голосов
/ 26 февраля 2010

Что вам нужно сделать, так это думать о возможных ходах коня как о графике, где каждая позиция на доске - это узел, а возможные перемещения - в другую позицию как ребро. Нет необходимости в алгоритме Дейкстры, потому что каждое ребро имеет одинаковый вес или расстояние (все они такие же простые или короткие). Вы можете просто выполнить поиск BFS от начальной точки, пока не достигнете конечной позиции.

6 голосов
/ 19 декабря 2016

Решение из первых принципов в Python

Впервые я столкнулся с этой проблемой в тесте Codility. Мне дали 30 минут на ее решение - мне понадобилось гораздо больше времени, чтобы достичь этого результата! Проблема заключалась в следующем: сколько ходов требуется рыцарю, чтобы перейти от 0,0 до x, y, используя только легальные ходы Рыцаря. x и y были более или менее неограниченными (поэтому мы не говорим здесь о простой шахматной доске 8x8).

Они хотели O (1) решение. Я хотел найти решение, в котором программа явно решала проблему (т.е. я хотел что-то более очевидно правильное, чем паттерн Грэма - паттерны имеют привычку ломаться, когда вы не смотрите), и я действительно хотел не полагаться на безусловная формула, как в растворе Мустафы

Итак, вот мое решение, для чего оно стоит. Начните, как и другие, отметив, что решение симметрично относительно осей и диагоналей, поэтому нам нужно решать только для 0> = y> = x. Для простоты объяснения (и кода) я собираюсь обратить вспять проблему: конь начинается с x, y и стремится к 0,0.

Давайте предположим, что мы сократили проблему до области происхождения. Мы вернемся к тому, что на самом деле означает «vicinty», но сейчас давайте просто напишем некоторые решения в виде таблицы (источник слева внизу):

2 1 4 3
3 2 1 2
0 3 2 3

Итак, учитывая x, y на сетке, мы можем просто считать количество ходов к началу координат.

Если мы начали вне сетки, мы должны вернуться к ней. Мы вводим «среднюю линию», которая является линией, представленной y = x / 2. Любой рыцарь в точке x, y на этой линии может вернуться к чит-листу, используя серию 8-часовых ходов (то есть: (-2, -1) ходов). Если x, y лежит выше средней линии, то нам понадобится последовательность ходов в 8 часов и 7 часов, а если она лежит ниже средней линии, нам понадобится последовательность в 8 часов и 10 часов. Часы движутся. Здесь следует отметить две вещи:

  • Эти последовательности являются предположительно кратчайшими путями. (Хотите, чтобы я это доказал, или это очевидно?)
  • Мы заботимся только о количестве таких ходов. Мы можем смешивать и сочетать ходы в любом порядке.

Итак, давайте посмотрим на движения выше средней линии. Мы утверждаем, что:

  • (dx; dy) = (2,1; 1,2) (n8; n7) (матричная запись, без набора математических символов - вектор столбца (dx; dy) равен квадратной матрице, умноженной на вектор столбца ( n8; n7) - количество ходов в 8 часов и количество ходов в 7 часов), а также аналогично;

  • (dx; dy) = (2,2; 1, -1) (n8; n10)

Я утверждаю, что dx, dy будет примерно (x, y), поэтому (x-dx, y-dy) будет находиться вблизи источника (независимо от того, что "окрестность" окажется).

Две строки в коде, которые вычисляют эти термины, являются решением для них, но они выбраны, чтобы иметь некоторые полезные свойства:

  • Формула выше средней линии перемещается (x, y) к одному из (0,0), (1,1) или (2,2).
  • Формула ниже средней линии перемещается (x, y) к одному из (0,0), (1,0), (2,0) или (1,1).

(Вы хотите доказательства этого?) Итак, расстояние Рыцаря будет суммой n7, n8, n10 и шпаргалки [x-dx, y-dy], и наша шпаргалка сводится к этому:

. . 4
. 2 .
0 3 2

Так вот, это еще не конец истории. Посмотрите на 3 в нижнем ряду. Единственные способы, которыми мы можем достичь этого:

  • Мы начали там, или
  • Мы переместились туда на 8 и 10 часов. Но если последний ход был 8 часов (что он имеет право, так как мы можем делать наши ходы в любом порядке), то мы должны были пройти через (3,1), расстояние которого на самом деле 2 (как вы можете см. из оригинальной таблицы). Поэтому мы должны вернуться на один ход назад на 8 часов, сохранив всего два хода.

Есть аналогичная оптимизация для 4 справа вверху. Помимо начала, единственный способ достичь этого - это 8-часовой ход из (4,3). Этого нет в чит-листе, но если бы он был там, его расстояние было бы 3, потому что вместо этого мы могли бы иметь 7 occlocked to (3,1), который имеет расстояние только 2. Итак, мы должны вернуться на один Двигайтесь на 8 часов, а затем идите вперед на 7 часов.

Итак, нам нужно добавить еще один номер в таблицу:

. . 4
. 2 . 2
0 3 2

(Примечание: существует целый ряд оптимизаций обратного слежения из (0,1) и (0,2), но, поскольку решатель никогда не приведет нас туда, нам не нужно беспокоиться о них.)

Итак, вот код Python для оценки этого:

def knightDistance (x, y):
    # normalise the coordinates
    x, y = abs(x), abs(y)
    if (x<y): x, y = y, x
    # now 0 <= y <= x

    # n8 means (-2,-1) (8 o'clock), n7 means (-1,-2) (7 o'clock), n10 means (-2,+1) (10 o'clock)
    if (x>2*y):
        # we're below the midline.  Using 8- & 10-o'clock moves
        n7, n8, n10 = 0,  (x + 2*y)//4,  (x - 2*y + 1)//4
    else:
        # we're above the midline.  Using 7- and 8-o'clock moves
        n7, n8, n10 = (2*y - x)//3, (2*x - y)//3,  0
    x -= 2*n8 + n7 + 2*n10
    y -= n8 + 2*n7 - n10
    # now 0<=x<=2, and y <= x.  Also (x,y) != (2,1)

    # Try to optimise the paths.
    if (x, y)==(1, 0): # hit the  3.  Did we need to?
        if (n8>0): # could have passed through the 2 at 3,1.  Back-up
            x, y = 3, 1; n8-=1;
    if (x, y)==(2, 2): # hit the 4.  Did we need to?
        if (n8>0): # could have passed through a 3 at 4,3.  Back-up, and take 7 o'clock to 2 at 3,1
            x, y = 3, 1; n8-=1; n7+=1

    # Almost there.  Now look up the final leg
    cheatsheet = [[0, 3, 2], [2, None, 2], [4]]
    return n7 + n8 + n10 + cheatsheet [y][x-y]

Кстати, если вы хотите узнать фактический маршрут, то этот алгоритм также обеспечивает это: это просто последовательность n7 ходов в 7 часов, за которыми следуют (или чередуются) n8 ходов в 8 часов, 10 10-часовой ход, и любой танец продиктован чит-листом (который сам по себе может быть в шпаргалке).

Теперь: как доказать, что это правильно. Недостаточно просто сравнить эти результаты с таблицей правильных ответов, потому что сама проблема не ограничена. Но мы можем сказать, что если расстояние Рыцаря от квадрата s равно d, то если {m} - это множество допустимых ходов от s, расстояние Рыцаря (s + m) должно быть либо d-1, либо d + 1. для всех м. (Вам нужно доказательство этого?) Кроме того, должен быть хотя бы один такой квадрат с расстоянием d-1, если s не является источником. Таким образом, мы можем доказать правильность, показывая, что это свойство верно для каждого квадрата. Таким образом:

def validate (n):

    def isSquareReasonable (x, y):
        d, downhills = knightDistance (x, y), 0
        moves = [(1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1), (-2, 1), (-1,  2)]
        for dx, dy in moves:
            dd = knightDistance (x+dx,  y+dy)
            if (dd == d+1): pass
            elif (dd== d-1): downhills += 1
            else: return False;
        return (downhills>0) or (d==0)

    for x in range (0,  n+1):
        for y in range (0,  n+1):
            if not isSquareReasonable (x,  y): raise RuntimeError ("Validation failed")

В качестве альтернативы, мы можем доказать правильность любого квадрата s, преследуя маршрут от s под гору до начала координат. Сначала проверьте s на предмет разумности, как указано выше, затем выберите любое значение s + m, чтобы расстояние (s + m) == d-1. Повторяйте, пока мы не достигнем начала координат.

Howzat

2 голосов
/ 06 ноября 2012

Я думаю, что это также может помочь вам ..

NumWays(x,y)=1+min(NumWays(x+-2,y-+1),NumWays(x+-1,y+-2)); 

и использование динамического программирования для получения решения.

P.S .: Он вроде использует BFS без необходимости объявлять узлы и ребра графа.

2 голосов
/ 07 октября 2011
/*
This program takes two sets of cordinates on a 8*8 chessboard, representing the
starting and ending points of a knight's path.
The problem is to print the cordinates that the knight traverses in between, following
the shortest path it can take.
Normally this program is to be implemented using the Djikstra's algorithm(using graphs)
but can also be implemented using the array method.
NOTE:Between 2 points there may be more than one shortest path. This program prints
only one of them.
*/

#include<stdio.h>

#include<stdlib.h>

#include<conio.h>

int m1=0,m2=0;

/*
This array contains three columns and 37 rows:
The rows signify the possible coordinate differences.
The columns 1 and 2 contains the possible permutations of the row and column difference 
between two positions on a chess board;
The column 3 contains the minimum number of steps involved in traversing the knight's 
path with the given permutation*/

int arr[37][3]={{0,0,0},{0,1,3},{0,2,2},{0,3,3},{0,4,2},{0,5,3},{0,6,4},{0,7,5},    {1,1,2},{1,2,1},{1,3,2},{1,4,3},{1,5,4},{1,6,3},{1,7,4},{2,2,4},{2,3,3},{2,4,2},
            {2,5,3},{2,6,3},{2,7,5},{3,3,2},{3,4,3},{3,5,4},{3,6,3},{3,7,4},{4,4,4},{4,5,3},{4,6,4},{4,7,5},{5,5,4},{5,6,5},{5,7,4},{6,6,5},{6,7,5},{7,7,6}};

void printMoves(int,int,int,int,int,int);
void futrLegalMove(int,int,int,int);
main()
{
  printf("KNIGHT'S SHORTEST PATH ON A 8*8 CHESSBOARD :\n");
  printf("------------------------------------------");
  printf("\nThe chessboard may be treated as a 8*8 array here i.e. the (1,1) ");
  printf("\non chessboard is to be referred as (0,0) here and same for (8,8) ");
  printf("\nwhich is to be referred as (7,7) and likewise.\n");
  int ix,iy,fx,fy;
  printf("\nEnter the initial position of the knight :\n");
  scanf("%d%d",&ix,&iy);
  printf("\nEnter the final position to be reached :\n");
  scanf("%d%d",&fx,&fy);
  int px=ix,py=iy;
  int temp;
  int tx,ty;
  printf("\nThe Knight's shortest path is given by :\n\n");
  printf("(%d, %d)",ix,iy);
  futrLegalMove(px,py,m1,m2);
  printMoves(px,py,fx,fy,m1,m2);
   getch();
} 

/*
  This method checkSteps() checks the minimum number of steps involved from current
  position(a & b) to final position(c & d) by looking up in the array arr[][].
*/

int checkSteps(int a,int b,int c,int d)
{  
    int xdiff, ydiff;
    int i, j;
    if(c>a)
        xdiff=c-a;
    else
        xdiff=a-c;
    if(d>b)
        ydiff=d-b;
    else
        ydiff=b-d;
    for(i=0;i<37;i++)
        {
            if(((xdiff==arr[i][0])&&(ydiff==arr[i][1])) || ((xdiff==arr[i][1])&& (ydiff==arr[i] [0])))
            {
                j=arr[i][2];break;
            }
        }

        return j;
}   

/*
This method printMoves() prints all the moves involved.
*/

void printMoves(int px,int py, int fx, int fy,int a,int b)
{    
 int temp;
 int tx,ty;
 int t1,t2;
  while(!((px==fx) && (py==fy)))
  {   
      printf(" --> ");
      temp=checkSteps(px+a,py+b,fx,fy);
      tx=px+a;
      ty=py+b;
      if(!(a==2 && b==1))
      {if((checkSteps(px+2,py+1,fx,fy)<temp) && checkMove(px+2,py+1))
      {temp=checkSteps(px+2,py+1,fx,fy);
       tx=px+2;ty=py+1;}}
      if(!(a==2 && b==-1))
      {if((checkSteps(px+2,py-1,fx,fy)<temp) && checkMove(px+2,py-1))
      {temp=checkSteps(px+2,py-1,fx,fy);
       tx=px+2;ty=py-1;}}
      if(!(a==-2 && b==1))
      {if((checkSteps(px-2,py+1,fx,fy)<temp) && checkMove(px-2,py+1))
      {temp=checkSteps(px-2,py+1,fx,fy);
       tx=px-2;ty=py+1;}}
      if(!(a==-2 && b==-1))
      {if((checkSteps(px-2,py-1,fx,fy)<temp) && checkMove(px-2,py-1))
      {temp=checkSteps(px-2,py-1,fx,fy);
       tx=px-2;ty=py-1;}}
      if(!(a==1 && b==2))
      {if((checkSteps(px+1,py+2,fx,fy)<temp) && checkMove(px+1,py+2))
      {temp=checkSteps(px+1,py+2,fx,fy);
       tx=px+1;ty=py+2;}}
      if(!(a==1 && b==-2))
      {if((checkSteps(px+1,py-2,fx,fy)<temp) && checkMove(px+1,py-2))
      {temp=checkSteps(px+1,py-2,fx,fy);
       tx=px+1;ty=py-2;}}
      if(!(a==-1 && b==2))
      {if((checkSteps(px-1,py+2,fx,fy)<temp) && checkMove(px-1,py+2))
      {temp=checkSteps(px-1,py+2,fx,fy);
       tx=px-1;ty=py+2;}}
      if(!(a==-1 && b==-2))
      {if((checkSteps(px-1,py-2,fx,fy)<temp) && checkMove(px-1,py-2))
      {temp=checkSteps(px-1,py-2,fx,fy);
       tx=px-1;ty=py-2;}}
       t1=tx-px;//the step taken in the current move in the x direction.
       t2=ty-py;//" " " " " " " " " " " " " " " " " " " " " y " " " " ".
       px=tx;
       py=ty;
       printf("(%d, %d)",px,py);
       futrLegalMove(px,py,t1,t2);
       a=m1;
       b=m2;
   }

} 

/*
The method checkMove() checks whether the move in consideration is beyond the scope of
board or not.
*/   

int checkMove(int a, int b)
{
    if(a>7 || b>7 || a<0 || b<0)
        return 0;
    else
        return 1;
}

/*Out of the 8 possible moves, this function futrLegalMove() sets the valid move by
  applying the following constraints
      1. The next move should not be beyond the scope of the board.
      2. The next move should not be the exact opposite of the previous move.
  The 1st constraint is checked by sending all possible moves to the checkMove() 
  method;
  The 2nd constraint is checked by passing as parameters(i.e. a and b) the steps of the 
  previous move and checking whether or not it is the exact opposite of the current move.
*/

void futrLegalMove(int px,int py,int a,int b)
{
     if(checkMove(px+2,py+1) && (a!=-2 && b!=-1))
         m1=2,m2=1;
     else
     {
         if(checkMove(px+2,py-1)&& (a!=-2 && b!=1))
             m1=2,m2=-1;
     else
     {
         if(checkMove(px-2,py+1)&& (a!=2 && b!=-1))
              m1=-2,m2=1;
     else
     {
         if(checkMove(px-2,py-1)&& (a!=2 && b!=1))
               m1=-2,m2=-1;
     else
     {
         if(checkMove(px+1,py+2)&& (b!=-2 && a!=-1))
               m2=2,m1=1;
     else
     {
         if(checkMove(px+1,py-2)&& (a!=-1 && b!=2))
               m2=-2,m1=1;
     else
     {
         if(checkMove(px-1,py+2)&& (a!=1 && b!=-2))
               m2=2,m1=-1;
     else
     {
         if(checkMove(px-1,py-2)&& (a!=1 && b!=2))
               m2=-2,m1=-1;
     }}}}}}}
}

//End of Program.

Я еще не изучал графики .. Что касается проблемы реализации его с помощью простых массивов, я не мог найти никакого другого решения, кроме этого. Я рассматривал позиции не как ранги и файлы (обычные шахматные обозначения), а как индексы массивов. К вашему сведению, это только для шахматной доски 8 * 8. Любые советы по улучшению всегда приветствуются.

* Комментариев должно хватить для понимания логики. Тем не менее, вы всегда можете спросить.

* Проверено на компиляторе DEV-C ++ 4.9.9.2 (Bloodshed Software).

...