var el,g,ratio,shapes,my={}
function init(mode,rel){var version='0.936'
this.mode=typeof mode!=='undefined'?mode:'asc'
rel=typeof rel!=='undefined'?rel:'../'
var w=510
var h=320
my.clrs=[["PaleGreen",'#98FB98'],["SpringGreen",'#00FF7F'],["Thistle",'#D8BFD8'],["Yellow",'#FFFF00'],["Gold",'#FFD700'],["Pink",'#FFC0CB'],["LightSalmon",'#FFA07A'],["Lime",'#00FF00'],["DarkSeaGreen",'#8FBC8F'],["Orange",'#FFA500'],["Khaki",'#F0E68C'],["Violet",'#EE82EE'],["Teal",'#008080'],["LightBlue",'#ADD8E6'],["SkyBlue",'#87CEEB'],["Blue",'#0000FF'],["Navy",'#000080'],["Purple",'#800080'],["Wheat",'#F5DEB3'],["Tan",'#D2B48C'],["AntiqueWhite",["SlateBlue",'#6A5ACD'],'#FAEBD7'],["Aquamarine",'#7FFFD4'],["Silver",'#C0C0C0']];my.startX=50
my.startY=200
my.diskHt=17
my.poleX=90
my.poleDist=160
my.poleY=240
my.drag={type:'block',q:false,n:0,hold:{x:0,y:0}}
my.moves=[]
var s=''
s+='
'
s+='
'
s+='Disks: '
s+=wrap({id:'num',tag:'out',style:'width:40px;'},'3')
s+='▼ '
s+='▲ '
s+='Moves: 0 '
s+='Restart '
s+='Log '
s+='Solve! '
s+='
'
s+=`
`
s+='
YO!
'
s+='
Well Done !
'
s+='
'
s+='
Minimum Moves: 7
'
s+=wrap({cls:'copyrt',style:'left:5px; bottom:3px'},`© 2021 MathsIsFun.com v${version}`)
s+='
'
docInsert(s)
el=document.getElementById('canvasId')
ratio=2
el.width=w*ratio
el.height=h*ratio
el.style.width=w+'px'
el.style.height=h+'px'
g=el.getContext('2d')
g.setTransform(ratio,0,0,ratio,0,0)
shapes=[]
my.poles=[]
this.moveN=0
this.my.diskTot=3
my.logPop=new Pop('logPop','','','','')
gameNew()
el.addEventListener('mousedown',mouseDown,false)
el.addEventListener('touchstart',touchStart,false)
el.addEventListener('mousemove',doPointer,false)}
function logOpen(){document.getElementById('log').value=my.log
my.logPop.open()}
function getNum(){return this.my.diskTot}
function numDn(){var num=getNum()
if(num>3){num--
chgNumPts(num)}}
function numUp(){var num=getNum()
if(num<8){num++
chgNumPts(num)}}
function chgNumPts(n){document.getElementById('num').innerHTML=n
this.my.diskTot=n
gameNew()}
function drawPoles(){for(var i=0;i=0;i--){p0.push(i)}
my.poles=[p0,[],[]]
disksMake()
disksPlace()
g.clearRect(0,0,g.canvas.width,g.canvas.height)
drawPoles()
successTest()
my.log=''
my.logStt=performance.now()
document.getElementById('info').innerHTML='Minimum Moves: '+((1<=0;i--){var disk=my.disks[i]
my.poles[disk.pole].unshift(i)
disk.polePos=my.poles[disk.pole].length}}
function touchStart(evt){var touch=evt.targetTouches[0]
evt.clientX=touch.clientX
evt.clientY=touch.clientY
evt.touchQ=true
mouseDown(evt)}
function touchMove(evt){var touch=evt.targetTouches[0]
evt.clientX=touch.clientX
evt.clientY=touch.clientY
evt.touchQ=true
mouseMove(evt)
evt.preventDefault()}
function touchEnd(evt){el.addEventListener('touchstart',touchStart,false)
window.removeEventListener('touchend',touchEnd,false)
if(my.drag.q){my.drag.q=false
my.disks[my.drag.n].hiliteQ=false
doDrop(my.drag.n)
my.drag.n=-1
window.removeEventListener('touchmove',touchMove,false)}}
function doPointer(e){var bRect=el.getBoundingClientRect()
var mouseX=(e.clientX-bRect.left)*(el.width/ratio/bRect.width)
var mouseY=(e.clientY-bRect.top)*(el.height/ratio/bRect.height)
var inQ=false
for(var i=0;i0){if(n==pole[0])return true}}
return false}
function hitTest(shape,mx,my){if(mxshape.x+shape.wd)return false
if(my>shape.y+shape.ht)return false
return true}
function doDrop(dropNo){var disk=my.disks[dropNo]
disk.hilite(false)
var p=Math.round((disk.x-my.poleX)/my.poleDist)
p=Math.max(0,Math.min(p,2))
if(p!=disk.pole){var okQ=false
var pole=my.poles[p]
if(pole.length==0){okQ=true}else{var top=pole[0]
console.log('doDrp',dropNo,top)
if(dropNomy.moves.length)return
my.frame++
if(my.frame>60){my.frame=0
var move=my.moves[my.moveNo]
var poleFr=my.poles[move[0]]
var diskFr=poleFr[0]
my.disks[diskFr].pole=move[1]
disksToPoles()
disksPlace(false)
my.moveNo++
moveNChg(1)}
if(my.moveNo1){hanoi(from,buf,to,nmv-1)
my.moves.push([from,to])
hanoi(buf,to,from,nmv-1)}else{my.moves.push([from,to])}}
class Pop{constructor(id,yesStr,yesFunc,noStr,noFunc){this.id=id
this.div=document.getElementById(this.id)
this.div.style='position:absolute; left:-450px; top:10px; width:auto; padding: 5px; border-radius: 9px; background-color: #88aaff; box-shadow: 10px 10px 5px 0px rgba(40,40,40,0.75); transition: all linear 0.3s; opacity:0; text-align: center; '
this.bodyDiv=document.createElement('div')
this.div.appendChild(this.bodyDiv)
var yesBtn=document.createElement('button')
this.div.appendChild(yesBtn)
if(yesStr.length<1){yesStr='✔'
yesBtn.style='font: 22px Arial;'}
yesBtn.innerHTML=yesStr
yesBtn.classList.add('togglebtn')
yesBtn.onclick=this.yes.bind(this)
if(false){var noBtn=document.createElement('button')
this.div.appendChild(noBtn)
if(noStr.length<1){noStr='✘'
noBtn.style='font: 22px Arial;'}
noBtn.innerHTML=noStr
noBtn.classList.add('togglebtn')
noBtn.onclick=this.no.bind(this)}
this.yesFunc=yesFunc
this.noFunc=noFunc
return this}
open(){var div=this.div
div.style.transitionDuration='0.3s'
div.style.opacity=1
div.style.zIndex=12
div.style.left=10+'px'}
yes(me){console.log('me',me)
var div=document.getElementById(this.id)
div.style.opacity=0
div.style.zIndex=1
div.style.left='-999px'
if(typeof this.yesFunc==='function'){this.yesFunc()}}
no(){console.log('Pop no')
var div=this.div
div.style.opacity=0
div.style.zIndex=1
div.style.left='-999px'
if(typeof this.noFunc==='function'){this.noFunc()}}
bodySet(s){this.bodyDiv.innerHTML=s
return s}}
class Disk{constructor(x,y,n){this.x=x
this.y=y
this.n=n
this.wd=(n+2)*my.diskHt
this.ht=my.diskHt
this.pad=4
this.pole=0
this.hiliteQ=false
var ratio=2
this.div=document.createElement('div')
this.div.style.position='absolute'
this.div.style.pointerEvents='none'
this.div.style.transitionDuration='0s'
document.getElementById('disks').appendChild(this.div)
this.elFG=document.createElement('canvas')
this.elFG.style.position='absolute'
this.div.appendChild(this.elFG)
var canWd=this.wd+this.pad*2
var canHt=this.ht+this.pad*2
this.elFG.width=canWd*ratio
this.elFG.height=canHt*ratio
this.elFG.style.width=canWd+'px'
this.elFG.style.height=canHt+'px'
this.elFG.style.zIndex=2
this.gFG=this.elFG.getContext('2d')
this.gFG.setTransform(ratio,0,0,ratio,0,0)
this.elBG=document.createElement('canvas')
this.elBG.style.position='absolute'
this.div.appendChild(this.elBG)
this.elBG.width=canWd*ratio
this.elBG.height=canHt*ratio
this.elBG.style.width=canWd+'px'
this.elBG.style.height=canHt+'px'
this.elBG.style.zIndex=1
this.gBG=this.elBG.getContext('2d')
this.gBG.setTransform(ratio,0,0,ratio,0,0)
this.moveMe(true)
this.drawMe()
return this}
removeMe(){this.elFG.parentNode.removeChild(this.elFG)
this.elBG.parentNode.removeChild(this.elBG)}
moveMe(fastQ=true){if(fastQ){this.div.style.transitionDuration='0s'}else{this.div.style.transitionDuration='0.8s'}
this.div.style.left=this.x-this.pad+'px'
this.div.style.top=this.y-this.pad+'px'}
drawMe(){console.log('drawMe',this.hiliteQ)
var g=this.gFG
g.clearRect(0,0,g.canvas.width,g.canvas.height)
if(this.hiliteQ){console.log('hilite',this)
g.strokeStyle='rgba(150, 150, 33, 1)'
g.lineWidth=1}else{g.strokeStyle='black'
g.lineWidth=1}
g.fillStyle=my.clrs[this.n][1]
g.beginPath()
g.roundRect(this.pad,this.pad,this.wd,this.ht,10)
g.closePath()
g.stroke()
g.fill()}
hilite(onQ){this.hiliteQ=onQ
this.drawMe()}}
function hex2rgba(hex,opacity){hex=hex.replace('#','')
var r=parseInt(hex.substring(0,2),16)
var g=parseInt(hex.substring(2,4),16)
var b=parseInt(hex.substring(4,6),16)
return 'rgba('+r+','+g+','+b+','+opacity+')'}
CanvasRenderingContext2D.prototype.roundRect=function(x,y,w,h,r){if(w<2*r)r=w/2
if(h<2*r)r=h/2
this.moveTo(x+r,y)
this.arcTo(x+w,y,x+w,y+h,r)
this.arcTo(x+w,y+h,x,y+h,r)
this.arcTo(x,y+h,x,y,r)
this.arcTo(x,y,x+w,y,r)
return this}
my.drag={n:0,onq:false,holdX:0,holdY:0}
class Mouse{constructor(el){console.log('new moose')
el.addEventListener('touchstart',this.onTouchStart.bind(this),false)
el.addEventListener('touchmove',this.onTouchMove.bind(this),false)
window.addEventListener('touchend',this.onTouchEnd.bind(this),false)
el.addEventListener('mousedown',this.onMouseDown.bind(this),false)
el.addEventListener('mousemove',this.onMouseMove.bind(this),false)
window.addEventListener('mouseup',this.onMouseUp.bind(this),false)
this.el=el
this.ratio=1}
onTouchStart(ev){console.log('onTouchStart',this)
let touch=ev.targetTouches[0]
ev.clientX=touch.clientX
ev.clientY=touch.clientY
ev.touchQ=true
this.onMouseDown(ev)}
onTouchMove(ev){let touch=ev.targetTouches[0]
ev.clientX=touch.clientX
ev.clientY=touch.clientY
ev.touchQ=true
this.onMouseMove(ev)}
onTouchEnd(ev){my.moose.onMouseUp(ev)}
onMouseDown(ev){document.getElementById('angA').focus()
let mouse=this.mousePos(ev)
console.log('moose doon',mouse.x,mouse.y,my.shapes)
my.drag.onQ=false
my.drag.n=this.hitFind(my.shapes,mouse)
if(my.drag.n>=0){console.log('drrragin!',my.drag.n)
let pt=my.shapes[my.drag.n]
my.drag.holdX=mouse.x-pt.x
my.drag.holdY=mouse.y-pt.y
my.shapes[my.drag.n].shadQ=true
my.drag.onQ=true}
ev.preventDefault()}
onMouseMove(ev){let mouse=this.mousePos(ev)
if(my.drag.onQ){let shape=my.shapes[my.drag.n]
let pt={x:mouse.x-my.drag.holdX,y:mouse.y-my.drag.holdY}
shape.x=pt.x
shape.y=pt.y
shape.div.style.left=pt.x+'px'
shape.div.style.top=pt.y+'px'
shape.div.style.filter='drop-shadow(3px 3px 3px #229)'}else{if(this.hitFind(my.shapes,mouse)>=0){document.body.style.cursor='pointer'}else{document.body.style.cursor='default'}}}
onMouseUp(){if(my.drag.onQ){my.shapes[my.drag.n].div.style.filter='none'
my.drag.onQ=false}
document.body.style.cursor='default'}
mousePos(ev){let bRect=this.el.getBoundingClientRect()
return{x:(ev.clientX-bRect.left)*(bRect.width/this.ratio/bRect.width),y:(ev.clientY-bRect.top)*(bRect.height/this.ratio/bRect.height),}}
hitFind(pts,mouse){for(let i=0;ishape.x+shape.wd)return false
if(mouse.y>shape.y+shape.ht)return false
return true}}
my.theme=localStorage.getItem('theme')
my.lineClr=my.theme=='dark'?'white':'black'
my.imgHome=(document.domain=='localhost'?'/mathsisfun':'')+'/numbers/images/'
my.opts={name:'user'}
function optGet(name){let val=localStorage.getItem(`mif.${name}`)
if(val==null)val=my.opts[name]
return val}
function optSet(name,val){localStorage.setItem(`mif.${name}`,val)
my.opts[name]=val}
function getJSQueryVar(varName,defaultVal){let scripts=document.getElementsByTagName('script')
let lastScript=scripts[scripts.length-1]
let scriptName=lastScript.src
let bits=scriptName.split('?')
if(bits.length<2)return defaultVal
let query=bits[1]
console.log('query: ',query)
let vars=query.split('&')
for(let i=0;i{if(cls.length==0)cls='btn'
return ''''
',edit:()=>' >'+txt+'',inp:()=>'>'+(lbl.length>0?'':''),out:()=>' >'+txt+''+(lbl.length>0?'':''),rad:()=>{let s=''
s+='>\n'
for(let i=0;i \n'
s+=''+opts[i][1]+' \n'}
s+=''
return s},sel:()=>{let s=''
s+='>\n'
for(let i=0;i'+opt.descr+'\n'}
s+=''
if(lbl.length>0)s+=''
return s},sld:()=>'>',}[tag]()||''
s+='\n'
return s.trim()}
init()