@name DFS_Maze
@inputs BUTTON
@outputs 
@persist [CellStack R]:array [ SProps Table CellWalls VisitedCells PropModels Props]:table [CurrentCell Size]:vector2
@persist SS TotalCells CellsCount Scale CurrentProp EPos:vector RandomN Seed Z W
#This maze generator uses "Depth-First Search" algorithm
#Constructor

runOnChat(1)
    
if(first()){  
    runOnTick(1)
    
    print(_HUD_PRINTCENTER,"1")
    Size = vec2(100,100)
    Z=12232232322  #Seed goes here
    
    Scale = 94.9
    CellsCount = 0
    Size -= vec2(1,1)
    TotalCells = (Size:x()+1)*(Size:y()+1)
    EPos = entity():pos()+vec(Scale,Scale,0)
    
    CurrentCell = Size
    R=array(CurrentCell)
    
    holoCreate(1)
    holoScale(1,vec(5,5,5))
    holoEntity(1):setTrails(20,20,10,"trails/electric",vec(255,255,255),255)
    runOnChat(1)
    runOnTick(1)
    
    W=362
    function number rGenerator(Length){
        Z = 36969 * bAnd(Z,65535) + bShr(Z,16)
        W = 18000 * bAnd(W,65535) + bShr(W,16)
        return abs(mod(bShl(Z,16) + Z,Length))+1
    }
    
    function string fixPosIndex(Pos1:vector2, Pos2:vector2){
        if (Pos1:x() < Pos2:x()||((Pos1:x() == Pos2:x())&&(Pos1:y() < Pos2:y()))){
            Str=Pos1:toString()+Pos2:toString()
        }else{
            Str=Pos2:toString()+Pos1:toString()
        }
        return Str
    }
    
    function vector4 getWalls(Pos:vector2){
        W1=CellWalls[fixPosIndex(Pos,Pos+vec2(0,-1)),number]
        W2=CellWalls[fixPosIndex(Pos,Pos+vec2(1,0)),number]
        W3=CellWalls[fixPosIndex(Pos,Pos+vec2(0,1)),number]
        W4=CellWalls[fixPosIndex(Pos,Pos+vec2(-1,0)),number]
        return vec4(W1,W2,W3,W4)
    }
    function array getFullCells(Pos:vector2){
        R=array()
        if(Pos:y()<Size:y()&getWalls(Pos+vec2(0,1))==vec4()){
            R:pushVector2(Pos+vec2(0,1)) 
        }
        if(Pos:y()>0&getWalls(Pos+vec2(0,-1))==vec4()){
            R:pushVector2(Pos+vec2(0,-1)) 
        }
        if(Pos:x()<Size:x()&getWalls(Pos+vec2(1,0))==vec4()){
            R:pushVector2(Pos+vec2(1,0)) 
        }
        if(Pos:x()>0&getWalls(Pos+vec2(-1,0))==vec4()){
            R:pushVector2(Pos+vec2(-1,0)) 
        }
        
        #print(R)
        return R
    }
    function array getNonvisitedCells(Pos:vector2){
        R=array()
        T=(Pos+vec2(0,-1))
        if(Pos:y()>0&!VisitedCells[T:toString(),number]&getWalls(T):z()==1){
            R:pushVector2(T) 
        }
        
        T=(Pos+vec2(1,0))
        if(Pos:x()<Size:x()&!VisitedCells[T:toString(),number]&getWalls(T):w()==1){
            R:pushVector2(T) 
        }
        
        T=(Pos+vec2(0,1))
        if(Pos:y()<Size:y()&!VisitedCells[T:toString(),number]&getWalls(T):x()==1){
            R:pushVector2(T) 
        }
        
        T=(Pos+vec2(-1,0))
        if(Pos:x()>0&!VisitedCells[T:toString(),number]&getWalls(T):y()==1){
            R:pushVector2(T) 
        }
        return R
        
    }
    PropModels["[0,1,0,1]",table]=table("models/hunter/tubes/tube2x2x2.mdl",ang(90,0,0))
    PropModels["[1,0,1,0]",table]=table("models/hunter/tubes/tube2x2x2.mdl",ang(0,0,90))
    
    PropModels["[1,1,0,0]",table]=table("models/hunter/tubes/tubebend2x2x90square.mdl",ang(90,90,-90))
    PropModels["[0,1,1,0]",table]=table("models/hunter/tubes/tubebend2x2x90square.mdl",ang(-90,-90,90))
    PropModels["[0,0,1,1]",table]=table("models/hunter/tubes/tubebend2x2x90square.mdl",ang(90,-90,-90))
    PropModels["[1,0,0,1]",table]=table("models/hunter/tubes/tubebend2x2x90square.mdl",ang(-90,-90,-90))
    
    PropModels["[1,1,1,0]",table]=table("models/hunter/tubes/tube2x2xt.mdl",ang(-90,0,0))
    PropModels["[0,1,1,1]",table]=table("models/hunter/tubes/tube2x2xt.mdl",ang(-90,90,0))
    PropModels["[1,0,1,1]",table]=table("models/hunter/tubes/tube2x2xt.mdl",ang(90,0,0))
    PropModels["[1,1,0,1]",table]=table("models/hunter/tubes/tube2x2xt.mdl",ang(90,90,0))
    
    
    
    PropModels["[0,0,0,1]",table]=table("models/hunter/blocks/cube2x2x2.mdl",ang(0,0,0))
    PropModels["[0,1,0,0]",table]=table("models/hunter/blocks/cube2x2x2.mdl",ang(0,0,0))
    PropModels["[1,0,0,0]",table]=table("models/hunter/blocks/cube2x2x2.mdl",ang(0,0,0))
    PropModels["[0,0,1,0]",table]=table("models/hunter/blocks/cube2x2x2.mdl",ang(0,0,0))
    
    PropModels["[1,1,1,1]",table]=table("models/hunter/tubes/tube2x2x+.mdl",ang(90,0,90))
    
    
}
#Code
elseif(CellsCount < TotalCells && !SS){
    print(_HUD_PRINTCENTER,"2")
    if(R:count()>0){
        NCell=R[rGenerator(R:count()),vector2] #round(random(1,R:count()))
        CellWalls[fixPosIndex(NCell,CurrentCell),number]=1
        #print(CellWalls:toString())
        CellStack:pushVector2(CurrentCell)
        
        CurrentCell=NCell
        Props:pushTable(table(NCell))
        
        CellsCount++    
    }else{
        
        CurrentCell=CellStack:popVector2()
        
    } 
    holoPos(1,entity():pos()+vec(CurrentCell*Scale))
    R=getFullCells(CurrentCell)

}elseif(CurrentProp<CellsCount){
    print(_HUD_PRINTCENTER,"3")
  
    CurrentProp++ 
    Pos=Props[CurrentProp,table][1,vector2]
    Walls = getWalls(Pos)
    
    
    if(Pos==vec2(0,0)){
        Ent=propSpawn("models/hunter/blocks/cube2x2x2.mdl",EPos+vec(Pos*Scale)+vec(0,0,47.5),ang(),1)
        Ent:setColor(255,255,0,100)
        Ent:propNotSolid(1)
    }elseif(Pos==Size){
        Ent=propSpawn("models/hunter/blocks/cube2x2x2.mdl",EPos+vec(Pos*Scale)+vec(0,0,47.5),ang(),1)
        Ent:setColor(255,0,0,100)
        Ent:propNotSolid(1)
    }else{
        Ent=propSpawn(PropModels[Walls:toString(),table][1,string],EPos+vec(Pos*Scale)+vec(0,0,47.5),PropModels[Walls:toString(),table][2,angle],1)
    }
    Ent:setMaterial("phoenix_storms/middle")
    Ent:setColor(vec(255))
    Props[CurrentProp,table] = table(Pos,Ent)
    SProps[Pos:toString(),entity]=Ent
}

if(SS==1){
    print(_HUD_PRINTCENTER,"5")

    
    if(R:count()>0){
        NCell=R[rGenerator(R:count()),vector2]
        VisitedCells[NCell:toString(),number]=1
        if(NCell!=vec2()&&NCell!=Size){
            SProps[NCell:toString(),entity]:setColor(255,255,0)
        }
        CellStack:pushVector2(CurrentCell)
        CurrentCell=NCell
    }else{
        SProps[CurrentCell:toString(),entity]:setColor(255,0,0)
        CurrentCell=CellStack:popVector2()
    }
    R=getNonvisitedCells(CurrentCell)
    if(CurrentCell==Size){SS=2} 
}elseif(SS==2){
    print("Path found! Path is "+CellStack:count()+" cells long.")
    SS=3
}


if(changed(BUTTON) && BUTTON){
        SS=1
        CellStack=array()
        CurrentCell=vec2()
        CellStack:pushVector2(CurrentCell)
        R=array(CurrentCell)
        print("Searching path to finish...")
    
}
