閑來無事,也寫一個javascript連連看,注釋比較完整,想學的朋友可要看了。
連連看最難的部分應該是路徑搜索,即鼠標點的兩點之間看有無可通的路徑。 看過有人寫的遞歸寫法,心里癢癢,就捉摸了一下,發現不用遞歸的情況下難度也不大。
路徑搜索由簡到難分析,先分析一條直線上是否可直線連通,再分析一條直線上的兩點通過拐兩個彎是否可通,最后分析不在一條直線上的情況.
在IE6, IE8, firefox3.0.3下測試過.
//全局變量
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 = '//www.companysz.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>
|
新聞熱點
疑難解答