Как сгенерировать все ходы коня?

Я пишу шахматную программу на Python, которая должна генерировать все ходы коня. Для тех, кто не знаком с шахматами, конь ходит в форме буквы L.

Таким образом, учитывая позицию (2, 4), конь может перейти на (0, 3), (0, 5), (1, 2), (3, 2) и т. д., всего (максимум) восемь различных ходов.

Я хочу написать функцию knight_moves, которая генерирует эти кортежи в виде списка. Как проще всего это сделать в Python?

def knight_moves(position):
    ''' Returns a list of new positions given a knight's current position. '''
    pass

person sdasdadas    schedule 15.10.2013    source источник


Ответы (9)


Итак, спасибо Найлу Бирну, я придумал это:

from itertools import product
def knight_moves(position):
    x, y = position
    moves = list(product([x-1, x+1],[y-2, y+2])) + list(product([x-2,x+2],[y-1,y+1]))
    moves = [(x,y) for x,y in moves if x >= 0 and y >= 0 and x < 8 and y < 8]
    return moves
person sdasdadas    schedule 15.10.2013
comment
есть возможность уменьшить этот код с помощью лямбда, карты и фильтра - person DevC; 09.01.2014
comment
Я не думаю, что это правильно. например, knight_moves((0,0)) дает [(1, 1), (2, 1)], когда должно быть (1,2) и (2,1) - person cmantas; 07.01.2016
comment
После комментария cmantas [y-1, y+1] в первом product следует заменить на [y-2, y+2] для создания правильных ходов. (То есть шаг 1 вправо/влево должен сочетаться с шагом 2 вверх/вниз или наоборот). Я только что отредактировал решение, чтобы исправить это. - person Kurt Peek; 26.10.2017
comment
Спасибо вам обоим, эта ошибка была скрыта в течение 4 лет! - person sdasdadas; 27.10.2017

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

т.е. учитывая вашу (2, 4) начальную точку, варианты (-2,-1), (-2,+1), (-1,+2), (+2,+1) Таким образом, относительные позиции будут всегда быть одинаковым.

person Niall Byrne    schedule 15.10.2013

Не знаком с шахматами...

deltas = [(-2, -1), (-2, +1), (+2, -1), (+2, +1), (-1, -2), (-1, +2), (+1, -2), (+1, +2)]
def knight_moves(position):
    valid_position = lambda (x, y): x >= 0 and y >= 0 and ???
    return filter(valid_position, map(lambda (x, y): (position[0] + x, position[1] + y), deltas))
person xiaowl    schedule 15.10.2013

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

https://www.chessprogramming.org/Bitboards

Основная идея битовых досок состоит в том, чтобы использовать 64-битное целое число и установить 1, если часть присутствует в бите. Например, если бы у вас было 64-битное целое число для представления белых рыцарей, вы бы установили 2-й и 6-й биты в начале игры, поскольку они являются позициями, в которых находятся белые рыцари. Используя это обозначение, становится легко вычислить ходы коня. Несложно будет вычислить и ходы других фигур.

С таким представлением вы можете посмотреть по этой ссылке на шахматный движок, чтобы найти готовый алгоритм для реализации ходов конем.
http://www.mayothi.com/nagaskakichess6.html

person Rohit Shinde    schedule 25.10.2014

Это может показаться излишним, если вы не знакомы с аналитической геометрией (или геометрией комплексных чисел), но я придумал очень элегантное математическое решение, когда реализовывал проверку движения фигур.

Ходы коня лежат на окружности, которую можно определить как (x-x_0)^2+(y-y_0)^2=5, где x_0 и y_0 — текущие координаты коня. Если вы переключитесь на полярные координаты, вы можете получить все возможные координаты с помощью этого простого кода:

import math
def knight_moves(x,y):
    new_positions=[]
    r=math.sqrt(5) #radius of the circle
    for phi in [math.atan(2),math.atan(1/2)]: #angles in radians
        for quadrant in range(4):
            angle=phi+quadrant*math.pi/2 # add 0, 90, 180, 270 degrees in radians      
            new_x=round(x+r*math.cos(angle))
            new_y=round(y+r*math.sin(angle))
            if max(new_x,new_y,7-new_x,7-new_y)<=7: #validation whether the move is in grid
                new_positions.append([new_x,new_y])
    return(new_positions)

def validate_knight_move(x,y,x_0,y_0):
    return((x-x_0)**2+(y-y_0)**2==5)

x_0=2
y_0=4

moves=knight_moves(x_0,y_0)
print(moves)

validation=[validate_knight_move(move[0],move[1],x_0,y_0) for move in moves]
print(validation)


[[3, 6], [0, 5], [1, 2], [4, 3], [4, 5], [1, 6], [0, 3], [3, 2]]
[True, True, True, True, True, True, True, True]

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

def knight_moves2(x,y):
    new_positions=[]
    for dx in [-2,-1,1,2]:
        for dy in [-2,-1,1,2]:
            if(validate_knight_move(x+dx,y+dy,x,y)): #is knight move?
                if max(x+dx,y+dy,7-(x+dx),7-(y+dy))<=7: #validation whether the move is in grid
                    new_positions.append([x+dx,y+dy])
    return(new_positions)

new_positions=knight_moves2(x_0,y_0)
print(new_positions)



[[0, 3], [0, 5], [1, 2], [1, 6], [3, 2], [3, 6], [4, 3], [4, 5]]
person DovaX    schedule 04.04.2020

Вот простая реализация:

def knights_moves():
  a = []
  b = (1, 2)
  while 1:
    a.append(b)
    b = (-b[0], b[1])
    a.append(b)
    b = (b[1], b[0])
    if b in a:
      return a

[(1, 2), (-1, 2), (2, -1), (-2, -1), (-1, -2), (1, -2), (-2, 1), (2, 1)]

Оттуда вы можете просто добавить текущую позицию к каждому члену этого списка, а затем дважды проверить ее достоверность.

person OmnipotentEntity    schedule 29.11.2013

Завершая ответ xiaowl,

possible_places = [(-2, -1), (-2, +1), (+2, -1), (+2, +1), (-1, -2), (-1, +2), (+1, -2), (+1, +2)]
def knight_moves(cur_pos):
    onboard = lambda (x, y): x >= 0 and y >= 0 and x<8 and y<8
    eval_move = lambda(x,y): (cur_pos[0] + x, cur_pos[1] + y)
    return filter(onboard, map(eval_move, possible_places))
person DevC    schedule 09.01.2014

Для ходов конями:

def getAllValidMoves(x0, y0):
    deltas = [(-2, -1), (-2, +1), (+2, -1), (+2, +1), (-1, -2), (-1, +2), (+1, -2), (+1, +2)]
    validPositions = []
    for (x, y) in deltas:
        xCandidate = x0 + x
        yCandidate = y0 + y
        if 0 < xCandidate < 8 and 0 < yCandidate < 8:
            validPositions.append([xCandidate, yCandidate])

    return validPositions

print getAllValidMoves(3,3)

Я просто сохранил все возможные дельты, применил каждую из них к "исходной позиции" и сохранил те, что были внутри шахматной доски

person Juan    schedule 17.03.2017

from itertools import product

def moves():
    """ The available (relative) moves"""
    a = list(product( (1, -1), (2,-2)))
    return a + [tuple(reversed(m)) for m in a]

def neighbors(a,b):
    # true if x,y belongs in a chess table
    in_table = lambda (x, y): all((x < 8, y < 8, x >= 0, y >= 0))
    # returns the possible moving positions
    return filter(in_table, [(a+x, b+y) for x, y in moves()])

"соседи" - доступные позиции, на которые конь может перейти из a,b

person cmantas    schedule 07.01.2016