# 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

``````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.normals=CalculateNormals(m.vertices)
--user parameters and fixed light settings
SurfaceColour=color(223, 177, 177, 255)
AmbientColour=color(255, 255, 255, 255)
AmbientStrength=0.2
m:setColors(SurfaceColour)
AmbientColour.b*AmbientStrength)
DirectColour=color(255,0,0)
DirectStrength=0.8
DirectColour.b*DirectStrength)
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
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)
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:draw()
resetMatrix()
end
for i=1,3 do
translate(pillars[i].x,pillars[i].y,pillars[i].z)
scale(1,pillars.height,1)
m:draw()
resetMatrix()
end
translate(0,-.01,0)
scale(100,.02,100)
m:draw()
resetMatrix()
popMatrix()
end
function touched(touch)
if touch.state==BEGAN then
--animate()
end
end
``````
``````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

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;
}

]],

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
``````

@Ignatz

I copied the lighting code from your ebook!

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

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

thank you

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.

<3

Goodjob

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.

@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).