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 相关文章推荐
更换select下拉菜单背景样式的实现代码
Dec 20 Javascript
jQuery仿Excel表格编辑功能的实现代码
May 01 Javascript
什么是cookie?js手动创建和存储cookie
May 27 Javascript
JavaScript indexOf方法入门实例(计算指定字符在字符串中首次出现的位置)
Oct 17 Javascript
jQuery中map()方法用法实例
Jan 06 Javascript
require.js的用法详解
Oct 20 Javascript
基于node.js依赖express解析post请求四种数据格式
Feb 13 Javascript
原JS实现banner图的常用功能
Jun 12 Javascript
JS 中LocalStorage和SessionStorage的使用
Aug 17 Javascript
angular6 填坑之sdk的方法
Dec 27 Javascript
JavaScript数组去重的方法总结【12种方法,号称史上最全】
Feb 28 Javascript
vue首次渲染全过程
Apr 21 Vue.js
解析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
对javascript和select部件的结合运用
2006/10/09 PHP
php 表单验证实现代码
2009/03/10 PHP
在windows服务器开启php的gd库phpinfo中未发现
2013/01/13 PHP
thinkphp普通查询与表达式查询实例分析
2014/11/24 PHP
PHP+MySQL修改记录的方法
2015/01/21 PHP
php源码分析之DZX1.5加密解密函数authcode用法
2015/06/17 PHP
php使用QueryList轻松采集js动态渲染页面方法
2018/09/11 PHP
php面向对象程序设计中self与static的区别分析
2019/05/21 PHP
完美解决JS中汉字显示乱码问题(已解决)
2006/12/27 Javascript
学习YUI.Ext 第七天--关于View&amp;JSONView
2007/03/10 Javascript
JavaScript 空位补零实现代码
2010/02/26 Javascript
jquery tab插件制作实现代码
2010/06/22 Javascript
jQuery find和children方法使用
2011/01/31 Javascript
Jquery封装tab自动切换效果的具体实现
2013/07/13 Javascript
网页下载文件期间如何防止用户对网页进行其他操作
2014/06/27 Javascript
使用nodejs开发cli项目实例
2015/06/03 NodeJs
jQuery实现动态表单验证时文本框抖动效果完整实例
2015/08/21 Javascript
jquery实现无刷新验证码的简单实例
2016/05/19 Javascript
Knockoutjs 学习系列(二)花式捆绑
2016/06/07 Javascript
JavaScript中transform实现数字翻页效果
2017/03/08 Javascript
jQuery remove()过滤被删除的元素(推荐)
2017/07/18 jQuery
微信小程序实现拖拽 image 触摸事件监听的实例
2017/08/17 Javascript
webpack4+react多页面架构的实现
2018/10/25 Javascript
微信小程序如何使用globalData的方法
2019/06/06 Javascript
jQuery实现推拉门效果
2020/10/19 jQuery
让python的Cookie.py模块支持冒号做key的方法
2010/12/28 Python
Python获取Redis所有Key以及内容的方法
2019/02/19 Python
Pycharm 文件更改目录后,执行路径未更新的解决方法
2019/07/19 Python
PyCharm设置注释字体颜色以及是否倾斜的操作
2020/09/16 Python
Python开发.exe小工具的详细步骤
2021/01/27 Python
计算机专业推荐信范文
2013/11/27 职场文书
大学生自我鉴定
2013/12/08 职场文书
学前教育毕业生自荐信范文
2013/12/24 职场文书
实习生个人总结范文
2015/02/28 职场文书
考研导师推荐信范文
2015/03/27 职场文书
mysql知识点整理
2021/04/05 MySQL