I have nothing to do, so I also wrote a javascript for Lianliankan. The comments are relatively complete. Friends who want to learn should read it.
The most difficult part of Lian Lian Kan is probably the path search, which is to see if there is a passable path between the two points clicked by the mouse. I saw someone's recursive writing method, and I felt itchy, so I figured it out and found that it's not that difficult without recursion.
The path search is analyzed from simple to difficult. First analyze whether a straight line can be connected in a straight line, then analyze whether two points on a straight line can be connected by turning two turns, and finally analyze the situation when they are not on a straight line.
Tested under IE6, IE8, firefox3.0.3.
Copy the code code as follows:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>JS Lianliankan source code perfect annotated version</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>
//The following part is the path search algorithm part and has nothing to do with the presentation layer
//global variables
var X = 16; //Total number of rows
var Y = 14; //Total number of columns
var types = 15;//Graphic types
//layout matrix
//For the convenience of the algorithm, the first row, first column, last row and last column of the matrix are all marked as 0, which is a natural path.
var arr = new Array(Y);
var tbl;//Display the table element of the layout
var p1 = null;//The coordinates of the first point used in the search path
var p2 = null;//The coordinates of the second point used in the search path
var e1 = null;//The element corresponding to the first point
var e2 = null;//The element corresponding to the second point
//Path search, given two points, search for a path
//The path is represented by connectable points
function getPath(p1, p2){
//Sort p1 and p2 before starting the search so that p2 is as far to the lower right of p1 as possible.
//This can simplify the algorithm
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;
}
}
//By analyzing the positional relationship between the two points in Lianliankan, we gradually analyze each type from simple to difficult.
//The first type, whether two points are on a straight line, and whether the two points can be connected by a straight line
if((onlineY(p1, p2)||onlineX(p1, p2)) && hasLine(p1, p2)){
status = 'type 1';
return [p1,p2];
}
//The second type, if any one of the two points is completely surrounded, it will not work.
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;
}
//The third type, two points are on a straight line, but cannot be connected in a straight line
var pt0, pt1, pt2, pt3;
//If they are all on the x-axis, scan possible paths from left to right,
//Construct 4 vertices pt0, pt1, pt2, pt3 each time, and then check whether they are connected between each other
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 the vertex is not empty, the road is blocked.
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];
}
}
}
//If they are all on the y-axis, scan possible paths from top to bottom,
//Construct 4 vertices pt0, pt1, pt2, pt3 each time, and then check whether they are connected between each other
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 the vertex is not empty, the road is blocked.
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];
}
}
}
//The fourth type, the two points are not on a straight line.
//Scan possible paths vertically first
//Similarly, construct 4 vertices each time to see if it is passable
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 + ')' ;
//Special case, if pt0 and pt1 coincide
if(equal(pt0,pt1)){
//If pt2 is not empty, this path is blocked
if(!isEmpty(pt2)){
continue;
}
if( hasLine(pt1, pt2) && hasLine(pt2, pt3) ){
return [pt1, pt2, pt3];
}
else{
continue;
}
}
//Special case, if pt2 and pt3 overlap
else if(equal(pt2,pt3)){
//If pt1 is not empty, this path is blocked
if(!isEmpty(pt1)){
continue;
}
if( hasLine(pt0, pt1) && hasLine(pt1, pt2) ){
return [pt0, pt1, pt2];
}
else{
continue;
}
}
//If neither pt1 nor pt2 is empty, it will not work.
if(!isEmpty(pt1) || !isEmpty(pt2)){
continue;
}
if( hasLine(pt0, pt1) && hasLine(pt1, pt2) && hasLine(pt2, pt3) ){
return [pt0, pt1, pt2, pt3];
}
}
//Scan possible paths horizontally
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[py][px]==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;
}
}
//The following part is the presentation layer part, including drawing, initializing matrix, binding mouse events...
function $(id){return document.getElementById(id)}
var t1, t2;//for testing
//Picture base path
var IMG_PATH = '//www.VeVB.COm';
//initialization
function init(){
//Construct image library
var imgs = new Array(30);
for(var i=1; i<=30; i++){
imgs[i] = 'r_' + i + '.gif';
}
tbl = $('tbl');
//Construct 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);
}
}
//Construct matrix
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); //Generate random positions
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]] + '" />';
}
}
//Bind mouse events
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 Lianliankan perfect annotated version<br />
<table id="tbl" cellspacing="0" cellpadding="0">
</table>
</body>
</html>