仕方ないので、連聯館のJavaScriptも書きました。コメントは比較的充実していますので、学びたい方は読んでみてください。
Lian Lian Kan で最も難しい部分はおそらく経路検索です。これは、マウスでクリックした 2 点の間に通行可能な経路があるかどうかを確認することです。 誰かの再帰の書き方を見てムズムズしたので考えてみたら、再帰を使わなければそれほど難しくないことが分かりました。
経路探索は、簡単なものから難しいものまで、まず直線が直線で結べるかどうかを分析し、次に直線上の2点が2回転して結べるかどうかを分析し、最後に結べない場合の状況を分析します。直線上にあります。
IE6、IE8、Firefox3.0.3でテスト済み。
次のようにコードをコピーします。
<html>
<頭>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>JS Lianliankan ソース コードの完全な注釈付きバージョン</title>
</head>
<スタイル>
テーブル{
境界崩壊: 崩壊;
}
td{
境界線: 実線 #ccc 1px;
高さ: 36px;
幅: 36px;
カーソル: ポインタ;
}
td画像{
高さ: 30ピクセル;
幅: 30ピクセル;
境界線: 実線 #fff 3px;
/*
フィルター: アルファ(不透明度= 80);
-moz-不透明度: 0.8;
不透明度: 0.8;
*/
}
</スタイル>
<スクリプト>
//以下の部分はパス探索アルゴリズム部分であり、プレゼンテーション層とは関係ありません
//グローバル変数
var X = 16; // 総行数
var Y = 14; // 列の総数
var type = 15;//グラフィックタイプ
// レイアウトマトリックス
//アルゴリズムの便宜上、行列の最初の行、最初の列、最後の行、および最後の列はすべて 0 としてマークされます。これは自然なパスです。
var arr = 新しい配列(Y);
var tbl;//レイアウトのテーブル要素を表示します
var p1 = null;//検索パスで使用される最初の点の座標
var p2 = null;//検索パスで使用される 2 番目の点の座標
var e1 = null;//最初の点に対応する要素
var e2 = null;//2 番目の点に対応する要素
//パス検索 2 つの点が与えられた場合、パスを検索します
//パスは接続可能な点で表されます
関数 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;
}
}
//連聯館の2点の位置関係を分析することで、簡単なものから難しいものまで段階的に分析していきます。
// 1 番目のタイプ、2 点が直線上にあるかどうか、および 2 点を直線で結ぶことができるかどうか
if((onlineY(p1, p2)||onlineX(p1, p2)) && hasLine(p1, p2)){
ステータス = 'タイプ 1';
[p1,p2] を返します。
}
//2 番目のタイプは、2 つの点のいずれかが完全に囲まれている場合は機能しません。
if( !isEmpty({x:p1.x, y:p1.y+1}) && !isEmpty({x:p1.x, y:p1.y-1}) && !isEmpty({x:p1.y+1}) x-1, y:p1.y}) && !isEmpty({x:p1.x+1, y:p1.y}) ){
ステータス = 'タイプ 2';
null を返します。
}
if( !isEmpty({x:p2.x, y:p2.y+1}) && !isEmpty({x:p2.x, y:p2.y-1}) && !isEmpty({x:p2.y+1}) x-1, y:p2.y}) && !isEmpty({x:p2.x+1, y:p2.y}) ){
ステータス = 'タイプ 2';
null を返します。
}
// 3 番目のタイプ、2 点は直線上にありますが、直線で結ぶことはできません
var pt0、pt1、pt2、pt3;
// それらがすべて X 軸上にある場合は、可能なパスを左から右にスキャンします。
//毎回 4 つの頂点 pt0、pt1、pt2、pt3 を構築し、それらが相互に接続されているかどうかを確認します
if(オンラインX(p1, p2)){
for(var i=0; i<Y; i++){
if(i==p1.y){
続く;
}
pt0 = p1;
pt1 = {x: p1.x, y: i};
pt2 = {x: p2.x, y: i};
pt3 = p2;
//頂点が空でない場合、道路はブロックされています。
if(!isEmpty(pt1) || !isEmpty(pt2)){
続く;
}
if( hasLine(pt0, pt1) && hasLine(pt1, pt2) && hasLine(pt2, pt3) ){
ステータス = '(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、pt2、pt3] を返します。
}
}
}
// それらがすべて y 軸上にある場合は、可能なパスを上から下にスキャンします。
//毎回 4 つの頂点 pt0、pt1、pt2、pt3 を構築し、それらが相互に接続されているかどうかを確認します
if(オンラインY(p1, p2)){
for(var j=0; j<X; j++){
if(j==p1.x){
続く;
}
pt0 = p1;
pt1 = {x:j, y:p1.y};
pt2 = {x:j, y:p2.y};
pt3 = p2;
//頂点が空でない場合、道路はブロックされています。
if(!isEmpty(pt1) || !isEmpty(pt2)){
続く;
}
if( hasLine(pt0, pt1) && hasLine(pt1, pt2) && hasLine(pt2, pt3) ){
ステータス = '(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、pt2、pt3] を返します。
}
}
}
// 4 番目のタイプ。2 つの点が一直線上にありません。
//最初に可能なパスを垂直方向にスキャンします
//同様に、毎回 4 つの頂点を構築して、通過可能かどうかを確認します
for(var k=0; k<Y; k++){
pt0 = p1;
pt1 = {x:p1.x, y:k};
pt2 = {x:p2.x, y:k};
pt3 = p2;
ステータス = '(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(等しい(pt0,pt1)){
// pt2 が空でない場合、このパスはブロックされます
if(!isEmpty(pt2)){
続く;
}
if( hasLine(pt1, pt2) && hasLine(pt2, pt3) ){
[pt1、pt2、pt3] を返します。
}
それ以外{
続く;
}
}
//特殊なケース、pt2 と pt3 が重なっている場合
else if(equal(pt2,pt3)){
// pt1 が空でない場合、このパスはブロックされます
if(!isEmpty(pt1)){
続く;
}
if( hasLine(pt0, pt1) && hasLine(pt1, pt2) ){
[pt0、pt1、pt2] を返します。
}
それ以外{
続く;
}
}
//pt1 も pt2 も空の場合は動作しません。
if(!isEmpty(pt1) || !isEmpty(pt2)){
続く;
}
if( hasLine(pt0, pt1) && hasLine(pt1, pt2) && hasLine(pt2, pt3) ){
[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;
ステータス = '(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)){
続く;
}
if( hasLine(pt1, pt2) && hasLine(pt2, pt3) ){
[pt1、pt2、pt3] を返します。
}
}
if(equal(pt2,pt3)){
if(!isEmpty(pt1)){
続く;
}
if( hasLine(pt0, pt1) && hasLine(pt1, pt2) ){
[pt0、pt1、pt2] を返します。
}
}
if(!isEmpty(pt1) || !isEmpty(pt2)){
続く;
}
if( hasLine(pt0, pt1) && hasLine(pt1, pt2) && hasLine(pt2, pt3) ){
[pt0、pt1、pt2、pt3] を返します。
}
}
//ステータス='タイプ4';
null を返します。
/************ 終了タイプ 4 **************/
}
関数等しい(p1, p2){
return ((p1.x==p2.x)&&(p1.y==p2.y));
}
関数 onlineX(p1, p2){
p1.y==p2.y を返します。
}
関数オンラインY(p1, p2){
p1.x==p2.x を返します。
}
関数 isEmpty(p){
return (arr[py][px]==0);
}
関数 hasLine(p1, p2){
if(p1.x==p2.x&&p1.y==p2.y){
true を返します。
}
if(オンラインY(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)){
壊す
}
}
if(i==max){
true を返します。
}
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)){
壊す
}
}
if(j==max){
true を返します。
}
false を返します。
}
}
// 次の部分はプレゼンテーション層部分で、描画、行列の初期化、マウス イベントのバインドなどを含みます。
関数 $(id){return document.getElementById(id)}
var t1, t2;//テスト用
//ピクチャのベースパス
var IMG_PATH = '//www.VeVB.COm';
//初期化
関数 init(){
//画像ライブラリを構築する
var imgs = 新しい配列(30);
for(var i=1; i<=30; i++){
imgs[i] = 'r_' + i + '.gif';
}
tbl = $('tbl');
//テーブルを構築する
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] = 新しい配列(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<合計; i++){
tmp[i] = 0;
}
for(var i=0; i<合計; 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;
壊す;
}
}
}
}
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'){
戻る;
}
if(!img1){
img1 = エル;
}
それ以外{
img2 = エル;
}
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;
}
それ以外{
p2 = {x:c, y:r};
e2 = el;
if(!equal(p1, p2)&&e1.innerHTML==el.innerHTML){
var パス = getPath(p1, p2);
if(パス!=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 = 'ライトピンク';
}
}
}
</script>
<body onload="init();">
js Lianliankan 完全注釈付きバージョン<br />
<table id="tbl" cellpacing="0" cellpadding="0">
</テーブル>
</body>
</html>