title: "Lecture 5: Chess Problems" author: "Junaid Hasan" date: "June 30, 2021"¶
Today we will discuss Chess Problems. More specifically we will discuss non-attacking problems.
Lets begin.
Question 1: Non-attacking Rooks.¶
Place the most number of non-attacking rooks on a chess board.
Idea: If rook is place at $(i,j)$ then another rooks cannot be placed at the same row/column.
Answer: Let $x_{ij} = 1$ if rook is placed at row $i$, column $j$ and $0$ otherwise. Then we want to
maximize $\sum_{ij} x_{ij}$ subject to
$\sum_{i} x_{ij} \leq 1$ non attacking on rows.
$\sum_{j} x_{ij} \leq 1$ non attacking on columns.
Question 2: Non-attacking Bishops.¶
How do we translate the same if we want non-attacking bishops instead of rooks.
- Idea: Bishops attack across diagonals ($i+j = k$) or anti-diagonals ($i-j = k$). Note that $2 \leq i+j \leq 16$ and $ -7 \leq i-j \leq 7$.
- Answer: Let $x_{ij} = 1$ if bishop is placed at $(i,j)$ and 0 otherwise. We want to
- maxmimize $\sum_{ij} x_{ij}$ subject to
- $\sum_{i+j = k} x_{ij} \leq 1$ for $k = 2, \ldots, 16$.
- $\sum_{i-j = k} x_{ij} \leq 1$ for $k = -7, \ldots, 7$.
Question 3: Non-attacking Queens.¶
A queen is basically a bishop and a rook. Therefore we enforce both constraints.
- maxmimize $\sum_{ij} x_{ij}$ subject to
- $\sum_{i+j = k} x_{ij} \leq 1$ for $k = 2, \ldots, 16$.
- $\sum_{i-j = k} x_{ij} \leq 1$ for $k = -7, \ldots, 7$.
- $\sum_{i} x_{ij} \leq 1$.
- $\sum_{j} x_{ij} \leq 1$.
Question 4: Non-attacking Kings¶
There cannot be more than one king among
$x_{i,j}, x_{i\pm1, j}, x_{i, j \pm 1}, x_{i \pm 1, j \pm 1}$ However, note that we can alternatively just enforce one king among $x_{i,j}, x_{i+1,j}, x_{i,j+1}, x_{i+1,j+1}$.
Therefore we get
- maximize $\sum_{ij} x_{ij}$ subject to
- $x_{i,j}+ x_{i+1,j}+ x_{i,j+1}+ x_{i+1,j+1} \leq 1$ for $1, \leq i,j \leq 7$.
Question 5: Non-attacking knights¶
Note that similar to kings, looking forward and down is enough. (We do not need to look backward and up). Except at the borders.
Therefore we get
- maximize $\sum_{ij} x_{ij}$ subject to
- $x_{i,j} + x_{i',j'} \leq 1$ for $(i',j') \in jump(i,j)$.
I = list(range(1,9))
J = list(range(1,9))
from pyscipopt import Model, quicksum
modelknight = Model("Non attacking knights")
IJ = [(i,j) for i in I for j in J]
IplusJ={}
for k in range(2,17):
IplusJ[k] = []
for i in I:
for j in J:
IplusJ[i+j].append((i,j))
IminusJ={}
for k in range(-7,8):
IminusJ[k] = []
for i in I:
for j in J:
IminusJ[i-j].append((i,j))
jumpIJ = {}
for c in IJ:
jumpIJ[c] = []
if 1<= c[0]-2 <=8 and 1<= c[1]+1 <=8:
jumpIJ[c].append((c[0]-2, c[1]+1))
if 1<= c[0]-2 <=8 and 1<= c[1]-1 <=8:
jumpIJ[c].append((c[0]-2, c[1]-1))
if 1<= c[0]-1 <=8 and 1<= c[1]+2 <=8:
jumpIJ[c].append((c[0]-1, c[1]+2))
if 1<= c[0]-1 <=8 and 1<= c[1]-2 <=8:
jumpIJ[c].append((c[0]-1, c[1]-2))
if 1<= c[0]+1 <=8 and 1<= c[1]+2 <=8:
jumpIJ[c].append((c[0]+1, c[1]+2))
if 1<= c[0]+1 <=8 and 1<= c[1]-2 <=8:
jumpIJ[c].append((c[0]+1, c[1]-2))
if 1<= c[0]+2 <=8 and 1<= c[1]+1 <=8:
jumpIJ[c].append((c[0]+2, c[1]+1))
if 1<= c[0]+2 <=8 and 1<= c[1]-1 <=8:
jumpIJ[c].append((c[0]+2, c[1]-1))
IplusJ
{2: [(1, 1)],
3: [(1, 2), (2, 1)],
4: [(1, 3), (2, 2), (3, 1)],
5: [(1, 4), (2, 3), (3, 2), (4, 1)],
6: [(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)],
7: [(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1)],
8: [(1, 7), (2, 6), (3, 5), (4, 4), (5, 3), (6, 2), (7, 1)],
9: [(1, 8), (2, 7), (3, 6), (4, 5), (5, 4), (6, 3), (7, 2), (8, 1)],
10: [(2, 8), (3, 7), (4, 6), (5, 5), (6, 4), (7, 3), (8, 2)],
11: [(3, 8), (4, 7), (5, 6), (6, 5), (7, 4), (8, 3)],
12: [(4, 8), (5, 7), (6, 6), (7, 5), (8, 4)],
13: [(5, 8), (6, 7), (7, 6), (8, 5)],
14: [(6, 8), (7, 7), (8, 6)],
15: [(7, 8), (8, 7)],
16: [(8, 8)]}
knightdata = {}
for c in IJ:
knightdata[c] = modelknight.addVar(vtype="B", name = "Knight at %s,%s" %c)
for c in IJ:
for d in jumpIJ[c]:modelknight.addCons(knightdata[c] + knightdata[d] <=1, "atmost one knight among %s, %s" %c + " and %s, %s" %d)
modelknight.setObjective(quicksum(knightdata[d] for d in IJ), "maximize")
modelknight.optimize()
modelknight.getObjVal()
32.0
modelrooks = Model("Non attacking Rooks")
rookdata = {}
for i in I:
for j in J:
rookdata[(i,j)] = modelrooks.addVar(vtype="B", name = "Rook at %s,%s" %(i,j))
for i in I:
modelrooks.addCons(quicksum(rookdata[(i,j)] for j in J) <=1)
for j in J:
modelrooks.addCons(quicksum(rookdata[(i,j)] for i in I) <=1)
modelrooks.setObjective(quicksum(rookdata[(i,j)] for i in I for j in J), "maximize")
modelrooks.optimize()
modelrooks.getObjVal()
8.0
Lets draw the knights:
import chess
import chess.svg
import random
This converts from the (i,j) notation to the FEN notation
def getFenChess(chessboard):
Rank = [8, 7, 6, 5, 4, 3, 2, 1]
File = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
string1 = ""
for r in Rank:
rowstring = ""
num_spaces = 0
for f in File:
if chessboard[(r,f)] == ' ':
num_spaces +=1
if f == 'h':
rowstring = rowstring+str(num_spaces)
else:
if num_spaces != 0:
rowstring = rowstring+str(num_spaces)
num_spaces = 0
rowstring = rowstring+chessboard[(r,f)]
if r != 1:
string1 = string1+rowstring+"/"
# print(rowstring)
else:
string1 = string1+ rowstring
return string1
Filenumber = {1:'a', 2:'b', 3:'c', 4:'d', 5:'e', 6:'f', 7:'g', 8:'h'}
chessboard = {}
for i in I:
for j in J:
if modelknight.getVal(knightdata[(i,j)]) == 1:
chessboard[(i,Filenumber[j])] = 'N'
else:
chessboard[(i,Filenumber[j])] = ' '
knightboard = chess.Board(getFenChess(chessboard))
chess.svg.board(knightboard, size=240)
chessboard = {}
for i in I:
for j in J:
if modelrooks.getVal(rookdata[(i,j)]) == 1:
chessboard[(i,Filenumber[j])] = 'R'
else:
chessboard[(i,Filenumber[j])] = ' '
rookboard = chess.Board(getFenChess(chessboard))
chess.svg.board(rookboard, size=240)
modelbishops = Model("Non attacking Bishops")
bishopdata = {}
for i in I:
for j in J:
bishopdata[(i,j)] = modelbishops.addVar(vtype="B", name = "Bishop at %s,%s" %(i,j))
for k in IplusJ:
modelbishops.addCons(quicksum(bishopdata[l] for l in IplusJ[k]) <=1)
for k in IminusJ:
modelbishops.addCons(quicksum(bishopdata[l] for l in IminusJ[k]) <=1)
modelbishops.setObjective(quicksum(bishopdata[(i,j)] for i in I for j in J), "maximize")
modelbishops.optimize()
modelbishops.getObjVal()
14.0
chessboard = {}
for i in I:
for j in J:
if modelbishops.getVal(bishopdata[(i,j)]) == 1:
chessboard[(i,Filenumber[j])] = 'b'
else:
chessboard[(i,Filenumber[j])] = ' '
bishopboard = chess.Board(getFenChess(chessboard))
chess.svg.board(bishopboard, size=240)
modelqueens = Model("Non attacking Queen")
queendata = {}
for i in I:
for j in J:
queendata[(i,j)] = modelqueens.addVar(vtype="B", name = "Queen at %s,%s" %(i,j))
for i in I:
modelqueens.addCons(quicksum(queendata[(i,j)] for j in J) <=1)
for j in J:
modelqueens.addCons(quicksum(queendata[(i,j)] for i in I) <=1)
for k in IplusJ:
modelqueens.addCons(quicksum(queendata[l] for l in IplusJ[k]) <=1)
for k in IminusJ:
modelqueens.addCons(quicksum(queendata[l] for l in IminusJ[k]) <=1)
modelqueens.setObjective(quicksum(queendata[(i,j)] for i in I for j in J), "maximize")
modelqueens.optimize()
modelqueens.getObjVal()
8.0
chessboard = {}
for i in I:
for j in J:
if modelqueens.getVal(queendata[(i,j)]) == 1:
chessboard[(i,Filenumber[j])] = 'q'
else:
chessboard[(i,Filenumber[j])] = ' '
queenboard = chess.Board(getFenChess(chessboard))
chess.svg.board(queenboard, size=240)
modelqueens.getSolvingTime