Skip to content

Instantly share code, notes, and snippets.

@Samuel-Retter
Last active August 29, 2015 14:01
Show Gist options
  • Save Samuel-Retter/6329590760e05d82c86a to your computer and use it in GitHub Desktop.
Save Samuel-Retter/6329590760e05d82c86a to your computer and use it in GitHub Desktop.
Solves the Nqueens puzzle (http://en.wikipedia.org/wiki/Eight_queens_puzzle) using simulated annealing. I provided links to images of some results in a comment.
__author__ = 'Samuel'
import time
from itertools import permutations
import random
import math
import matplotlib.pyplot as plt
e = math.e
n = 8 #default size of board, can be changed
def createBoard(arr):
#############################################################
#
# Takes a single array arr of length n, where
# every element in the array is an int in [0,n). Returns a 2D array of n rows, each of
# length n. In this array, all elements are ' ' except
# those at positions specified by arr. For example:
#
# createBoard([0,2,1,3,5,4,6,7])
#
# would return
#
# [['۩', ' ', ' ', ' ', ' ', ' ', ' ', ' '], --> Queen at index 0
# [' ', ' ', '۩', ' ', ' ', ' ', ' ', ' '], --> Queen at index 2
# [' ', '۩', ' ', ' ', ' ', ' ', ' ', ' '], etc.
# [' ', ' ', ' ', '۩', ' ', ' ', ' ', ' '],
# [' ', ' ', ' ', ' ', ' ', '۩', ' ', ' '],
# [' ', ' ', ' ', ' ', '۩', ' ', ' ', ' '],
# [' ', ' ', ' ', ' ', ' ', ' ', '۩', ' '],
# [' ', ' ', ' ', ' ', ' ', ' ', ' ', '۩']]
#############################################################
rows = []
for i in range(n):
rows.append([])
for rowindex in range(n):
for squareindex in range(n):
if squareindex == arr[rowindex]:
# consider either 'Q' or chr(1769)
rows[rowindex].append(chr(1769))
else:
rows[rowindex].append(' ')
return rows
#returns a board (in 2D list form)
def scoreArr(arr):
# tests how good of a solution arr is
# lower is better
# there is one queen in every row and column
# so only problem will be queens in the same diagonal
s = 0
diags1 = [] # positive sloping diagonals
diags2 = [] # negative sloping diagonals
for i in range(n):
diags1.append(arr[i] - i)
diags2.append(arr[i] - (n-i))
for i in set(diags1):
s += (diags1.count(i) - 1)
for i in set(diags2):
s += (diags2.count(i) - 1)
return s
# if s == 0, then arr is a solution
def annealing():
now = time.time()
sol = []
for i in range(n):
sol.append(i)
random.shuffle(sol)
T = 1.2
coolingrate = .006
while scoreArr(sol) != 0:
T *= (1-coolingrate)
newsol = sol[:]
first = random.randint(0,(n-1))
second = random.randint(0, (n-1))
f1 = newsol[first]
f2 = newsol[second]
newsol[first] = f2
newsol[second] = f1
r = random.uniform(0,1)
currentscore = scoreArr(sol)
newscore = scoreArr(newsol)
# acceptance function:
if (newscore < currentscore) or (newscore >= currentscore and r < e**(-(newscore-currentscore)/T)):
sol = newsol
print(scoreArr(sol), '\t', T)
t = time.time()-now
print("Found a solution in", t, "seconds.")
print(sol)
queens = sol
queen_xs = [x + 0.5 for x in queens[::-1]]
queen_ys = [y + 0.5 for y in range(len(queens))]
fig, ax = plt.subplots()
ax.scatter(queen_xs, queen_ys, s=24174.457*(n**(-1.58652)), alpha=.5, marker="$\Psi$")
ticks, bounds = range(len(queens) + 1), [0, len(queens)]
ax.set_xticks(ticks), ax.set_yticks(ticks)
ax.set_xbound(*bounds), ax.set_ybound(*bounds)
ax.grid(True)
plt.show()
return sol
annealing()
@Samuel-Retter
Copy link
Author

Image of a solved standard chess board (n=8): http://i.imgur.com/1Lw99DB.png
Image of a solved board with n=30: http://i.imgur.com/UFJURoG.png
Image of a solved board with n = 60: http://i.imgur.com/TbxWDEH.png

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment