Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Tower of Hanoi in 3D

You can change the number of disks.
As well as set the connection of each pair of the 3 pillars:
1.can move disks from pillar A to pillar B
2.or from B to A
3.or both
4.or both not

Tagged:

Comments

  • edited February 2016 Posts: 8
    function setup()
        parameter.integer("disk number",1,20,7)
        parameter.action("Reset",init)
        parameter.number("camPos x",-50,50,-15)
        parameter.number("camPos y",0,50,15)
        parameter.number("camPos z",0,50,20)
        parameter.integer("Point_Range",0,50,40)
        --[[
        0:both
        1:a to b
        2:b to a
        3:both not
          ]]
        parameter.integer("isCon12",0,3,0)
        parameter.integer("isCon23",0,3,0)
        parameter.integer("isCon13",0,3,0)
        d=5
        pillars={width=1,height=10}
        pillars[1]={x=-d,y=pillars.height/2,z=0,content={}}
        pillars[2]={x=0,y=pillars.height/2,z=3^.5*d,content={}}
        pillars[3]={x=d,y=pillars.height/2,z=0,content={}}
        -------------------------------------------------------------------------
        m=CreateCube()
        m.shader=shader(pointShader.vertexShader,pointShader.fragmentShader)
        m.normals=CalculateNormals(m.vertices) 
        m.shader.pointRange=Point_Range  --NEW
        --user parameters and fixed light settings
        SurfaceColour=color(223, 177, 177, 255) 
        AmbientColour=color(255, 255, 255, 255) 
        AmbientStrength=0.2
        m:setColors(SurfaceColour)
        m.shader.ambientLight = color(AmbientColour.r*AmbientStrength,AmbientColour.g*AmbientStrength,
                                        AmbientColour.b*AmbientStrength)
        DirectColour=color(255,0,0)
        DirectStrength=0.8
        m.shader.directColor=color(DirectColour.r*DirectStrength,DirectColour.g*DirectStrength,
                                    DirectColour.b*DirectStrength)
        m.shader.directDirection=vec4(-2, 10, 5, 1) 
        m.shader.specularColor=color(255)
        m.shader.reflect=1 
        m.shader.shiny=0.6
        init()
    end 
    function init()
        tween.stopAll()
        N=disk_number
        disks={height=math.min(pillars.height*.8/N,2),maxWidth=d*1.8}
        isCon={{},{},{}}
        for i=1,3 do
            for j=1,3 do
                isCon[i][j]=true
            end
            pillars[i].content={}
        end
        for i=1,N do
            disks[i]={x=pillars[1].x,y=(i-.5)*disks.height,z=pillars[1].z,
            width=pillars.width+(N-i+1)*(disks.maxWidth-pillars.width)/N}
            pillars[1].content[i]=i
        end
        if isCon12==1 or isCon12==3 then isCon[2][1]=false end
        if isCon12==2 or isCon12==3 then isCon[1][2]=false end
        if isCon13==1 or isCon13==3 then isCon[3][1]=false end
        if isCon13==2 or isCon13==3 then isCon[1][3]=false end
        if isCon23==1 or isCon23==3 then isCon[3][2]=false end
        if isCon23==2 or isCon23==3 then isCon[2][3]=false end
        instruction=nil
        co=coroutine.create(function() hanoi(1,3,N) end)
        if isCycle() then
            animate()
        else
            alert("The arrows can't form a circle,please change the parameters!","No Solution!")
        end
    end
    function isCycle()
        local r=true
        for i=1,3 do
            local x,y,z=i,i+1,i+2
            if y>3 then y=y%3 end
            if z>3 then z=z%3 end
            if not(isCon[x][y] or (isCon[x][z] and isCon[z][y])) then r=false end
            if not(isCon[x][z] or (isCon[x][y] and isCon[y][z])) then r=false end
        end
        return r
    end
    function animate()
        coroutine.resume(co)--call up the producer
        if instruction then
            if instruction[1]=="move" then
                local s,e=instruction[2],instruction[3]
                instruction=nil
                local i=table.remove(pillars[s].content)--the index of the disk to move
                table.insert(pillars[e].content,i)
                tween(.4,disks[i],{y=pillars.height+disks.height},tween.easing.cubicIn,function()
                    tween(.4,disks[i],{x=pillars[e].x,z=pillars[e].z},tween.easing.linear,function()
                        tween(.4,disks[i],{y=(#pillars[e].content-.5)*disks.height},tween.easing.cubicOut,function()
                            animate()
                        end)
                    end)
                end)
            end
        else
            --init()
            print("Complete!")
        end
    end
    function hanoi(x,y,n)--move n disks from x to y
        local z=6-(x+y)
        if n==1 then
            if isCon[x][y] then
                instruction={"move",x,y}
            else
                instruction={"move",x,z}
                coroutine.yield(co)--once get a instruction,yield itself(ie the producer)
                instruction={"move",z,y}
            end
            coroutine.yield(co)--once get a instruction,yield itself(ie the producer)
        else
            if isCon[x][y] then
                hanoi(x,z,n-1)
                hanoi(x,y,1)
                hanoi(z,y,n-1)
            else
                hanoi(x,y,n-1)
                hanoi(x,z,1)
                hanoi(y,x,n-1)
                hanoi(z,y,1)
                hanoi(x,y,n-1)
            end
        end
    end
    function draw()
        background()
        perspective()
        camera(camPos_x, camPos_y, camPos_z, 0, pillars.height/2, 0)
        m.shader.eyePosition=vec4(camPos_x, camPos_y, camPos_z) 
        m.shader.pointRange=Point_Range
        pushMatrix()
        rotate(90,1,0,0)
        --sprite("Cargo Bot:Starry Background")
        resetMatrix()
        --move and draw cube
        for i=1,N do
            translate(disks[i].x,disks[i].y,disks[i].z)
            scale(disks[i].width,disks.height,disks[i].width)
            m.shader.mModel = modelMatrix()
            m:draw()
            resetMatrix()
        end
        for i=1,3 do
            translate(pillars[i].x,pillars[i].y,pillars[i].z)
            scale(1,pillars.height,1)
            m.shader.mModel = modelMatrix()
            m:draw()
            resetMatrix()
        end
        translate(0,-.01,0)
        scale(100,.02,100)
        m.shader.mModel = modelMatrix()
        m:draw()
        resetMatrix()
        popMatrix()
    end
    function touched(touch)
        if touch.state==BEGAN then
            --animate()
        end
    end
    
  • IgnatzIgnatz Mod
    Posts: 5,396

    You haven't included the shader "pointShader" so it won't run.

  • edited February 2016 Posts: 8
    function CreateCube() --NEW
       local m = mesh()
    
        --vertices for the corners of the cube (stolen from 3d lab)
        local vertices = {
          vec3(-0.5, -0.5,  0.5), -- Left  bottom front
          vec3( 0.5, -0.5,  0.5), -- Right bottom front
          vec3( 0.5,  0.5,  0.5), -- Right top    front
          vec3(-0.5,  0.5,  0.5), -- Left  top    front
          vec3(-0.5, -0.5, -0.5), -- Left  bottom back
          vec3( 0.5, -0.5, -0.5), -- Right bottom back
          vec3( 0.5,  0.5, -0.5), -- Right top    back
          vec3(-0.5,  0.5, -0.5), -- Left  top    back
        }
    
        -- now construct a cube out of the vertices above
        m.vertices = {
          -- Front
          vertices[1], vertices[2], vertices[3],
          vertices[1], vertices[3], vertices[4],
          -- Right
          vertices[2], vertices[6], vertices[7],
          vertices[2], vertices[7], vertices[3],
          -- Back
          vertices[6], vertices[5], vertices[8],
          vertices[6], vertices[8], vertices[7],
          -- Left
          vertices[5], vertices[1], vertices[4],
          vertices[5], vertices[4], vertices[8],
          -- Top
          vertices[4], vertices[3], vertices[7],
          vertices[4], vertices[7], vertices[8],
          -- Bottom
          vertices[5], vertices[6], vertices[2],
          vertices[5], vertices[2], vertices[1],
        }
    
        --now texture it
        -- all the unique texture positions needed
        local q=.03
        local texvertices = { vec2(q,q),
                              vec2(1-q,q),
                              vec2(q,1-q),
                              vec2(1-q,1-q) }
    
        -- apply the texture coordinates to each triangle
        m.texCoords = {
          -- Front
          texvertices[1], texvertices[2], texvertices[4],
          texvertices[1], texvertices[4], texvertices[3],
          -- Right
          texvertices[1], texvertices[2], texvertices[4],
          texvertices[1], texvertices[4], texvertices[3],
          -- Back
          texvertices[1], texvertices[2], texvertices[4],
          texvertices[1], texvertices[4], texvertices[3],
          -- Left
          texvertices[1], texvertices[2], texvertices[4],
          texvertices[1], texvertices[4], texvertices[3],
          -- Top
          texvertices[1], texvertices[2], texvertices[4],
          texvertices[1], texvertices[4], texvertices[3],
          -- Bottom
          texvertices[1], texvertices[2], texvertices[4],
          texvertices[1], texvertices[4], texvertices[3],
        } 
        return m
    end
    pointShader = {
    
    vertexShader = [[
    
    uniform mat4 modelViewProjection;
    uniform mat4 mModel; //matrix to convert from object to world space
    uniform vec4 directColor; 
    uniform vec4 directDirection; 
    
    attribute vec4 position;
    attribute vec4 color;
    attribute vec2 texCoord;
    attribute vec3 normal;
    
    varying lowp vec4 vColor;
    varying highp vec2 vTexCoord;
    varying vec4 vDirectDiffuse; 
    varying lowp vec4 vPosition; 
    varying lowp vec4 vNormal; 
    
    void main()
    {
        vColor = color;
        vTexCoord = texCoord;
        vPosition = mModel * position; 
        vNormal = mModel * vec4( normal, 0.0 );
        vec4 norm = normalize(vNormal); 
        vec4 d = normalize( directDirection - vPosition );
        vDirectDiffuse = directColor * max( 0.0, dot( norm, d ));   
        gl_Position = modelViewProjection * position;
    }
    
    ]],
    fragmentShader = [[
    
    precision highp float;
    
    uniform vec4 ambientLight; 
    uniform vec4 directDirection; 
    uniform float pointRange;  //--NEW
    uniform float reflect;
    uniform float shiny;
    uniform vec4 specularColor;
    uniform vec4 eyePosition;
    
    varying lowp vec4 vColor;
    varying highp vec2 vTexCoord;
    varying vec4 vDirectDiffuse; 
    varying lowp vec4 vPosition;
    varying lowp vec4 vNormal;
    
    vec4 normalizedNormal = normalize(vNormal);   
    
    vec4 GetSpecularColor(vec4 lightPosition)
    {
        vec4 lightDirection = normalize( lightPosition - vPosition );
        vec4 cameraDirection = normalize( eyePosition - vPosition );
        vec4 halfAngle = normalize( cameraDirection + lightDirection );
        vec4 specularColor = min(specularColor + 0.5, 1.0);
        float spec = pow( max( 0.0, dot( normalizedNormal, halfAngle)), 32.0 );
        return specularColor * spec;
    }  
    
    void main()
    {
        vec4 pixel = vColor;
        vec4 ambient = ambientLight;
        vec4 diffuse = vDirectDiffuse;
        vec4 specular = GetSpecularColor( directDirection );
        //--NEW now calculate attenuation
        float attenuation = max( 0.0, 1.0 - length( directDirection - vPosition ) / pointRange );
        vec4 totalColor = clamp(pixel * reflect * attenuation * (ambient + diffuse + specular * shiny),0.,1.);
        totalColor.a=1.;
        gl_FragColor=totalColor;
    }
    ]]
    }
    function CalculateNormals(vertices)
        local norm = {}
        for i=1, #vertices,3 do 
            local n = ((vertices[i+1] - vertices[i]):cross(vertices[i+2] - vertices[i])):normalize()
            norm[i] = n 
            norm[i+1] = n
            norm[i+2] = n
        end
    return norm
    end
    
  • edited February 2016 Posts: 8

    @Ignatz

    I copied the lighting code from your ebook!

  • IgnatzIgnatz Mod
    Posts: 5,396

    I thought I recognized it...

    Put three ~~~ on a blank line before and after your code to make it look nice, I fixed it above

  • I will do it...It's my first post,so

  • IgnatzIgnatz Mod
    Posts: 5,396

    It looks real nice, well done, I bet you're pleased with that! B)

  • thank you

  • dave1707dave1707 Mod
    Posts: 7,521

    I added the 3~'s just in case you wonder how they got there. Nice job with the program.

  • I was inspired by the recursive algorithm to solve the hanoi problem,and I extend it to solve all the conditions.

  • Jmv38Jmv38 Mod
    Posts: 3,295

    <3

  • Posts: 809

    Goodjob

  • edited February 2016 Posts: 8

    It would seem better if replace the cube with cylinder,but there are some troubles in dealing with the normals for me.And I'd like to add some arrows to indicate the connection between each pair of the pillars.

  • IgnatzIgnatz Mod
    edited February 2016 Posts: 5,396

    @yahuishuo - The normals are actually very easy for a cylinder.

    Calculate them in the vertex shader. Give it the bottom centre position, and then for any vertex, you calculate the centre point of the cylinder at the same height as the vertex.

    eg if the bottom centre is at (10,14,20), and the vertex is at (25,40,10), then the centre point at the same height is (10,40,20).

    The normal is then your vertex LESS this centre point, normalised (although there's no need to normalise because you will need to do it again in the fragment shader anyway).

Sign In or Register to comment.