JS连连看源码完美注释版(推荐)


Posted in Javascript onDecember 09, 2013

闲来无事,也写一个javascript连连看,注释比较完整,想学的朋友可要看了。

连连看最难的部分应该是路径搜索,即鼠标点的两点之间看有无可通的路径。 看过有人写的递归写法,心里痒痒,就捉摸了一下,发现不用递归的情况下难度也不大。

路径搜索由简到难分析,先分析一条直线上是否可直线连通,再分析一条直线上的两点通过拐两个弯是否可通,最后分析不在一条直线上的情况.

在IE6, IE8, firefox3.0.3下测试过.

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>JS连连看源码完美注释版</title>
</head>
<style>
 table{
  border-collapse: collapse;
 }
 td{
  border: solid #ccc 1px;
  height: 36px;
  width: 36px;
  cursor: pointer;
 }
 td img{
   height: 30px;
   width: 30px;
   border: solid #fff 3px;
   /*
   filter: alpha(opacity=80);
   -moz-opacity: 0.8;
   opacity: 0.8;
   */
 }</style>
<script>
//以下部分为路径搜索算法部分,与表现层无关

//全局变量
var X = 16;//总行数
var Y = 14;//总列数
var types = 15;//图形种类
//布局矩阵
//为了算法方便,矩阵的第一行,第一列,最后一行,最后一列都标注为0,天然通路。
var arr = new Array(Y);
var tbl;//显示布局的table元素
var p1 = null;//搜索路径用的第1个点的坐标
var p2 = null;//搜索路径用的第2个点的坐标
var e1 = null;//第1个点对应的元素
var e2 = null;//第2个点对应的元素
//路径搜索,给出两个点,搜索出通路
//通路用可连通的点表示
function getPath(p1, p2){
 //开始搜索前对p1,p2排序,使p2尽可能的在p1的右下方。
 //这样做可以简化算法
 if(p1.x>p2.x){
  var t = p1; 
  p1 = p2;
  p2 = t; 
 }
 else if(p1.x==p2.x){
  if(p1.y>p2.y){
   var t = p1; 
   p1 = p2;
   p2 = t; 
  }
 }
 //通过分析连连看中两点之间的位置关系,逐步由简到难分析每一种类型
 //第一种类型, 两点是否在一条直线上,而且两点之间可直线连通
 if((onlineY(p1, p2)||onlineX(p1, p2)) && hasLine(p1, p2)){
  status = 'type 1';
  return [p1,p2];
 }
 //第二种类型, 如果两点中任何一个点被全包围,则不通。
 if( !isEmpty({x:p1.x, y:p1.y+1}) && !isEmpty({x:p1.x, y:p1.y-1}) && !isEmpty({x:p1.x-1, y:p1.y}) && !isEmpty({x:p1.x+1, y:p1.y}) ){
  status = 'type 2';
  return null;
 }
 if( !isEmpty({x:p2.x, y:p2.y+1}) && !isEmpty({x:p2.x, y:p2.y-1}) && !isEmpty({x:p2.x-1, y:p2.y}) && !isEmpty({x:p2.x+1, y:p2.y}) ){
  status = 'type 2';
  return null;
 }
 //第三种类型, 两点在一条直线上,但是不能直线连接
 var pt0, pt1, pt2, pt3;
 //如果都在x轴,则自左至右扫描可能的路径,
 //每次构造4个顶点pt0, pt1, pt2, pt3,然后看他们两两之间是否连通
 if(onlineX(p1, p2)){
  for(var i=0; i<Y; i++){
   if(i==p1.y){
    continue;
   }
   pt0 = p1;
   pt1 = {x: p1.x, y: i};
   pt2 = {x: p2.x, y: i};
   pt3 = p2;
   //如果顶点不为空,则该路不通。
   if(!isEmpty(pt1) || !isEmpty(pt2)){
    continue;
   }
   if( hasLine(pt0, pt1) && hasLine(pt1, pt2) && hasLine(pt2, pt3) ){
    status = '(x:' + pt0.x + ',y:' + pt0.y + ')' + ', (x:' + pt1.x + ',y:' + pt1.y + ')' + ', (x:' + pt2.x + ',y:' + pt2.y + ')' + ', (x:' + pt3.x + ',y:' + pt3.y + ')';
    return [pt0, pt1, pt2, pt3];
   }
  }
 }
 //如果都在y轴,则自上至下扫描可能的路径,
 //每次构造4个顶点pt0, pt1, pt2, pt3,然后看他们两两之间是否连通
 if(onlineY(p1, p2)){
  for(var j=0; j<X; j++){
   if(j==p1.x){
    continue; 
   }
   pt0 = p1;
   pt1 = {x:j, y:p1.y};
   pt2 = {x:j, y:p2.y};
   pt3 = p2;
   //如果顶点不为空,则该路不通。
   if(!isEmpty(pt1) || !isEmpty(pt2)){
    continue;
   }
   if( hasLine(pt0, pt1) && hasLine(pt1, pt2) && hasLine(pt2, pt3) ){
    status = '(x:' + pt0.x + ',y:' + pt0.y + ')' + ', (x:' + pt1.x + ',y:' + pt1.y + ')' + ', (x:' + pt2.x + ',y:' + pt2.y + ')' + ', (x:' + pt3.x + ',y:' + pt3.y + ')';
    return [pt0, pt1, pt2, pt3];
   }
  }
 }
 //第四种类型, 两点不在一条直线上。
 //先纵向扫描可能的路径
 //同样,每次构造4个顶点,看是否可通
 for(var k=0; k<Y; k++){
   pt0 = p1;
   pt1 = {x:p1.x, y:k};
   pt2 = {x:p2.x, y:k};
   pt3 = p2;
   status = '(x:' + pt0.x + ',y:' + pt0.y + ')' + ', (x:' + pt1.x + ',y:' + pt1.y + ')' + ', (x:' + pt2.x + ',y:' + pt2.y + ')' + ', (x:' + pt3.x + ',y:' + pt3.y + ')';
   //特殊情况,如果pt0和pt1重合
   if(equal(pt0,pt1)){
    //如果pt2不为空,则此路不通
    if(!isEmpty(pt2)){
     continue;
    }
    if( hasLine(pt1, pt2) && hasLine(pt2, pt3) ){
     return [pt1, pt2, pt3];
    }
    else{
     continue;
    }
   }
   //特殊情况,如果pt2和pt3重合
   else if(equal(pt2,pt3)){
    //如果pt1不为空,则此路不通
    if(!isEmpty(pt1)){
     continue;
    }
    if( hasLine(pt0, pt1) && hasLine(pt1, pt2) ){
     return [pt0, pt1, pt2];
    }
    else{
     continue;
    }
   }
   //如果pt1, pt2都不为空,则不通
   if(!isEmpty(pt1) || !isEmpty(pt2)){
    continue;
   }
   if( hasLine(pt0, pt1) && hasLine(pt1, pt2) && hasLine(pt2, pt3) ){
    return [pt0, pt1, pt2, pt3];
   }
 }
 //横向扫描可能的路径
 for(var k=0; k<X; k++){
   pt0 = p1;
   pt1 = {x:k, y:p1.y};
   pt2 = {x:k, y:p2.y};
   pt3 = p2;
   status = '(x:' + pt0.x + ',y:' + pt0.y + ')' + ', (x:' + pt1.x + ',y:' + pt1.y + ')' + ', (x:' + pt2.x + ',y:' + pt2.y + ')' + ', (x:' + pt3.x + ',y:' + pt3.y + ')';
   if(equal(pt0,pt1)){
    if(!isEmpty(pt2)){
     continue;
    }
    if( hasLine(pt1, pt2) && hasLine(pt2, pt3) ){
     return [pt1, pt2, pt3];
    }
   }
   if(equal(pt2,pt3)){
    if(!isEmpty(pt1)){
     continue;
    }
    if( hasLine(pt0, pt1) && hasLine(pt1, pt2) ){
     return [pt0, pt1, pt2];
    }
   }
   if(!isEmpty(pt1) || !isEmpty(pt2)){
    continue;
   }
   if( hasLine(pt0, pt1) && hasLine(pt1, pt2) && hasLine(pt2, pt3) ){
    return [pt0, pt1, pt2, pt3];
   }
 }
 //status='type4';
 return null;
 /********** end type 4 **************/
}
function equal(p1, p2){
 return ((p1.x==p2.x)&&(p1.y==p2.y));
}
function onlineX(p1, p2){
 return p1.y==p2.y;
}
function onlineY(p1, p2){
 return p1.x==p2.x; 
}
function isEmpty(p){
 return (arr[p.y][p.x]==0); 
}
function hasLine(p1, p2){
 if(p1.x==p2.x&&p1.y==p2.y){
  return true; 
 }
 if(onlineY(p1, p2)){
  var i = p1.y>p2.y?p2.y:p1.y;
  i = i+1;
  var max = p1.y>p2.y?p1.y:p2.y;
  for(; i<max; i++){
   var p = {x: p1.x, y: i};
   if(!isEmpty(p)){
    break
   }
  }
  if(i==max){
   return true;
  }
  return false;
 }
 else if(onlineX(p1, p2)){
  var j = p1.x>p2.x?p2.x:p1.x;
  j = j+1;
  var max = p1.x>p2.x?p1.x:p2.x;
  for(; j<max; j++){
   var p = {x: j, y: p1.y};
   if(!isEmpty(p)){
    break
   }
  }
  if(j==max){
   return true;
  }
  return false;
 }
}
//以下部分为表现层部分,包括绘图, 初始化矩阵, 绑定鼠标事件...
function $(id){return document.getElementById(id)}
var t1, t2;//测试用
//图片基路径
var IMG_PATH = 'https://3water.com';
//初始化
function init(){
 //构造图片库
 var imgs = new Array(30);
 for(var i=1; i<=30; i++){
  imgs[i] = 'r_' + i + '.gif';
 }
 tbl = $('tbl');
 //构造table
 for(var row=0;row<Y-2;row++){
  var tr=tbl.insertRow(-1);
  for(var col=0;col<X-2;col++) {
   var td=tr.insertCell(-1);
  }
 }
 //构造矩阵
 for(var i=0; i<Y; i++){
  arr[i] = new Array(X);
  for(var j=0; j<X; j++){
   arr[i][j] = 0;
  }
 }
 var total = (X-2)*(Y-2);
 var tmp = new Array(total);//产生随机位置用
 for(var i=0; i<total; i++){
  tmp[i] = 0;
 }
 for(var i=0; i<total; i++){
  if(tmp[i]==0){
   var t = Math.floor(Math.random()*types) + 1;
   tmp[i] = t;
   while(true){
    var c = Math.floor(Math.random()*(total-i)) + i;
    if(tmp[c]==0){
     tmp[c] = t;
     break;
    }
   }
  }
 }
 var c = 0;
 for(var i=1; i<Y-1; i++){
  for(var j=1; j<X-1; j++){
   arr[i][j] = tmp[c++];
   tbl.rows[i-1].cells[j-1].innerHTML = '<img src="' + IMG_PATH + imgs[arr[i][j]] + '" />';
  } 
 }
 //绑定鼠标事件
  var img1, img2;
 document.body.onclick = function(e){
  var el = document.all?event.srcElement:e.target;
  if(el.parentNode.tagName!='TD'){
   return;
  }
  if(!img1){
   img1 = el;
  }
  else{
   img2 = el;
  }
  el.style.border = 'solid #3399FF 3px';
  el = el.parentNode;
  if(el.innerHTML==''){
   p1 = p2 = e1 = e2 = null;
  }
  var r = el.parentNode.rowIndex +1;
  var c = el.cellIndex +1;
  if(p1==null){
   //el.childNodes[0].style.border = 'solid #ccc 3px';
   p1 = {x:c, y:r};
   e1 = el;
  }
  else{
   p2 = {x:c, y:r};
   e2 = el;
   if(!equal(p1, p2)&&e1.innerHTML==el.innerHTML){
    var path = getPath(p1, p2);
    if(path!=null){
     e1.innerHTML = e2.innerHTML = '';
     arr[p1.y][p1.x] = arr[p2.y][p2.x] = 0;
    }
   }
   if(t1){t1.style.backgroundColor = '';}
   t1 = e1;
   if(t2){t2.style.backgroundColor = '';}
   t2 = e2;
   img1.style.border = 'solid #fff 3px';
   img2.style.border = 'solid #fff 3px';
   p1 = p2 = e1 = e2 = img1 = img2 = null;
   t1.style.backgroundColor = t2.style.backgroundColor = 'lightpink';
  }
 }
}
</script>
<body onload="init();">
 js连连看完美注释版<br />
 <table id="tbl" cellspacing="0" cellpadding="0" border="1">
 </table>
</body>
</html>
Javascript 相关文章推荐
getElementById在任意一款浏览器中都可以用吗的疑问回复
May 13 Javascript
ExtJS[Desktop]实现图标换行示例代码
Nov 17 Javascript
原生javascript实现图片弹窗交互效果
Jan 12 Javascript
JS+CSS实现六级网站导航主菜单效果
Sep 28 Javascript
requireJS使用指南
Apr 27 Javascript
BootStrap智能表单实战系列(七)验证的支持
Jun 13 Javascript
jQuery滚动新闻实现代码
Jun 26 Javascript
vue路由嵌套的SPA实现步骤
Nov 06 Javascript
关于react中组件通信的几种方式详解
Dec 10 Javascript
Vue 父子组件的数据传递、修改和更新方法
Mar 01 Javascript
详解JavaScript中的函数、对象
Apr 01 Javascript
解决vue-router 嵌套路由没反应的问题
Sep 22 Javascript
解析Javascript中难以理解的11个问题
Dec 09 #Javascript
深入理解Javascript作用域与变量提升
Dec 09 #Javascript
Javascript全局变量var与不var的区别深入解析
Dec 09 #Javascript
jquery div拖动效果示例代码
Dec 08 #Javascript
jquery垂直公告滚动实现代码
Dec 08 #Javascript
jquery中交替点击事件toggle方法的使用示例
Dec 08 #Javascript
JavaScript 判断用户输入的邮箱及手机格式是否正确
Dec 08 #Javascript
You might like
php flv视频时间获取函数
2010/06/29 PHP
ThinkPHP入口文件设置及相关注意事项分析
2014/12/05 PHP
php实现简单的语法高亮函数实例分析
2015/04/27 PHP
PHP实现导出带样式的Excel
2016/08/28 PHP
PHP读取CSV大文件导入数据库的实例
2017/07/24 PHP
laravel在中间件内生成参数并且传递到控制器中的2种姿势
2019/10/15 PHP
安装PHP扩展时解压官方 tgz 文件后没有configure文件无法进行配置编译的问题
2020/08/26 PHP
XMLHTTPRequest的属性和方法简介
2010/11/23 Javascript
JQUERY对单选框(radio)操作的小例子
2013/04/25 Javascript
JavaScript实现简单的时钟实例代码
2013/11/23 Javascript
用jquery写的一个万年历(自写)
2014/01/20 Javascript
基于JavaScript实现表单密码的隐藏和显示出来
2016/03/02 Javascript
50 个 jQuery 插件可将你的网站带到另外一个高度
2016/04/26 Javascript
javascript之with的使用(阿里云、淘宝使用代码分析)
2016/10/11 Javascript
如何使用puppet替换文件中的string
2018/12/06 Javascript
Vue export import 导入导出的多种方式与区别介绍
2020/02/12 Javascript
[29:23]2014 DOTA2国际邀请赛中国区预选赛 LGD-GAMING VS CIS 第一场1
2014/05/23 DOTA
python 远程统计文件代码分享
2015/05/14 Python
Python max内置函数详细介绍
2016/11/17 Python
python登录并爬取淘宝信息代码示例
2017/12/09 Python
对python遍历文件夹中的所有jpg文件的实例详解
2018/12/08 Python
Python实现病毒仿真器的方法示例(附demo)
2020/02/19 Python
使用 Python 遍历目录树的方法
2020/02/29 Python
Python无头爬虫下载文件的实现
2020/04/02 Python
从Pytorch模型pth文件中读取参数成numpy矩阵的操作
2021/03/04 Python
canvas画图被放大且模糊的解决方法
2020/08/11 HTML / CSS
美国知名的旅游网站:OneTravel
2018/10/09 全球购物
捷克玩具商店:Bambule
2019/02/23 全球购物
100%羊绒:NakedCashmere
2020/08/26 全球购物
幼儿园秋游活动方案
2014/01/21 职场文书
运动会稿件50字
2014/02/17 职场文书
党员干部形式主义个人整改措施
2014/09/17 职场文书
离婚协议书范本2014
2014/10/27 职场文书
答谢酒会主持词
2015/07/02 职场文书
MySQL不使用order by实现排名的三种思路总结
2021/06/02 MySQL
Python使用mitmproxy工具监控手机 下载手机小视频
2022/04/18 Python