I'm not trying to solve the puzzle, I'm just interested in how many solutions there are, since the OP claims he doesn't know. I brute forced it with a program.
There are
264 solutions, not taking rotations and reflections into account. However, there are only 36 unique configurations. 30 of these solutions are multiplied by 8 (4 rotations * 2 reflections), while the other 6 only by 4. This is because the solution is already its own reflection.
First of all, there are 32432400 configurations, not taking rotations and reflections into account. Since the board has 16 squares, if we were to place the two kings anywhere, we'd be looking at their combinations using the formula C(16, 2)
. The knights would now have 14 available squares, so multiply the previous result with C(14, 2)
, etc.
I named each square with a number from 0 to 15 like so,
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
generated the piece configurations, checked for validity and saved the solutions. The numbers for each solution represent the board squares, with the kings placed first, then the knights, then the rooks and then the bishops. For example, Skyvask's solution is (2, 13, 0, 15, 7, 8, 1, 14)
.
The complete list can be found here and the unique solutions here.
Code:
from collections import defaultdict
import itertools as it
SIZE = 4
N = SIZE**2
def flatten(rank, file):
return rank * SIZE + file
def is_in_bounds(square):
return 0 <= square < SIZE
def king_moves(rank, file):
moves = []
if rank - 1 >= 0:
moves += [flatten(rank-1, f) for f in range(file-1, file+2) if is_in_bounds(f)]
moves += [flatten(rank, f) for f in (file-1, file+1) if is_in_bounds(f)]
if rank + 1 < SIZE:
moves += [flatten(rank+1, f) for f in range(file-1, file+2) if is_in_bounds(f)]
return moves
def rook_moves(rank, file):
moves = [[flatten(r, file) for r in range(rank-1, -1, -1)]]
moves += [[flatten(r, file) for r in range(rank+1, SIZE)]]
moves += [[flatten(rank, f) for f in range(file-1, -1, -1)]]
moves += [[flatten(rank, f) for f in range(file+1, SIZE)]]
return moves
def bishop_moves(rank, file):
down = range(-1, -rank-1, -1)
up = range(1, SIZE-rank)
moves = [[flatten(rank+i, file-i) for i in down if is_in_bounds(file-i)]]
moves += [[flatten(rank+i, file+i) for i in down if is_in_bounds(file+i)]]
moves += [[flatten(rank+i, file-i) for i in up if is_in_bounds(file-i)]]
moves += [[flatten(rank+i, file+i) for i in up if is_in_bounds(file+i)]]
return moves
def knight_moves(rank, file):
offsets = ((-2, -1), (-2, 1),
(-1, -2), (-1, 2),
(1, -2), (1, 2),
(2, -1), (2, 1))
moves = [flatten(rank+x, file+y) for x, y in offsets
if is_in_bounds(rank+x) and is_in_bounds(file+y)]
return moves
def filter_ray_moves(moves, occupied):
filtered_moves = []
for direction in moves:
for square in direction:
if square not in occupied:
filtered_moves.append(square)
else:
filtered_moves.append(square)
break
return filtered_moves
def solve(piece_moves_from):
solutions = []
attack_rule = [0, 0, 1, 1, 1, 1, 1, 1]
for k1, k2 in it.combinations(range(N), 2):
# if the kings attack each other, skip
if k2 in piece_moves_from['K'][k1]:
continue
allowed = [s for s in range(N) if s not in (k1, k2)]
for n1, n2 in it.combinations(allowed, 2):
n1_attacks = piece_moves_from['N'][n1]
n2_attacks = piece_moves_from['N'][n2]
knight_attacks = n1_attacks + n2_attacks
king_attacks = (piece_moves_from['K'][k1] +
piece_moves_from['K'][k2])
# if either knight attacks any king, skip
if k1 in knight_attacks or k2 in knight_attacks:
continue
# if two kings + one knight gang up on the other knight, skip
if (king_attacks + n1_attacks).count(n2) > 1:
continue
if (king_attacks + n2_attacks).count(n1) > 1:
continue
# Keep track of which squares are attacked more than once so far,
# so we can exclude rooks and bishops there.
# We can't take into account the rooks' attacks, because they
# may be blocked by the time every piece is on the board.
k_n_attacks = king_attacks + knight_attacks
allowed = [s for s in range(N) if s not in (k1, k2, n1, n2)]
for r1, r2 in it.combinations(allowed, 2):
if k_n_attacks.count(r1) > 1 or k_n_attacks.count(r2) > 1:
continue
allowed = [s for s in range(N) if s not in (k1, k2, n1, n2, r1, r2)]
for b1, b2 in it.combinations(allowed, 2):
if k_n_attacks.count(b1) > 1 or k_n_attacks.count(b2) > 1:
continue
piece_coords = (k1, k2, n1, n2, r1, r2, b1, b2)
attack_counter = defaultdict(int)
for coord in (k1, k2):
for square in piece_moves_from['K'][coord]:
attack_counter[square] += 1
for coord in (n1, n2):
for square in piece_moves_from['N'][coord]:
attack_counter[square] += 1
for coord in (r1, r2):
for square in filter_ray_moves(
piece_moves_from['R'][coord], piece_coords):
attack_counter[square] += 1
for coord in (b1, b2):
for square in filter_ray_moves(
piece_moves_from['B'][coord], piece_coords):
attack_counter[square] += 1
attacks = [attack_counter[piece] for piece in piece_coords]
if attacks == attack_rule:
solutions.append(piece_coords)
return solutions
piece_moves = {'K': king_moves,
'N': knight_moves,
'R': rook_moves,
'B': bishop_moves,
}
piece_moves_from = {}
for piece, moves in piece_moves.items():
piece_moves_from[piece] = {}
for rank in range(SIZE):
for file in range(SIZE):
piece_moves_from[piece][flatten(rank, file)] = moves(rank, file)
solutions = solve(piece_moves_from)
One can print a board representation of the solutions with the following function.
def print_board(solution):
squares = [' '] * N
pieces = tuple('KNRB')
sep = '\n' + '+'.join(['-'] * SIZE) + '\n'
for i in range(8):
squares[solution[i]] = pieces[i // 2]
print(sep.join('|'.join(squares[i:i+SIZE]) for i in range(0, N, SIZE)))
The unique solutions are then
K | | K | N K | | K | N K | | K | N K | | K | K | | K |
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
B | | | N B | | | B | | | B | | | N B | | | N
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
| | | B N | | | B | | | B N | | | B | | | B
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
| R | | R R | | | R N | R | | R R | | | R N | R | | R
K | | K | K | | K | K | | K | K | | K | K | | | K
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
| | | R | | | R | | | | | | R N | | | N
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
N | R | N | B N | | N | B N | B | | R N | B | | B B | | | B
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
B | | | R | | | B N | B | | R R | | | N R | | | R
K | | | K K | | | K K | | | K K | | | K K | | | K
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
N | | | B N | | | B N | | | B N | | | B N | | |
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
N | R | | N | | | B B | | | N | R | | B B | R | | B
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
B | | R | R | | | R R | | | R | | R | N | | R | N
K | | | K K | | | K K | | | K K | | | K K | | | K
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
N | | | B B | | | B | | | B B | | | B B | | | B
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
B | | | N | R | | N N | R | | N N | | | N N | R | |
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
R | | R | N | | R | B | | R | R | | | R N | | R |
K | | B | K K | | | K K | | | K K | | | N K | B | | N
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
| | | | | | B | | | B | | | K | | | K
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
N | | | B | R | | B B | R | | B N | | | B B | | |
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
N | R | | R N | | R | N N | | R | N R | R | | R | | R | N
K | | | K | | N | B K | | N | B K | | N | B B | K | | N
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
B | | | K | R | | N | R | | | R | | N | | | K
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
N | | | B | | R | N | | R | | | R | B | | |
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
R | R | | N B | | | K B | | | K B | N | | K R | | R |
| K | | N B | K | | N | K | | N B | K | | | K | |
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
N | | | K | | | K | | | K N | | | K N | | | K
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
B | | | B | | | N B | | | N B | | | N B | | | N
--+---+---+-- --+---+---+-- --+---+---+-- --+---+---+-- --+---+---+--
R | | R | B R | | R | R | | R | B R | | R | R | | R | B
| K | B | N
--+---+---+--
R | | |
--+---+---+--
| | | R
--+---+---+--
N | B | K |
K.K./...R/NB.B/R..N
has a knight attacking a bishop, for example. $\endgroup$