N Queens in Python

Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
3
down vote
favorite
I used Python very occasionally, today I solved n queens in Python just for fun. I am sure there are better way of doing certain things in Python and can't wait for improvement suggestions!
#!/usr/bin/python3
def print_board(board):
for row in board:
for field in row:
print('Q ', end='') if field else print('X ', end='')
print()
print()
def can_place_queen(board, row, col):
for field in board[row]:
if field: return False
for i in range(len(board[0])):
if board[i][col]: return False
i, j = row, col
l = len(board)
while i < l and j < l:
if board[i][j]: return False
i, j = i + 1, j + 1
i, j = row, col
while i < l and j >= 0:
if board[i][j]: return False
i, j = i + 1, j - 1
i, j = row, col
while i >= 0 and j < l:
if board[i][j]: return False
i, j = i - 1, j + 1
i, j = row, col
while i >= 0 and j >= 0:
if board[i][j]: return False
i, j = i - 1, j - 1
return True
def place_queens(board, row=0):
global nSolutions
if row >= len(board):
print_board(board)
nSolutions += 1
return
for col, field in enumerate(board):
if can_place_queen(board, row, col):
board[row][col] = True
place_queens(board, row + 1)
board[row][col] = False
nSolutions = 0
# Note that [[False]*8]*8 does not work since * will just copy the address
# of the row!
board = [[False] * 8 for i in range(8)]
place_queens(board)
print("Found %s solutions!" % nSolutions)
python recursion backtracking n-queens
add a comment |Â
up vote
3
down vote
favorite
I used Python very occasionally, today I solved n queens in Python just for fun. I am sure there are better way of doing certain things in Python and can't wait for improvement suggestions!
#!/usr/bin/python3
def print_board(board):
for row in board:
for field in row:
print('Q ', end='') if field else print('X ', end='')
print()
print()
def can_place_queen(board, row, col):
for field in board[row]:
if field: return False
for i in range(len(board[0])):
if board[i][col]: return False
i, j = row, col
l = len(board)
while i < l and j < l:
if board[i][j]: return False
i, j = i + 1, j + 1
i, j = row, col
while i < l and j >= 0:
if board[i][j]: return False
i, j = i + 1, j - 1
i, j = row, col
while i >= 0 and j < l:
if board[i][j]: return False
i, j = i - 1, j + 1
i, j = row, col
while i >= 0 and j >= 0:
if board[i][j]: return False
i, j = i - 1, j - 1
return True
def place_queens(board, row=0):
global nSolutions
if row >= len(board):
print_board(board)
nSolutions += 1
return
for col, field in enumerate(board):
if can_place_queen(board, row, col):
board[row][col] = True
place_queens(board, row + 1)
board[row][col] = False
nSolutions = 0
# Note that [[False]*8]*8 does not work since * will just copy the address
# of the row!
board = [[False] * 8 for i in range(8)]
place_queens(board)
print("Found %s solutions!" % nSolutions)
python recursion backtracking n-queens
2
I have rolled back the last edit to avoid confusing readers of the question with two different versions of the same code. Please see what you may and may not do after receiving answers.
â Vogel612â¦
May 3 at 19:34
add a comment |Â
up vote
3
down vote
favorite
up vote
3
down vote
favorite
I used Python very occasionally, today I solved n queens in Python just for fun. I am sure there are better way of doing certain things in Python and can't wait for improvement suggestions!
#!/usr/bin/python3
def print_board(board):
for row in board:
for field in row:
print('Q ', end='') if field else print('X ', end='')
print()
print()
def can_place_queen(board, row, col):
for field in board[row]:
if field: return False
for i in range(len(board[0])):
if board[i][col]: return False
i, j = row, col
l = len(board)
while i < l and j < l:
if board[i][j]: return False
i, j = i + 1, j + 1
i, j = row, col
while i < l and j >= 0:
if board[i][j]: return False
i, j = i + 1, j - 1
i, j = row, col
while i >= 0 and j < l:
if board[i][j]: return False
i, j = i - 1, j + 1
i, j = row, col
while i >= 0 and j >= 0:
if board[i][j]: return False
i, j = i - 1, j - 1
return True
def place_queens(board, row=0):
global nSolutions
if row >= len(board):
print_board(board)
nSolutions += 1
return
for col, field in enumerate(board):
if can_place_queen(board, row, col):
board[row][col] = True
place_queens(board, row + 1)
board[row][col] = False
nSolutions = 0
# Note that [[False]*8]*8 does not work since * will just copy the address
# of the row!
board = [[False] * 8 for i in range(8)]
place_queens(board)
print("Found %s solutions!" % nSolutions)
python recursion backtracking n-queens
I used Python very occasionally, today I solved n queens in Python just for fun. I am sure there are better way of doing certain things in Python and can't wait for improvement suggestions!
#!/usr/bin/python3
def print_board(board):
for row in board:
for field in row:
print('Q ', end='') if field else print('X ', end='')
print()
print()
def can_place_queen(board, row, col):
for field in board[row]:
if field: return False
for i in range(len(board[0])):
if board[i][col]: return False
i, j = row, col
l = len(board)
while i < l and j < l:
if board[i][j]: return False
i, j = i + 1, j + 1
i, j = row, col
while i < l and j >= 0:
if board[i][j]: return False
i, j = i + 1, j - 1
i, j = row, col
while i >= 0 and j < l:
if board[i][j]: return False
i, j = i - 1, j + 1
i, j = row, col
while i >= 0 and j >= 0:
if board[i][j]: return False
i, j = i - 1, j - 1
return True
def place_queens(board, row=0):
global nSolutions
if row >= len(board):
print_board(board)
nSolutions += 1
return
for col, field in enumerate(board):
if can_place_queen(board, row, col):
board[row][col] = True
place_queens(board, row + 1)
board[row][col] = False
nSolutions = 0
# Note that [[False]*8]*8 does not work since * will just copy the address
# of the row!
board = [[False] * 8 for i in range(8)]
place_queens(board)
print("Found %s solutions!" % nSolutions)
python recursion backtracking n-queens
edited May 3 at 19:34
Vogel612â¦
20.9k345124
20.9k345124
asked May 2 at 21:53
Nils
2631210
2631210
2
I have rolled back the last edit to avoid confusing readers of the question with two different versions of the same code. Please see what you may and may not do after receiving answers.
â Vogel612â¦
May 3 at 19:34
add a comment |Â
2
I have rolled back the last edit to avoid confusing readers of the question with two different versions of the same code. Please see what you may and may not do after receiving answers.
â Vogel612â¦
May 3 at 19:34
2
2
I have rolled back the last edit to avoid confusing readers of the question with two different versions of the same code. Please see what you may and may not do after receiving answers.
â Vogel612â¦
May 3 at 19:34
I have rolled back the last edit to avoid confusing readers of the question with two different versions of the same code. Please see what you may and may not do after receiving answers.
â Vogel612â¦
May 3 at 19:34
add a comment |Â
2 Answers
2
active
oldest
votes
up vote
6
down vote
accepted
One option that might help speed things up would be to include a bit of redundant metadata with your board. Rather than just representing an array of 8*8 Booleans representing whether a Queen is in play on a given cell, you could also have 8 Boolean arrays indicating whether a Queen is in play anywhere on a given row, column, or diagonal. That would make it marginally more complicated to actually play the piece, but considerably easier to check whether a move is legal.
Especially if you were looking to have a more complex representation of a board state, it would probably be worthwhile encapsulating it into a class.
A second area that is worth thinking about exploiting symmetry. If you have found one solution, that layout reflected vertically, reflected horizontally, or rotated this way or that will also be a valid solution. It takes a little bit of care to confirm that it's a distinct solution, but avoiding even checking the majority of board positions because you've already checked reflections or rotations is a big win.
One of the many elegant tricks that Python provides is list comprehensions, for manipulating whole lists at once.
For example, to convert a list row of booleans into a list of "Q" and "X" you can use this.
["Q" if i else "X" for i in row]
This means that your inner loop in print_board can be written
print("".join(["Q" if i else "X" for i in row]))
Another handy trick for avoiding explicit loops is the any and all reduction functions. These take a collection of Booleans (or things that can be converted to Boolean) and returns True if any or all respectively are True.
For example (ignoring my first suggestion) the first check in can_place_queen can be changed from
for field in board[row]:
if field: return False
to
if any(board[row]): return False
However writing the other checks quite so succinctly would not work as well.
Finally, the conventional way of writing a script to run on its own is
def main():
nSolutions = 0
# Note that [[False]*8]*8 does not work since * will just copy the address
# of the row!
board = [[False] * 8 for i in range(8)]
place_queens(board)
print("Found %s solutions!" % nSolutions)
if __name__ == "__main__": main()
The primary purpose of this is so that if someone imports the python file as a module, they don't get the results of running the script.
Thanks for such an elaborate answer! I was not aware that any and all are available as free standing functions.
â Nils
May 3 at 7:29
2
no need for the brackets inprint("".join(["Q" if i else "X" for i in row])). Join can accept a generator expression
â Maarten Fabré
May 3 at 19:45
add a comment |Â
up vote
2
down vote
I would suggest either commenting each of your check loops, to explain which check they're doing ("Checking diagonal to bottom right for queens") or perhaps putting each of these sub checks in a function of their own.
It is also possible that you are able to do both the bottom-right and top-left strides in a single loop, or even abstract all of the searches into a single function which takes strides, since there is a lot of code duplication there:
def diagonal_search(row, col, board, x_stride, y_stride):
i, j = row, col
l = len(board)
while i < l and i >= 0 and j < l and j >= 0:
if board[i][j]: return False
i, j = i + x_stride, j + y_stride
return True
and use it as:
# Search the row
if not diagonal_search(row, col, board, 1, 0):
return False
# Search the column
if not diagonal_search(row, col, board, 0, 1):
return False
# Search the right-down diagonal
if not diagonal_search(row, col, board, 1, 1):
return False
etc..
Thx! I like the idea of a generalized diagonal_search(...) function :-)
â Nils
May 3 at 11:06
Although the first two examples are not exactly a diagonal search.
â Nils
May 3 at 19:10
add a comment |Â
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
6
down vote
accepted
One option that might help speed things up would be to include a bit of redundant metadata with your board. Rather than just representing an array of 8*8 Booleans representing whether a Queen is in play on a given cell, you could also have 8 Boolean arrays indicating whether a Queen is in play anywhere on a given row, column, or diagonal. That would make it marginally more complicated to actually play the piece, but considerably easier to check whether a move is legal.
Especially if you were looking to have a more complex representation of a board state, it would probably be worthwhile encapsulating it into a class.
A second area that is worth thinking about exploiting symmetry. If you have found one solution, that layout reflected vertically, reflected horizontally, or rotated this way or that will also be a valid solution. It takes a little bit of care to confirm that it's a distinct solution, but avoiding even checking the majority of board positions because you've already checked reflections or rotations is a big win.
One of the many elegant tricks that Python provides is list comprehensions, for manipulating whole lists at once.
For example, to convert a list row of booleans into a list of "Q" and "X" you can use this.
["Q" if i else "X" for i in row]
This means that your inner loop in print_board can be written
print("".join(["Q" if i else "X" for i in row]))
Another handy trick for avoiding explicit loops is the any and all reduction functions. These take a collection of Booleans (or things that can be converted to Boolean) and returns True if any or all respectively are True.
For example (ignoring my first suggestion) the first check in can_place_queen can be changed from
for field in board[row]:
if field: return False
to
if any(board[row]): return False
However writing the other checks quite so succinctly would not work as well.
Finally, the conventional way of writing a script to run on its own is
def main():
nSolutions = 0
# Note that [[False]*8]*8 does not work since * will just copy the address
# of the row!
board = [[False] * 8 for i in range(8)]
place_queens(board)
print("Found %s solutions!" % nSolutions)
if __name__ == "__main__": main()
The primary purpose of this is so that if someone imports the python file as a module, they don't get the results of running the script.
Thanks for such an elaborate answer! I was not aware that any and all are available as free standing functions.
â Nils
May 3 at 7:29
2
no need for the brackets inprint("".join(["Q" if i else "X" for i in row])). Join can accept a generator expression
â Maarten Fabré
May 3 at 19:45
add a comment |Â
up vote
6
down vote
accepted
One option that might help speed things up would be to include a bit of redundant metadata with your board. Rather than just representing an array of 8*8 Booleans representing whether a Queen is in play on a given cell, you could also have 8 Boolean arrays indicating whether a Queen is in play anywhere on a given row, column, or diagonal. That would make it marginally more complicated to actually play the piece, but considerably easier to check whether a move is legal.
Especially if you were looking to have a more complex representation of a board state, it would probably be worthwhile encapsulating it into a class.
A second area that is worth thinking about exploiting symmetry. If you have found one solution, that layout reflected vertically, reflected horizontally, or rotated this way or that will also be a valid solution. It takes a little bit of care to confirm that it's a distinct solution, but avoiding even checking the majority of board positions because you've already checked reflections or rotations is a big win.
One of the many elegant tricks that Python provides is list comprehensions, for manipulating whole lists at once.
For example, to convert a list row of booleans into a list of "Q" and "X" you can use this.
["Q" if i else "X" for i in row]
This means that your inner loop in print_board can be written
print("".join(["Q" if i else "X" for i in row]))
Another handy trick for avoiding explicit loops is the any and all reduction functions. These take a collection of Booleans (or things that can be converted to Boolean) and returns True if any or all respectively are True.
For example (ignoring my first suggestion) the first check in can_place_queen can be changed from
for field in board[row]:
if field: return False
to
if any(board[row]): return False
However writing the other checks quite so succinctly would not work as well.
Finally, the conventional way of writing a script to run on its own is
def main():
nSolutions = 0
# Note that [[False]*8]*8 does not work since * will just copy the address
# of the row!
board = [[False] * 8 for i in range(8)]
place_queens(board)
print("Found %s solutions!" % nSolutions)
if __name__ == "__main__": main()
The primary purpose of this is so that if someone imports the python file as a module, they don't get the results of running the script.
Thanks for such an elaborate answer! I was not aware that any and all are available as free standing functions.
â Nils
May 3 at 7:29
2
no need for the brackets inprint("".join(["Q" if i else "X" for i in row])). Join can accept a generator expression
â Maarten Fabré
May 3 at 19:45
add a comment |Â
up vote
6
down vote
accepted
up vote
6
down vote
accepted
One option that might help speed things up would be to include a bit of redundant metadata with your board. Rather than just representing an array of 8*8 Booleans representing whether a Queen is in play on a given cell, you could also have 8 Boolean arrays indicating whether a Queen is in play anywhere on a given row, column, or diagonal. That would make it marginally more complicated to actually play the piece, but considerably easier to check whether a move is legal.
Especially if you were looking to have a more complex representation of a board state, it would probably be worthwhile encapsulating it into a class.
A second area that is worth thinking about exploiting symmetry. If you have found one solution, that layout reflected vertically, reflected horizontally, or rotated this way or that will also be a valid solution. It takes a little bit of care to confirm that it's a distinct solution, but avoiding even checking the majority of board positions because you've already checked reflections or rotations is a big win.
One of the many elegant tricks that Python provides is list comprehensions, for manipulating whole lists at once.
For example, to convert a list row of booleans into a list of "Q" and "X" you can use this.
["Q" if i else "X" for i in row]
This means that your inner loop in print_board can be written
print("".join(["Q" if i else "X" for i in row]))
Another handy trick for avoiding explicit loops is the any and all reduction functions. These take a collection of Booleans (or things that can be converted to Boolean) and returns True if any or all respectively are True.
For example (ignoring my first suggestion) the first check in can_place_queen can be changed from
for field in board[row]:
if field: return False
to
if any(board[row]): return False
However writing the other checks quite so succinctly would not work as well.
Finally, the conventional way of writing a script to run on its own is
def main():
nSolutions = 0
# Note that [[False]*8]*8 does not work since * will just copy the address
# of the row!
board = [[False] * 8 for i in range(8)]
place_queens(board)
print("Found %s solutions!" % nSolutions)
if __name__ == "__main__": main()
The primary purpose of this is so that if someone imports the python file as a module, they don't get the results of running the script.
One option that might help speed things up would be to include a bit of redundant metadata with your board. Rather than just representing an array of 8*8 Booleans representing whether a Queen is in play on a given cell, you could also have 8 Boolean arrays indicating whether a Queen is in play anywhere on a given row, column, or diagonal. That would make it marginally more complicated to actually play the piece, but considerably easier to check whether a move is legal.
Especially if you were looking to have a more complex representation of a board state, it would probably be worthwhile encapsulating it into a class.
A second area that is worth thinking about exploiting symmetry. If you have found one solution, that layout reflected vertically, reflected horizontally, or rotated this way or that will also be a valid solution. It takes a little bit of care to confirm that it's a distinct solution, but avoiding even checking the majority of board positions because you've already checked reflections or rotations is a big win.
One of the many elegant tricks that Python provides is list comprehensions, for manipulating whole lists at once.
For example, to convert a list row of booleans into a list of "Q" and "X" you can use this.
["Q" if i else "X" for i in row]
This means that your inner loop in print_board can be written
print("".join(["Q" if i else "X" for i in row]))
Another handy trick for avoiding explicit loops is the any and all reduction functions. These take a collection of Booleans (or things that can be converted to Boolean) and returns True if any or all respectively are True.
For example (ignoring my first suggestion) the first check in can_place_queen can be changed from
for field in board[row]:
if field: return False
to
if any(board[row]): return False
However writing the other checks quite so succinctly would not work as well.
Finally, the conventional way of writing a script to run on its own is
def main():
nSolutions = 0
# Note that [[False]*8]*8 does not work since * will just copy the address
# of the row!
board = [[False] * 8 for i in range(8)]
place_queens(board)
print("Found %s solutions!" % nSolutions)
if __name__ == "__main__": main()
The primary purpose of this is so that if someone imports the python file as a module, they don't get the results of running the script.
edited May 3 at 7:31
Nils
2631210
2631210
answered May 2 at 23:45
Josiah
3,172326
3,172326
Thanks for such an elaborate answer! I was not aware that any and all are available as free standing functions.
â Nils
May 3 at 7:29
2
no need for the brackets inprint("".join(["Q" if i else "X" for i in row])). Join can accept a generator expression
â Maarten Fabré
May 3 at 19:45
add a comment |Â
Thanks for such an elaborate answer! I was not aware that any and all are available as free standing functions.
â Nils
May 3 at 7:29
2
no need for the brackets inprint("".join(["Q" if i else "X" for i in row])). Join can accept a generator expression
â Maarten Fabré
May 3 at 19:45
Thanks for such an elaborate answer! I was not aware that any and all are available as free standing functions.
â Nils
May 3 at 7:29
Thanks for such an elaborate answer! I was not aware that any and all are available as free standing functions.
â Nils
May 3 at 7:29
2
2
no need for the brackets in
print("".join(["Q" if i else "X" for i in row])). Join can accept a generator expressionâ Maarten Fabré
May 3 at 19:45
no need for the brackets in
print("".join(["Q" if i else "X" for i in row])). Join can accept a generator expressionâ Maarten Fabré
May 3 at 19:45
add a comment |Â
up vote
2
down vote
I would suggest either commenting each of your check loops, to explain which check they're doing ("Checking diagonal to bottom right for queens") or perhaps putting each of these sub checks in a function of their own.
It is also possible that you are able to do both the bottom-right and top-left strides in a single loop, or even abstract all of the searches into a single function which takes strides, since there is a lot of code duplication there:
def diagonal_search(row, col, board, x_stride, y_stride):
i, j = row, col
l = len(board)
while i < l and i >= 0 and j < l and j >= 0:
if board[i][j]: return False
i, j = i + x_stride, j + y_stride
return True
and use it as:
# Search the row
if not diagonal_search(row, col, board, 1, 0):
return False
# Search the column
if not diagonal_search(row, col, board, 0, 1):
return False
# Search the right-down diagonal
if not diagonal_search(row, col, board, 1, 1):
return False
etc..
Thx! I like the idea of a generalized diagonal_search(...) function :-)
â Nils
May 3 at 11:06
Although the first two examples are not exactly a diagonal search.
â Nils
May 3 at 19:10
add a comment |Â
up vote
2
down vote
I would suggest either commenting each of your check loops, to explain which check they're doing ("Checking diagonal to bottom right for queens") or perhaps putting each of these sub checks in a function of their own.
It is also possible that you are able to do both the bottom-right and top-left strides in a single loop, or even abstract all of the searches into a single function which takes strides, since there is a lot of code duplication there:
def diagonal_search(row, col, board, x_stride, y_stride):
i, j = row, col
l = len(board)
while i < l and i >= 0 and j < l and j >= 0:
if board[i][j]: return False
i, j = i + x_stride, j + y_stride
return True
and use it as:
# Search the row
if not diagonal_search(row, col, board, 1, 0):
return False
# Search the column
if not diagonal_search(row, col, board, 0, 1):
return False
# Search the right-down diagonal
if not diagonal_search(row, col, board, 1, 1):
return False
etc..
Thx! I like the idea of a generalized diagonal_search(...) function :-)
â Nils
May 3 at 11:06
Although the first two examples are not exactly a diagonal search.
â Nils
May 3 at 19:10
add a comment |Â
up vote
2
down vote
up vote
2
down vote
I would suggest either commenting each of your check loops, to explain which check they're doing ("Checking diagonal to bottom right for queens") or perhaps putting each of these sub checks in a function of their own.
It is also possible that you are able to do both the bottom-right and top-left strides in a single loop, or even abstract all of the searches into a single function which takes strides, since there is a lot of code duplication there:
def diagonal_search(row, col, board, x_stride, y_stride):
i, j = row, col
l = len(board)
while i < l and i >= 0 and j < l and j >= 0:
if board[i][j]: return False
i, j = i + x_stride, j + y_stride
return True
and use it as:
# Search the row
if not diagonal_search(row, col, board, 1, 0):
return False
# Search the column
if not diagonal_search(row, col, board, 0, 1):
return False
# Search the right-down diagonal
if not diagonal_search(row, col, board, 1, 1):
return False
etc..
I would suggest either commenting each of your check loops, to explain which check they're doing ("Checking diagonal to bottom right for queens") or perhaps putting each of these sub checks in a function of their own.
It is also possible that you are able to do both the bottom-right and top-left strides in a single loop, or even abstract all of the searches into a single function which takes strides, since there is a lot of code duplication there:
def diagonal_search(row, col, board, x_stride, y_stride):
i, j = row, col
l = len(board)
while i < l and i >= 0 and j < l and j >= 0:
if board[i][j]: return False
i, j = i + x_stride, j + y_stride
return True
and use it as:
# Search the row
if not diagonal_search(row, col, board, 1, 0):
return False
# Search the column
if not diagonal_search(row, col, board, 0, 1):
return False
# Search the right-down diagonal
if not diagonal_search(row, col, board, 1, 1):
return False
etc..
answered May 3 at 10:41
gbartonowen
1163
1163
Thx! I like the idea of a generalized diagonal_search(...) function :-)
â Nils
May 3 at 11:06
Although the first two examples are not exactly a diagonal search.
â Nils
May 3 at 19:10
add a comment |Â
Thx! I like the idea of a generalized diagonal_search(...) function :-)
â Nils
May 3 at 11:06
Although the first two examples are not exactly a diagonal search.
â Nils
May 3 at 19:10
Thx! I like the idea of a generalized diagonal_search(...) function :-)
â Nils
May 3 at 11:06
Thx! I like the idea of a generalized diagonal_search(...) function :-)
â Nils
May 3 at 11:06
Although the first two examples are not exactly a diagonal search.
â Nils
May 3 at 19:10
Although the first two examples are not exactly a diagonal search.
â Nils
May 3 at 19:10
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f193505%2fn-queens-in-python%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
2
I have rolled back the last edit to avoid confusing readers of the question with two different versions of the same code. Please see what you may and may not do after receiving answers.
â Vogel612â¦
May 3 at 19:34