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 相关文章推荐
Js setInterval与setTimeout(定时执行与循环执行)的代码(可以传入参数)
Jun 11 Javascript
js封装的textarea操作方法集合(兼容很好)
Nov 16 Javascript
js 实现日期灵活格式化的小例子
Jul 14 Javascript
分享一个常用的javascript静态类
Dec 31 Javascript
JS实现控制表格内指定单元格内容对齐的方法
Mar 30 Javascript
js正则表达式验证邮件地址
Nov 12 Javascript
BootStrap的JS插件之轮播效果案例详解
May 16 Javascript
jquery插入兄弟节点的操作方法
Dec 07 Javascript
jQuery滚动插件scrollable.js用法分析
May 25 jQuery
jQuery 防止相同的事件快速重复触发方法
Feb 08 jQuery
VUE实现自身整体组件销毁的示例代码
Jan 13 Javascript
Kettle中使用JavaScrip调用jar包对文件内容进行MD5加密的操作方法
Sep 04 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仿QQ验证码的实例分析
2013/07/01 PHP
destoon出现验证码不显示时的紧急处理方法
2014/08/22 PHP
Domino中运用jQuery读取视图内容的方法
2009/10/21 Javascript
QUnit jQuery的TDD框架
2010/11/04 Javascript
关于IE BUG与字符串截取substr的解决办法
2013/04/10 Javascript
node中socket.io的事件使用详解
2014/12/15 Javascript
js实现背景图片感应鼠标变化的方法
2015/02/28 Javascript
jquery.map()方法的使用详解
2015/07/09 Javascript
Bootstrap布局组件应用实例讲解
2016/02/17 Javascript
详解JavaScript中的事件流和事件处理程序
2016/05/20 Javascript
深入理解angularjs过滤器
2016/05/25 Javascript
javascript中的后退和刷新实现方法
2016/11/10 Javascript
js实现楼层导航功能
2017/02/23 Javascript
Vue2.x中的父子组件相互通信的实现方法
2017/05/02 Javascript
bootstrap手风琴折叠示例代码分享
2017/05/22 Javascript
JS+HTML5 FileReader实现文件上传前本地预览功能
2020/03/27 Javascript
微信小程序实现上传图片功能
2018/05/28 Javascript
vue中轮训器的使用
2019/01/27 Javascript
react+redux仿微信聊天界面
2019/06/21 Javascript
python thread 并发且顺序运行示例
2009/04/09 Python
Python去掉字符串中空格的方法
2014/03/11 Python
详解Python的Django框架中的模版相关知识
2015/07/15 Python
Python打印“菱形”星号代码方法
2018/02/05 Python
python自制包并用pip免提交到pypi仅安装到本机【推荐】
2019/06/03 Python
PyQt5 如何让界面和逻辑分离的方法
2020/03/24 Python
Django DRF路由与扩展功能的实现
2020/06/03 Python
在python中使用pyspark读写Hive数据操作
2020/06/06 Python
python打开文件的方式有哪些
2020/06/29 Python
Ray-Ban雷朋西班牙官网:全球领先的太阳眼镜品牌
2018/11/28 全球购物
MaBelle玛贝尔香港官网:香港钻饰连锁店
2019/09/09 全球购物
会计与审计专业自荐信范文
2014/03/15 职场文书
戒毒悔改检讨书
2014/09/21 职场文书
党的群众路线教育实践活动对照检查材料范文
2014/09/24 职场文书
读书笔记格式
2015/07/02 职场文书
创业计划书之旅游网站
2019/09/06 职场文书
带你了解CSS基础知识,样式
2021/07/21 HTML / CSS