new file: Files/flashplayer_32_sa.exe new file: favicon.ico new file: globe.gif new file: imgs/download.png new file: imgs/zuck.jpg new file: index.html new file: other.ico new file: script.js new file: site.webmanifest new file: sitemap.html new file: styles/backround.css new file: styles/border.css new file: styles/fonts/Titillium_Web/OFL.txt new file: styles/fonts/Titillium_Web/TitilliumWeb-Black.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-Bold.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-BoldItalic.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-ExtraLight.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-ExtraLightItalic.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-Italic.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-Light.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-LightItalic.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-Regular.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-SemiBold.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-SemiBoldItalic.ttf new file: styles/fonts/webfontkit-20221027-163353/generator_config.txt new file: styles/fonts/webfontkit-20221027-163353/specimen_files/grid_12-825-55-15.css new file: styles/fonts/webfontkit-20221027-163353/specimen_files/specimen_stylesheet.css new file: styles/fonts/webfontkit-20221027-163353/stylesheet.css new file: styles/fonts/webfontkit-20221027-163353/titilliumweb-extralight-demo.html new file: styles/fonts/webfontkit-20221027-163353/titilliumweb-extralight-webfont.woff new file: styles/fonts/webfontkit-20221027-163353/titilliumweb-extralight-webfont.woff2 new file: styles/fonts/webfontkit-20221027-165950/generator_config.txt new file: styles/fonts/webfontkit-20221027-165950/specimen_files/grid_12-825-55-15.css new file: styles/fonts/webfontkit-20221027-165950/specimen_files/specimen_stylesheet.css new file: styles/fonts/webfontkit-20221027-165950/stylesheet.css new file: styles/fonts/webfontkit-20221027-165950/titilliumweb-bold-demo.html new file: styles/fonts/webfontkit-20221027-165950/titilliumweb-bold-webfont.woff new file: styles/fonts/webfontkit-20221027-165950/titilliumweb-bold-webfont.woff2 new file: styles/style.css new file: tools/2048/.gitignore new file: tools/2048/.jshintrc new file: tools/2048/CONTRIBUTING.md new file: tools/2048/LICENSE.txt new file: tools/2048/README.md new file: tools/2048/Rakefile new file: tools/2048/favicon.ico new file: tools/2048/index.html new file: tools/2048/js/animframe_polyfill.js new file: tools/2048/js/application.js new file: tools/2048/js/bind_polyfill.js new file: tools/2048/js/classlist_polyfill.js new file: tools/2048/js/game_manager.js new file: tools/2048/js/grid.js new file: tools/2048/js/html_actuator.js new file: tools/2048/js/keyboard_input_manager.js new file: tools/2048/js/local_storage_manager.js new file: tools/2048/js/tile.js new file: tools/2048/meta/apple-touch-icon.png new file: tools/webretro/cores/neocd_libretro.js new file: tools/webretro/cores/neocd_libretro.wasm new file: tools/webretro/cores/nestopia_libretro.js new file: tools/webretro/cores/nestopia_libretro.wasm new file: tools/webretro/cores/o2em_libretro.js new file: tools/webretro/cores/o2em_libretro.wasm new file: tools/webretro/cores/opera_libretro.js new file: tools/webretro/cores/opera_libretro.wasm
489 lines
20 KiB
JavaScript
489 lines
20 KiB
JavaScript
let w,h,ratio,g,my={}
|
|
function init(){let version='0.642'
|
|
my.nx=getJSQueryVar('nx',8)
|
|
my.ny=getJSQueryVar('ny',8)
|
|
let bdOffsetY=10
|
|
let playerColors=['Red','Blue']
|
|
my.hovered={x:0,y:0,onQ:false}
|
|
my.playerN=0
|
|
my.players=[{color:playerColors[0],ai:false,pieces:0,score:0},{color:playerColors[1],ai:true,pieces:0,score:0},]
|
|
my.currVal=0
|
|
w=360
|
|
h=540
|
|
my.tileSz=Math.min((w-bdOffsetY*2)/my.nx,(h-80)/my.ny,50)
|
|
my.bdWd=my.nx*my.tileSz
|
|
my.bdHt=my.ny*my.tileSz
|
|
console.log('my',my)
|
|
my.bd=new Board()
|
|
let s=''
|
|
my.sndHome=document.domain=='localhost'?'/mathsisfun/images/sounds/':'/images/sounds/'
|
|
s+='<audio id="sndwin" src="'+my.sndHome+'success.mp3" preload="auto"></audio>'
|
|
s+='<audio id="sndmove" src="'+my.sndHome+'click1.mp3" preload="auto"></audio>'
|
|
s+='<div style="position:relative; width:'+w+'px; margin:auto; display:block; font:14px Arial; border-radius:10px; background:'+my.style.bgClr+'; ">'
|
|
s+=optPopHTML()
|
|
s+='<div id="game-screen" style="font-size:24px; width:100%; height:100%;">'
|
|
s+='<div style="z-index:2; text-align:right; margin:5px;" >'
|
|
s+='<span id="undoN" style="font: bold 17px sans-serif; color:red;"></span>'
|
|
s+=' '
|
|
s+='<button id="prevBtn" type="button" class="btn" onclick="movePrev()"><</button>'
|
|
s+='<button id="nextBtn" type="button" class="btn" onclick="moveNext()">></button>'
|
|
s+=' '
|
|
s+='<button id="newBtn" type="button" class="btn" onclick="optpop()">New Game</button>'
|
|
s+=' '
|
|
s+=' '
|
|
my.soundQ=true
|
|
s+=soundBtnHTML()
|
|
s+='</div>'
|
|
s+='<div id="message" style="opacity: 1; transition: all 2s; font: 24px sans-serif; text-align:center;">Welcome</div>'
|
|
s+=wrap({tag:'out',style:'width:100px; border-radius:25px; padding:5px 5px;text-align:center; margin:auto;'},'<span id="player-0-pieces" style="color:'+playerColors[0]+';"></span>',' vs ','<span id="player-1-pieces" style="color:'+playerColors[1]+';"></span>')
|
|
s+='<div style="text-align:center;">'
|
|
s+='<canvas id="can" style="background:'+my.style.bdClr+'; box-shadow: 5px 6px 7px rgba(0,0,0,.5); border-radius:6px; border:3px outset '+my.style.bdEdgeClr+'; position:relative; text-align:center; margin:auto; transform:translateY('+bdOffsetY+'px);"></canvas>'
|
|
s+='</div>'
|
|
s+=wrap({cls:'copyrt',style:'left:5px; bottom:-12px'},`© 2021 MathsIsFun.com v${version}`)
|
|
s+='</div>'
|
|
s+='</div>'
|
|
docInsert(s)
|
|
resetBoard()
|
|
let el=document.getElementById('can')
|
|
ratio=3
|
|
el.width=my.bdWd*ratio
|
|
el.height=my.bdHt*ratio
|
|
el.style.width=my.bdWd+'px'
|
|
el.style.height=my.bdHt+'px'
|
|
g=el.getContext('2d')
|
|
g.setTransform(ratio,0,0,ratio,0,0)
|
|
el.onmousemove=function(evt){if(my.players[my.playerN].ai)return
|
|
let tile=toTileCoords(evt.offsetX,evt.offsetY)
|
|
if(tile.x!=my.hovered.x||tile.y!=my.hovered.y){my.hovered.x=tile.x
|
|
my.hovered.y=tile.y
|
|
my.hovered.onQ=true
|
|
my.hovered.clr=my.bd.isValidMove(my.hovered.x,my.hovered.y,my.playerN)?my.style.hoverClr:'transparent'
|
|
redraw()}}
|
|
el.onclick=function(evt){if(my.players[my.playerN].ai)return
|
|
pickTile(my.hovered.x,my.hovered.y)}
|
|
newGame()}
|
|
function resetBoard(){my.bd.reset()}
|
|
function optPopHTML(){let s=''
|
|
s+='<div id="optpop" style="position:absolute; left:-450px; top:10px; width:320px; padding: 5px; border-radius: 9px; background-color: #bcd; box-shadow: 10px 10px 5px 0px rgba(40,40,40,0.75); transition: all linear 0.3s; opacity:0; ">'
|
|
s+='<div id="optInside" style="margin: 15px 0 5px 0;">'
|
|
s+='</div>'
|
|
let lblStyle='display:inline-block; width:100px; text-align:right; margin-right:5px; font:18px Arial;'
|
|
s+='<div style="width:100%;">'
|
|
s+='<span style="'+lblStyle+' color:black;">Difficulty:</span>'
|
|
s+='<select id="difficulty" class="input"><option>Easy</option><option selected=true>Medium</option><option>Hard</option></select>'
|
|
s+='</div>'
|
|
s+='<br>'
|
|
s+='<div style="width:100%;">'
|
|
for(let i=0;i<my.players.length;i++){let p=my.players[i]
|
|
s+='<div style="color: '+p.color+';">'
|
|
s+='<span style="'+lblStyle+'">'+p.color+': </span>'
|
|
s+='<select id="player-'+i+'" class="input">'
|
|
if(i==0)s+='<option selected=true>Human</option><option>Computer</option>'
|
|
if(i==1)s+='<option>Human</option><option selected=true>Computer</option>'
|
|
s+='</select>'
|
|
s+='<span style="margin-left:15px; font:18px Arial; color: '+p.color+';" >Wins: '
|
|
s+='<span id="player-'+i+'-score" style="">0</span>'
|
|
s+='</span>'
|
|
s+='</div>'}
|
|
s+='</div>'
|
|
s+='<div style="float:right; margin: 0 0 5px 10px;">'
|
|
s+='<button id="optYes" onclick="optYes()" style="z-index:2; font: 22px Arial;" class="btn" >✔</button>'
|
|
s+='<button onclick="optNo()" style="z-index:2; font: 22px Arial;" class="btn" >✘</button>'
|
|
s+='</div>'
|
|
s+='</div>'
|
|
return s}
|
|
function optpop(){let pop=document.getElementById('optpop')
|
|
pop.style.transitionDuration='0.3s'
|
|
pop.style.opacity=1
|
|
pop.style.zIndex=12
|
|
pop.style.left=(w-320)/2+'px'}
|
|
function optYes(){let pop=document.getElementById('optpop')
|
|
pop.style.opacity=0
|
|
pop.style.zIndex=1
|
|
pop.style.left='-999px'
|
|
newGame()}
|
|
function optNo(){let pop=document.getElementById('optpop')
|
|
pop.style.opacity=0
|
|
pop.style.zIndex=1
|
|
pop.style.left='-999px'}
|
|
function updatePiecesCount(){let tiles=my.bd.getTiles()
|
|
my.players[0].pieces=0
|
|
my.players[1].pieces=0
|
|
for(let y=0;y<my.nx;y++){for(let x=0;x<my.ny;x++){let player=tiles[y][x][0]
|
|
if(player<0)continue
|
|
my.players[player].pieces++}}
|
|
pieceCountShow()}
|
|
function pickTile(row,col){let flipN=my.bd.makeMove(row,col,my.playerN)
|
|
if(flipN>0){if(my.soundQ)document.getElementById('sndmove').play()
|
|
endTurn()}
|
|
my.hovered.onQ=false
|
|
redraw()}
|
|
function tie(){msg('Tied Game!')
|
|
let half=(my.nx*my.ny)/2
|
|
my.players[0].pieces=half
|
|
my.players[1].pieces=half
|
|
pieceCountShow()}
|
|
function win(player){msg(my.players[player].color+' Wins!')
|
|
let pieceTot=my.players[0].pieces+my.players[1].pieces
|
|
let emptyCount=my.nx*my.ny-pieceTot
|
|
my.players[player].pieces+=emptyCount
|
|
pieceCountShow()
|
|
my.players[player].score++
|
|
document.getElementById('player-0-score').innerHTML=my.players[0].score
|
|
document.getElementById('player-1-score').innerHTML=my.players[1].score}
|
|
function pieceCountShow(){document.getElementById('player-0-pieces').innerHTML=my.players[0].pieces
|
|
document.getElementById('player-1-pieces').innerHTML=my.players[1].pieces}
|
|
function msg(s){let msgDiv=document.getElementById('message')
|
|
msgDiv.innerHTML=s}
|
|
function endTurn(){updatePiecesCount()
|
|
my.playerN=my.playerN==0?1:0
|
|
msg(my.players[my.playerN].color+"'s turn",0)
|
|
if(!my.bd.canMakeMove(my.playerN)){msg(my.players[my.playerN].color+' cannot move, loses turn',0)
|
|
my.playerN=my.playerN==0?1:0}
|
|
if(!my.bd.canMakeMove(0)&&!my.bd.canMakeMove(1)){if(my.players[0].pieces==my.players[1].pieces){tie()}else{win(my.players[0].pieces>my.players[1].pieces?0:1)}}
|
|
if(my.players[my.playerN].ai){setTimeout(aiTurn,1200)}}
|
|
function aiTurn(){let res=my.bd.makeBestMove(my.difficulty,my.playerN)
|
|
my.prevBd=my.bd.bd
|
|
console.log('aiTurn',res)
|
|
if(res.n>0){if(my.soundQ)document.getElementById('sndmove').play()
|
|
endTurn()}
|
|
redraw()
|
|
g.strokeStyle=my.style.moveHi
|
|
g.lineWidth=3
|
|
g.strokeRect(res.i*my.tileSz,res.j*my.tileSz,my.tileSz,my.tileSz)
|
|
setTimeout(redraw,2000)}
|
|
function toTileCoords(x,y){x=Math.min(my.nx-1,Math.max(0,Math.floor(x/my.tileSz)))
|
|
y=Math.min(my.ny-1,Math.max(0,Math.floor(y/my.tileSz)))
|
|
return{x:x,y:y,}}
|
|
function newGame(){resetBoard()
|
|
my.difficulty=document.getElementById('difficulty').selectedIndex+1
|
|
console.log('my.difficulty',my.difficulty)
|
|
let player0=document.getElementById('player-0')
|
|
my.players[0].ai=player0.options[player0.selectedIndex].text=='Computer'
|
|
let player1=document.getElementById('player-1')
|
|
my.players[1].ai=player1.options[player1.selectedIndex].text=='Computer'
|
|
updatePiecesCount()
|
|
redraw()
|
|
msg(my.players[my.playerN].color+"'s turn",0)
|
|
if(my.players[my.playerN].ai)aiTurn()}
|
|
function drawGrid(){g.lineWidth=1
|
|
let clrs=my.style.edgeClrs
|
|
for(let j=0;j<clrs.length;j++){let gap=(j-0.5)*1
|
|
g.strokeStyle=clrs[j]
|
|
for(let y=0;y<my.ny;y++){let yp=y*my.tileSz
|
|
g.beginPath()
|
|
g.moveTo(0,yp+gap)
|
|
g.lineTo(my.nx*my.tileSz,yp+gap)
|
|
g.stroke()}
|
|
for(let x=0;x<my.nx;x++){let xp=x*my.tileSz
|
|
g.beginPath()
|
|
g.moveTo(xp+gap,0)
|
|
g.lineTo(xp+gap,my.ny*my.tileSz)
|
|
g.stroke()}}}
|
|
function drawDisks(){let tiles=my.bd.getTiles()
|
|
for(let x=0;x<my.nx;x++){for(let y=0;y<my.ny;y++){let px=x*my.tileSz+my.tileSz/2
|
|
let py=y*my.tileSz+my.tileSz/2
|
|
let r=my.tileSz*0.4
|
|
let player=tiles[x][y][0]
|
|
if(player<0)continue
|
|
let c=my.players[player].color
|
|
drawDisk(px,py,r*0.9,c)}}}
|
|
function drawDisk(x,y,r,color){let grd1=g.createLinearGradient(x-r,y-r,x+r,y+r)
|
|
grd1.addColorStop(0,color)
|
|
grd1.addColorStop(1,'#ccc')
|
|
let grd2=g.createLinearGradient(x-r,y-r,x+r,y+r)
|
|
grd2.addColorStop(0,'#ccc')
|
|
grd2.addColorStop(1,color)
|
|
g.fillStyle=grd2
|
|
g.beginPath()
|
|
g.arc(x,y,r+r*0.2,0,Math.PI*2)
|
|
g.fill()
|
|
g.closePath()
|
|
g.fillStyle=grd1
|
|
g.beginPath()
|
|
g.arc(x,y,r,0,Math.PI*2)
|
|
g.fill()
|
|
g.closePath()}
|
|
function drawCircle(x,y,r,fill,stroke){g.fillStyle=fill||'transparent'
|
|
g.lineWidth=1
|
|
g.strokeStyle=stroke||'transparent'
|
|
g.beginPath()
|
|
g.arc(x,y,r,0,Math.PI*2,true)
|
|
g.fill()
|
|
g.stroke()
|
|
g.closePath()}
|
|
function drawHovered(){if(my.hovered.onQ){g.strokeStyle=my.hovered.clr
|
|
g.lineWidth=3
|
|
g.strokeRect(my.hovered.x*my.tileSz,my.hovered.y*my.tileSz,my.tileSz,my.tileSz)}}
|
|
function redraw(){g.clearRect(0,0,g.canvas.width,g.canvas.height)
|
|
document.getElementById('undoN').innerHTML=my.bd.undoN==0?'':'Undo: '+my.bd.undoN
|
|
drawGrid()
|
|
drawDisks()
|
|
if(my.players[my.playerN].ai)return
|
|
drawHovered()}
|
|
function movePrev(){my.bd.movePrev()}
|
|
function moveNext(){my.bd.moveNext()}
|
|
class Board{constructor(){this.hist=[]
|
|
this.Fld=new Array(my.nx)
|
|
this.FVal=new Array(my.nx)
|
|
this.moveCount=0
|
|
this.maxMoveCount=0
|
|
this.undoN=0
|
|
this.best_i
|
|
this.best_j
|
|
this.init()
|
|
this.aNewGame(1)}
|
|
movePrev(){console.log('movePrev',this.moveCount,this.maxMoveCount)
|
|
if(this.moveCount>0){this.moveCount--
|
|
let bd=this.hist[this.moveCount].bd
|
|
for(let x=0;x<my.nx;x++){for(let y=0;y<my.ny;y++){this.Fld[x][y][0]=bd[x][y]}}
|
|
my.playerN=my.playerN==0?1:0
|
|
this.undoN++}
|
|
redraw()}
|
|
moveNext(){console.log('moveNext')
|
|
if(this.moveCount<this.maxMoveCount){this.moveCount++
|
|
let bd=this.hist[this.moveCount].bd
|
|
for(let x=0;x<my.nx;x++){for(let y=0;y<my.ny;y++){this.Fld[x][y][0]=bd[x][y]}}
|
|
my.playerN=my.playerN==0?1:0
|
|
this.undoN--}
|
|
redraw()}
|
|
reset(){this.aNewGame(0)}
|
|
getTiles(){return this.Fld}
|
|
makeMove(x,y,player){return this.makeRealMove(x,y,player)}
|
|
makeBestMove(difficulty,player){return this.doBestMove(difficulty,player)}
|
|
isValidMove(x,y,player){return this.isValidMove2(x,y,0,player)}
|
|
isValidMove2(nn,mm,ll,p){let player=p
|
|
if(!this.Fld[nn])return
|
|
if(!this.Fld[nn][mm])return
|
|
if(this.Fld[nn][mm][ll]>-1){return false}
|
|
for(let ddx=-1;ddx<=1;ddx++){for(let ddy=-1;ddy<=1;ddy++){if(this.getFld(nn+ddx,mm+ddy,ll)==1-player){let cc=0,dd=1
|
|
do{dd++
|
|
cc=this.getFld(nn+dd*ddx,mm+dd*ddy,ll)}while(cc==1-player)
|
|
if(cc==player){return true}}}}
|
|
return false}
|
|
canMakeMove(player){return this.able2Move(player)}
|
|
init(){for(let i=0;i<my.nx;i++){this.Fld[i]=new Array(my.ny)
|
|
for(let j=0;j<my.ny;j++){this.Fld[i][j]=new Array(4)}}
|
|
for(let i=0;i<my.nx;i++){this.FVal[i]=new Array(my.ny)
|
|
for(let j=0;j<my.ny;j++){this.FVal[i][j]=5}}
|
|
let EdgeValArr=[20,-15,10]
|
|
for(let e=0;e<3;e++){let EdgeVal=EdgeValArr[e]
|
|
for(let i=0;i<my.nx;i++){this.FVal[i][e]+=EdgeVal
|
|
this.FVal[i][my.ny-1-e]+=EdgeVal}
|
|
for(let i=0;i<my.ny;i++){this.FVal[e][i]+=EdgeVal
|
|
this.FVal[my.nx-1-e][i]+=EdgeVal}}
|
|
this.FVal[0][0]+=80
|
|
this.FVal[my.nx-1][0]+=80
|
|
this.FVal[0][my.ny-1]+=80
|
|
this.FVal[my.nx-1][my.ny-1]+=80
|
|
this.FVal[1][1]-=40
|
|
this.FVal[my.nx-2][1]-=40
|
|
this.FVal[1][my.ny-2]-=40
|
|
this.FVal[my.nx-2][my.ny-2]-=40}
|
|
aNewGame(StartPlayer){this.moveCount=0
|
|
this.maxMoveCount=0
|
|
this.undoN=0
|
|
for(let i=0;i<my.nx;i++){for(let j=0;j<my.ny;j++){this.Fld[i][j][0]=-1}}
|
|
let p0=StartPlayer
|
|
let p1=1-StartPlayer
|
|
let mynx_2=my.nx/2
|
|
let myny_2=my.ny/2
|
|
let stts=[[mynx_2,myny_2,p0],[mynx_2-1,myny_2-1,p0],[mynx_2-1,myny_2,p1],[mynx_2,myny_2-1,p1],]
|
|
console.log('stts',stts)
|
|
for(let i=0;i<stts.length;i++){let stt=stts[i]
|
|
this.Fld[stt[0]][stt[1]][0]=stt[2]}
|
|
this.histUpdate(StartPlayer,0,0)}
|
|
getFld(nn,mm,ll){if(nn<0)return-1
|
|
if(nn>=my.nx)return-1
|
|
if(mm<0)return-1
|
|
if(mm>=my.ny)return-1
|
|
return this.Fld[nn][mm][ll]}
|
|
makeRealMove(nn,mm,p){console.log('makeRealMove',nn,mm,p)
|
|
let player=p
|
|
if(this.Fld[nn][mm][0]>-1){return 0}
|
|
let flipN=0
|
|
let hh=new Array(9)
|
|
let ll=-1
|
|
for(let ddx=-1;ddx<=1;ddx++){for(let ddy=-1;ddy<=1;ddy++){ll++
|
|
hh[ll]=0
|
|
if(this.getFld(nn+ddx,mm+ddy,0)==1-player){let cc
|
|
let dd=1
|
|
do{dd++
|
|
cc=this.getFld(nn+dd*ddx,mm+dd*ddy,0)}while(cc==1-player)
|
|
if(cc==player){hh[ll]=dd
|
|
do{dd--
|
|
this.Fld[nn+dd*ddx][mm+dd*ddy][0]=player
|
|
flipN++}while(dd>1)}}}}
|
|
if(flipN==0)return 0
|
|
this.Fld[nn][mm][0]=player
|
|
this.moveCount++
|
|
this.maxMoveCount=this.moveCount
|
|
this.histUpdate(player,nn,mm)
|
|
return flipN}
|
|
histUpdate(player,nn,mm){if(this.hist.length<=this.moveCount)this.hist[this.moveCount]=[]
|
|
let clone=[]
|
|
for(let x=0;x<my.nx;x++){let row=[]
|
|
for(let y=0;y<my.ny;y++){row.push(this.Fld[x][y][0])}
|
|
clone.push(row)}
|
|
this.hist[this.moveCount]={player:player,nn:nn,mm:mm,bd:clone}}
|
|
histShow(){let s=''
|
|
for(let i=0;i<this.hist.length;i++){let h=this.hist[i]
|
|
s+=my.players[h.player].color
|
|
s+=h.nn
|
|
s+=h.mm
|
|
for(let x=0;x<my.nx;x++){for(let y=0;y<my.ny;y++){if(h.bd[x][y]!=-1)s+=' '+h.bd[x][y]}}
|
|
s+=', '}
|
|
console.log('histShow',s)}
|
|
makeVirtualMove(nn,mm,rr,p){let player=p
|
|
let ddx,ddy,dd,cc,flipN=0
|
|
my.currVal=0
|
|
let MoveVal=1
|
|
if(rr>0){for(ddx=0;ddx<my.nx;ddx++){for(ddy=0;ddy<my.ny;ddy++){this.Fld[ddx][ddy][rr]=this.Fld[ddx][ddy][rr-1]}}}
|
|
if(this.Fld[nn][mm][rr]>-1){return 0}
|
|
for(ddx=-1;ddx<=1;ddx++){for(ddy=-1;ddy<=1;ddy++){if(this.getFld(nn+ddx,mm+ddy,rr)==1-player){dd=1
|
|
do{dd++
|
|
cc=this.getFld(nn+dd*ddx,mm+dd*ddy,rr)}while(cc==1-player)
|
|
if(cc==player){do{dd--
|
|
this.Fld[nn+dd*ddx][mm+dd*ddy][rr]=player
|
|
if(rr>0){my.currVal+=this.FVal[nn+dd*ddx][mm+dd*ddy]+MoveVal}
|
|
flipN++}while(dd>1)}}}}
|
|
if(flipN>0){this.Fld[nn][mm][rr]=player}
|
|
return flipN}
|
|
doBestMove(difficulty,p){let player=p
|
|
let D=difficulty
|
|
if(this.moveCount>=my.nx*my.ny-10){D++}
|
|
D=Math.min(D++,2)
|
|
this.getBestMove(0,D,player)
|
|
let flipN=this.makeRealMove(this.best_i,this.best_j,player)
|
|
console.log('doBestMove',difficulty,p,flipN)
|
|
return{i:this.best_i,j:this.best_j,n:flipN}}
|
|
getBestMove(depth,ll,p){let player=p
|
|
let best_val=-10000
|
|
let nvalid=0
|
|
let rr=depth+1
|
|
let MoveVal=1
|
|
let act_val=0
|
|
if(this.moveCount>40){MoveVal=this.moveCount-40}
|
|
for(let i=0;i<my.nx;i++){for(let j=0;j<my.ny;j++){if(this.makeVirtualMove(i,j,rr,player)>0){nvalid++
|
|
act_val=(5+rr)*(my.currVal+this.FVal[i][j]+MoveVal)
|
|
if(rr<ll){player=1-player
|
|
act_val-=this.getBestMove(rr,ll,player)
|
|
player=1-player}
|
|
if(rr==1){act_val+=Math.round(Math.random()*1000)%my.ny}
|
|
if(best_val<act_val){best_val=act_val
|
|
if(rr==1){this.best_i=i
|
|
this.best_j=j}}}}}
|
|
if(nvalid>0){return best_val+10*nvalid}else{return 0}}
|
|
bdDebug(){let s=''
|
|
for(let j=0;j<my.ny;j++){for(let i=0;i<my.nx;i++){let v=this.Fld[i][j][0]
|
|
if(v==-1){s+='-'}else{s+=v}
|
|
s+=' '}
|
|
s+='<br>'}
|
|
document.getElementById('debug').innerHTML=s}
|
|
able2Move(player){for(let i=0;i<my.nx;i++){for(let j=0;j<my.ny;j++){if(this.isValidMove(i,j,player)){return true}}}
|
|
return false}}
|
|
function soundBtnHTML(){let s=''
|
|
s+='<style> '
|
|
s+=' .speaker { height: 30px; width: 30px; position: relative; overflow: hidden; display: inline-block; vertical-align:top; } '
|
|
s+=' .speaker span { display: block; width: 9px; height: 9px; background-color: blue; margin: 10px 0 0 1px; }'
|
|
s+=' .speaker span:after { content: ""; position: absolute; width: 0; height: 0; border-style: solid; border-color: transparent blue transparent transparent; border-width: 10px 16px 10px 15px; left: -13px; top: 5px; }'
|
|
s+=' .speaker span:before { transform: rotate(45deg); border-radius: 0 60px 0 0; content: ""; position: absolute; width: 5px; height: 5px; border-style: double; border-color: blue; border-width: 7px 7px 0 0; left: 18px; top: 9px; transition: all 0.2s ease-out; }'
|
|
s+=' .speaker:hover span:before { transform: scale(.8) translate(-3px, 0) rotate(42deg); }'
|
|
s+=' .speaker.mute span:before { transform: scale(.5) translate(-15px, 0) rotate(36deg); opacity: 0; }'
|
|
s+=' </style>'
|
|
s+='<div id="sound" onClick="soundToggle()" class="speaker"><span></span></div>'
|
|
return s}
|
|
function soundToggle(){let btn='sound'
|
|
if(my.soundQ){my.soundQ=false
|
|
document.getElementById(btn).classList.add('mute')}else{my.soundQ=true
|
|
document.getElementById(btn).classList.remove('mute')}}
|
|
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<vars.length;i++){let pair=vars[i].split('=')
|
|
if(pair[0]==varName){return pair[1]}}
|
|
return defaultVal}
|
|
themeChg()
|
|
window.addEventListener('storage',themeChg)
|
|
function themeChg(){my.theme=localStorage.getItem('theme')
|
|
console.log('themeChg to',my.theme)
|
|
my.styles=[{bgClr:'hsla(240,40%,95%,1)',bdClr:'lightblue',bdEdgeClr:'#9ac',hoverClr:'#fff',moveHi:'yellow',edgeClrs:['#eee','#888']},{bgClr:'#cdf',bdClr:'#def',bdEdgeClr:'#8ac',hoverClr:'#880',moveHi:'#f00',edgeClrs:['#eee','#888']},{bgClr:'linear-gradient(rgb(62, 169, 255), rgb(16, 16, 16))',bdClr:'#65926d',bdEdgeClr:'#886850',hoverClr:'#880',moveHi:'#f00',edgeClrs:['#eee','#888']},{bgClr:'transparent',bdClr:'hsla(240,20%,20%,1)',bdEdgeClr:'black',hoverClr:'gold',moveHi:'#f00',edgeClrs:['#569','#000']},]
|
|
if(my.theme=='dark'){my.style=my.styles[3]}else{my.style=my.styles[0]}
|
|
console.log('my.style',my.style)}
|
|
function docInsert(s){let div=document.createElement('div')
|
|
div.innerHTML=s
|
|
let script=document.currentScript
|
|
script.parentElement.insertBefore(div,script)}
|
|
class Can{constructor(id,wd,ht,ratio){this.id=id
|
|
this.wd=wd
|
|
this.ht=ht
|
|
this.ratio=ratio
|
|
let el=document.getElementById(id)
|
|
el.width=wd*ratio
|
|
el.style.width=wd+'px'
|
|
el.height=ht*ratio
|
|
el.style.height=ht+'px'
|
|
this.g=el.getContext('2d')
|
|
this.g.setTransform(ratio,0,0,ratio,0,0)
|
|
this.el=el
|
|
return this}
|
|
clear(){this.g.clearRect(0,0,this.wd,this.ht)}
|
|
mousePos(ev){let bRect=this.el.getBoundingClientRect()
|
|
let mouseX=(ev.clientX-bRect.left)*(this.el.width/this.ratio/bRect.width)
|
|
let mouseY=(ev.clientY-bRect.top)*(this.el.height/this.ratio/bRect.height)
|
|
return[mouseX,mouseY]}}
|
|
function wrap({id='',cls='',pos='rel',style='',txt='',tag='div',lbl='',fn='',opts=[]},...mores){let s=''
|
|
s+='\n'
|
|
txt+=mores.join('')
|
|
s+={btn:()=>{if(cls.length==0)cls='btn'
|
|
return '<button onclick="'+fn+'"'},can:()=>'<canvas',div:()=>'<div',edit:()=>'<textarea onkeyup="'+fn+'" onchange="'+fn+'"',inp:()=>{if(cls.length==0)cls='input'
|
|
let s=''
|
|
s+=lbl.length>0?'<label class="label">'+lbl+' ':''
|
|
s+='<input value="'+txt+'"'
|
|
s+=fn.length>0?' oninput="'+fn+'" onchange="'+fn+'"':''
|
|
return s},out:()=>{if(cls.length==0)cls='output'
|
|
let s=''
|
|
s+=lbl.length>0?'<label class="label">'+lbl+' ':''
|
|
s+='<div '
|
|
return s},rad:()=>{if(cls.length==0)cls='radio'
|
|
return '<form'+(fn.length>0?(s+=' onclick="'+fn+'"'):'')},sel:()=>{if(cls.length==0)cls='select'
|
|
let s=''
|
|
s+=lbl.length>0?'<label class="label">'+lbl+' ':''
|
|
s+='<select '
|
|
s+=fn.length>0?' onchange="'+fn+'"':''
|
|
return s},sld:()=>'<input type="range" '+txt+' oninput="'+fn+'" onchange="'+fn+'"',}[tag]()||''
|
|
if(id.length>0)s+=' id="'+id+'"'
|
|
if(cls.length>0)s+=' class="'+cls+'"'
|
|
if(pos=='dib')s+=' style="position:relative; display:inline-block;'+style+'"'
|
|
if(pos=='rel')s+=' style="position:relative; '+style+'"'
|
|
if(pos=='abs')s+=' style="position:absolute; '+style+'"'
|
|
s+={btn:()=>'>'+txt+'</button>',can:()=>'></canvas>',div:()=>' >'+txt+'</div>',edit:()=>' >'+txt+'</textarea>',inp:()=>'>'+(lbl.length>0?'</label>':''),out:()=>' >'+txt+'</div>'+(lbl.length>0?'</label>':''),rad:()=>{let s=''
|
|
s+='>\n'
|
|
for(let i=0;i<opts.length;i++){let chk=''
|
|
if(i==0)chk='checked'
|
|
s+='<input type="radio" id="r'+i+'" name="typ" style="cursor:pointer;" value="'+opts[i][0]+'" '+chk+' />\n'
|
|
s+='<label for="r'+i+'" style="cursor:pointer;">'+opts[i][1]+'</label><br/>\n'}
|
|
s+='</form>'
|
|
return s},sel:()=>{let s=''
|
|
s+='>\n'
|
|
for(let i=0;i<opts.length;i++){let opt=opts[i]
|
|
let idStr=id+i
|
|
let chkStr=opt.descr==txt?' selected ':''
|
|
s+='<option id="'+idStr+'" value="'+opt.name+'"'+chkStr+'>'+opt.descr+'</option>\n'}
|
|
s+='</select>'
|
|
if(lbl.length>0)s+='</label>'
|
|
return s},sld:()=>'>',}[tag]()||''
|
|
s+='\n'
|
|
return s.trim()}
|
|
init() |