0

I want to build a maze using sat solver. Please help me to correctly compose the algorithm for generating conditions. I tried to implement some conditions, but they turned out to be wrong.

This picture explains how I represent the maze using the example 4*4 -> enter image description here

This is my code

main.py

from tkinter import *
import random
import formulas
from pysat.solvers import Solver
from itertools import cycle
import numpy as np

width = int(input("width - "))
height = int(input("height - "))

root = Tk()
root.title("Labirint")
root.state('zoomed')
z = PhotoImage(width=1, height=1)
root.configure(bg='white')

res = formulas.formula_1(width, height) + formulas.formula_2(width,height) + formulas.formula_3(width,height) + formulas.formula_4(width,height)

def reverse(mas):
    arr = np.array(mas)
    arr = -arr
    return arr.tolist()

def solve(): # function that finds a solution for an array of conditions res
    solver = Solver(name="g4")

    for clause in res:
        solver.add_clause(clause)

    flag = solver.solve()  # SAT | UNSAT
    if flag:
        solution = solver.get_model()

        res.append(list(map(lambda x: x * -1, solution)))

    i = 0

    while i<1000:
        for clause in res:
            solver.add_clause(clause)

        flag = solver.solve()  # SAT | UNSAT
        solution = solver.get_model()

        i+=1

        res.append(list(map(lambda x: x * -1, solution)))

    print(len(res))



def draw_walls(width, height):
    num1 = random.randint(0, (height * 2) - 1)
    num2 = random.randint(0, (width * 2) - 1)

    # maze walls are buttons so that we can manually change the maze as we want
    # num_1 and num_2 are the entrance and exit of the maze
    # vertical and horizontal arrays draw walls along the edges of the maze
    # arrays of green orange and paint the walls as in the picture
    # pink is the intersection of walls

    vertical = []
    for i in range(2):  #  vertical walls
        for f in range(height):
            vertical.append(
                Checkbutton(root, indicatoron=0, background="white", selectcolor="black", image=z, compound='c',
                            width=5, height=11, highlightthickness=0, bd=0))
            vertical[-1].place(x=i * width * 20, y=7 + (f * 20))
            vertical[-1].select()
    vertical[num1].deselect()

    horizontal = []
    for i in range(2):
        for f in range(width):
            horizontal.append(
                Checkbutton(root, indicatoron=0, background="white", selectcolor="black", image=z, compound='c',
                            width=11, height=5, highlightthickness=0, bd=0))
            horizontal[-1].place(x=7 + f * 20, y=i * height * 20)
            horizontal[-1].select()
    horizontal[num2].deselect()

    green = []
    for i in range(height):  # vertical walls
        for f in range(width - 1):
            green.append(
                Checkbutton(root, indicatoron=0, background="white", selectcolor="black", image=z, compound='c',
                            width=5, height=11, highlightthickness=0, bd=0))
            green[-1].place(x=20 + f * 20, y=7 + (i * 20))
            green[-1].select()

    orange = []
    for i in range(height - 1):  # horizontal walls
        for f in range(width):
            orange.append(
                Checkbutton(root, indicatoron=0, background="white", selectcolor="black", image=z, compound='c',
                            width=11, height=5, highlightthickness=0, bd=0))
            orange[-1].place(x=7 + (f * 20), y=20 + (i * 20))
            orange[-1].select()

    pink = []
    for i in range(height + 1):  # pink dots
        for f in range(width + 1):
            pink.append(Checkbutton(root, indicatoron=0, background="white", selectcolor="black", image=z, compound='c',
                                    width=5, height=5, highlightthickness=0, bd=0))
            pink[-1].place(x=0 + (f * 20), y=0 + (i * 20))
            pink[-1].select()

    return green, orange


walls = draw_walls(width, height)

licycle_1 = cycle(walls[0])
licycle_2 = cycle(walls[1])

solve()

end = res

u = random.randint(len(res) - 10 , len(res) - 1)
end = res[u]
end = reverse(end)

def draw_1():
    for i in range(height):
        for f in range(width - 1):
            t = next(licycle_1)
            if end[f + (i * ((width * 2) - 1))] < 0:
                t.deselect()

def draw_2():
    for i in range(height - 1):
        for f in range(width):
            t = next(licycle_2)
            if end[(((width - 1) + f) + (((width * 2) - 1) * i))] < 0:
                t.deselect()

draw_1()
draw_2()

root.mainloop()

formulas.py

#   count_walls = ((height-1)*width)+((width-1)*height)

def formula_1(width, height):  # There must be at least one wall in a 2*2 square
    tab = []
    delta = (width * 2) - 1

    for i in range(height - 1):
        for f in range(width - 1):
            l = [f + 1 + i * delta, f + width + i * delta, f + width + 1 + i * delta, f + (width * 2) + i * delta]
            tab.append(l)
    return tab


def formula_2(width, height):  # there cannot be a separate wall horizontally
    tab = []
    delta = (width * 2) - 1

    for i in range(height - 1):
        for f in range(width - 2):
            l = [f + 1 + i * delta, f + 2 + i * delta, f + width + i * delta,
                 f + 2 + width + i * delta, f + (width * 2) + i * delta, f + 1 + (width * 2) + i * delta]
            tab.append(l)
    return tab

def formula_3(width, height):  # there cannot be a separate wall vertically
    term = (width * 2) - 1
    tab = []
    for f in range(width - 1):
        for i in range(height - 2):
            l = [f + 1 + term * i,
            f + 1 + term * i + width,
            f + 1 + term * i + 3 * width - 1,
            f + 1 + term * i + 4 * width - 2,
            f + 1 + term * i + 3 * width - 2,
            f + 1 + term * i + width - 1]
        tab.append(l)
    return tab

def formula_4(width, height): # there can't be an isolated 1*1 square
    tab = []
    for i in range(width-2):
        l = [-(width + 1 + i), -((width * 2) + i) , -((width * 2) + 1 + i), -((width * 3) + i)]
        tab.append(l)
    for i in range(width-3):
        for f in range(height-2):
            l = [-((width * 3 * (i + 1)) - ((width + 1) * i) + f),
                 -((width * 3 * (i + 1)) - ((width + 1) * i) + width - 1 + f),
                 -((width * 3 * (i + 1)) - ((width + 1) * i) + width + f),
                 -((width * 3 * (i + 1)) - ((width + 1) * i) + (width * 2) - 1 +f)]
            tab.append(l)
    return tab

# there can't be an isolated 1*1 square on the borders and in the corners

With formula_1, I want to set a condition so that there is at least one wall in a 2 * 2 square. With formula_2, I want to set a condition so that there are no separate horizontal walls. With formula_3, I want to set a condition so that there are no separate vertical walls. And with аormula_4 I want to set a condition so that there are no isolated squares.

I did not make a function where there cannot be an isolated 1 * 1 square on the borders and in the corners, because most likely I will make a mistake

The output is clauses in cnf.

I use PySat to solve problem.

This is a labyrinth with a size of 15 * 15 -> enter image description here

But if you take a size larger, it looks bad. For example 20*20 -> enter image description here

Please help me write the clauses correctly. You may notice inaccuracies in using the sat solver. Thank you in advance

Ken White
  • 123,280
  • 14
  • 225
  • 444
Defqon
  • 1

1 Answers1

-1

Two maze generation algorithms are used:

binary tree produces simple easily solved mazes. https://en.wikipedia.org/wiki/Maze_generation_algorithm#Simple_algorithms

recursive division produces more complex mazes. https://en.wikipedia.org/wiki/Maze_generation_algorithm#Recursive_division_method

FYI there is a C++ implementation of both these algorithms at https://github.com/JamesBremner/PathFinder/wiki/Maze

ravenspoint
  • 19,093
  • 6
  • 57
  • 103