我花了一夜用資料結構給女朋友寫個H5走迷宮遊戲
先看效果圖(線上電腦嘗試地址http://biggsai.com/maze.html):
起因
又到深夜了,我按照以往在公眾號
寫著資料結構!這佔用了我大量的時間!我的超越妹妹嚴重缺乏陪伴而 怨氣滿滿!
超越妹妹時常埋怨,認為資料結構這麼抽象難懂的東西沒啥作用,常會問道:天天寫這玩意,有啥作用。而我答道:能幹事情多了,比如寫個小遊戲啥的!
當我碼完字準備睡覺時:寫不好別睡覺!
分析
如果用資料結構與演算法造出東西來呢?
- 什麼東西簡單容易呢?我百度一下,我靠,這個鳥遊戲原來不好搞啊,得接觸一堆不熟悉的東西,搞不來搞不來。
有了(靈光一閃
),寫個猜數字遊戲,問他加減乘除等於幾。
- 超越妹妹又不是小孩子,糊弄不過去。
經過一番折騰,終於在半夜12點確定寫迷宮小遊戲了。大概弄清楚其中的幾個步驟。
大概是:
- 畫線—>畫迷宮(擦線)—>方塊移動、移動約束(不出界不穿牆)—>完成遊戲。
畫線(棋盤)
對於html+js(canvas)畫的東西,之前學過javaswing應該有點映像。在html中有個canvas
的畫布,可以在上面畫一些東西和宣告一些監聽(鍵盤監聽)。
對於迷宮來說,那些線條是沒有屬性的,只有位置x,y
,你操作這個畫布時候,可能和我們習慣的面相物件思維不一樣。所以,在你設計的線或者點的時候,記得那個點、線在什麼位置,在後續劃線還是擦線還是移動的時候根據這個位置進行操作。
<!DOCTYPE html>
<html>
<head>
<title>MyHtml.html</title>
</head>
<body>
<canvas id="mycanvas" width="600px" height="600px"></canvas>
</body>
<script type="text/javascript">
var aa=14;
var chess = document.getElementById("mycanvas");
var context = chess.getContext('2d');
// var context2 = chess.getContext('2d');
// context.strokeStyle = 'yellow';
var tree = [];//存放是否聯通
var isling=[];//判斷是否相連
for(var i=0;i<aa;i++){
tree[i]=[];
for(var j=0;j<aa;j++){
tree[i][j]=-1;//初始值為0
}
} for(var i=0;i<aa*aa;i++){
isling[i]=[];
for(var j=0;j<aa*aa;j++){
isling[i][j]=-1;//初始值為0
}
}
function drawChessBoard(){//繪畫
for(var i=0;i<aa+1;i++){
context.strokeStyle='gray';//可選區域
context.moveTo(15+i*30,15);//垂直方向畫15根線,相距30px;
context.lineTo(15+i*30,15+30*aa);
context.stroke();
context.moveTo(15,15+i*30);//水平方向畫15根線,相距30px;棋盤為14*14;
context.lineTo(15+30*aa,15+i*30);
context.stroke();
}
}
drawChessBoard();//繪製棋盤
// var mymap=new Array(36);
// for(var i=0;i<36;i++)
// {mymap[i]=-1;}
</script>
</html>
實現效果
畫迷宮
隨機迷宮怎麼生成?怎麼搞?一臉懵逼。
- 因為我們想要迷宮,那麼就需要這個迷宮出口和入口有連通路徑,你可能壓根不知道迷宮改怎麼生成,用的什麼演算法。小聲BB:用並查集(不相交集合)。
迷宮和不相交集合有什麼聯絡呢?(規則
)
- 之前筆者在前面資料結構與演算法系列中曾經介紹過並查集(不相交集合),它的主要功能是森林的合併,不聯通的通過並查集能夠快速將兩個森林合併,並且能夠快速查詢兩個節點是否在同一個森林中!
而我們的隨機迷宮
:在每個方格都不聯通的情況下,是一個棋盤方格,這也是它的初始狀態。而這個節點可以跟鄰居可能相連,也可能不相連。我們可以通過並查集
實現。
具體思路為:(主要理解並查集)
- 1:定義好不想交集合的基本類和方法(
search,union
等)
2:陣列初始化,每一個陣列元素都是一個集合,值為-1
3:隨機查詢一個格子(一維資料要轉換成二維,有點麻煩),在隨機找一面牆(也就是找這個格子的上下左右),還要判斷找的格子出沒出界。
具體在格子中找個隨機數m——>隨機數m在二維中的位置[m/長,m%長]
——>這個二維的上下左右隨機找一個位置p[m/長+1,m%長]
或[m/長-1,m%長]
或[m/長,m%長+1]
或[m/長,m%長-1]
——>判斷是否越界
4:判斷兩個格子(一維陣列編號)是否在一個集合(並查集查詢)。如果在,則重新找,如果不在,那麼把牆挖去
5:把牆挖去有點繁瑣,需要考慮奇偶判斷它那種牆(上下還是左右,還要考慮位置),然後擦掉。(根據陣列轉換成真實距離)。具體為找一個節點,根據位置關係找到一維陣列的號位用並查集判斷是否在一個集合中。
6:最終得到一個完整的迷宮。直到第一個(1,1)和(n,n)聯通停止。雖然採用隨機數找牆,但是效果並不是特別差。其中要搞清一維二維陣列的關係。一維是真實資料,並查集操作。二維是位置。要搞懂轉化!
注意:避免混淆,搞清陣列的地址和邏輯矩陣位置。陣列從0開始的,邏輯上你自己判斷。別搞混淆!
主要邏輯為:
while(search(0)!=search(aa*aa-1))//主要思路
{
var num = parseInt(Math.random() * aa*aa );//產生一個小於196的隨機數
var neihbour=getnei(num);
if(search(num)==search(neihbour)){continue;}
else//不在一個上
{
isling[num][neihbour]=1;isling[neihbour][num]=1;
drawline(num,neihbour);//劃線
union(num,neihbour);
}
}
那麼在前面的程式碼為
<!DOCTYPE html>
<html>
<head>
<title>MyHtml.html</title>
</head>
<body>
<canvas id="mycanvas" width="600px" height="600px"></canvas>
</body>
<script type="text/javascript">
//自行新增上面程式碼
// var mymap=new Array(36);
// for(var i=0;i<36;i++)
// {mymap[i]=-1;}
function getnei(a)//獲得鄰居號 random
{
var x=parseInt(a/aa);//要精確成整數
var y=a%aa;
var mynei=new Array();//儲存鄰居
if(x-1>=0){mynei.push((x-1)*aa+y);}//上節點
if(x+1<14){mynei.push((x+1)*aa+y);}//下節點
if(y+1<14){mynei.push(x*aa+y+1);}//有節點
if(y-1>=0){mynei.push(x*aa+y-1);}//下節點
var ran=parseInt(Math.random() * mynei.length );
return mynei[ran];
}
function search(a)//找到根節點
{
if(tree[parseInt(a/aa)][a%aa]>0)//說明是子節點
{
return search(tree[parseInt(a/aa)][a%aa]);//不能壓縮路徑路徑壓縮
}
else
return a;
}
function value(a)//找到樹的大小
{
if(tree[parseInt(a/aa)][a%aa]>0)//說明是子節點
{
return tree[parseInt(a/aa)][a%aa]=value(tree[parseInt(a/aa)][a%aa]);//不能路徑壓縮
}
else
return -tree[parseInt(a/aa)][a%aa];
}
function union(a,b)//合併
{
var a1=search(a);//a根
var b1=search(b);//b根
if(a1==b1){}
else
{
if(tree[parseInt(a1/aa)][a1%aa]<tree[parseInt(b1/aa)][b1%aa])//這個是負數(),為了簡單減少計算,不在呼叫value函式
{
tree[parseInt(a1/aa)][a1%aa]+=tree[parseInt(b1/aa)][b1%aa];//個數相加 注意是負數相加
tree[parseInt(b1/aa)][b1%aa]=a1; //b樹成為a樹的子樹,b的根b1直接指向a;
}
else
{
tree[parseInt(b1/aa)][b1%aa]+=tree[parseInt(a1/aa)][a1%aa];
tree[parseInt(a1/aa)][a1%aa]=b1;//a所在樹成為b所在樹的子樹
}
}
}
function drawline(a,b)//劃線,要判斷是上下還是左右
{
var x1=parseInt(a/aa);
var y1=a%aa;
var x2=parseInt(b/aa);
var y2=b%aa;
var x3=(x1+x2)/2;
var y3=(y1+y2)/2;
if(x1-x2==1||x1-x2==-1)//左右方向的點 需要上下劃線
{
//alert(x1);
// context.beginPath();
context.strokeStyle = 'white';
// context.moveTo(30+x3*30,y3*30+15);//
// context.lineTo(30+x3*30,y3*30+45);
context.clearRect(29+x3*30, y3*30+16,2,28);
// context.stroke();
}
else
{
// context.beginPath();
context.strokeStyle = 'white';
// context.moveTo(x3*30+15,30+y3*30);//
// context.lineTo(45+x3*30,30+y3*30);
context.clearRect(x3*30+16, 29+y3*30,28,2);
// context.stroke();
}
}
while(search(0)!=search(aa*aa-1))//主要思路
{
var num = parseInt(Math.random() * aa*aa );//產生一個小於196的隨機數
var neihbour=getnei(num);
if(search(num)==search(neihbour)){continue;}
else//不在一個上
{
isling[num][neihbour]=1;isling[neihbour][num]=1;
drawline(num,neihbour);//劃線
union(num,neihbour);
}
}
</script>
</html>
實現效果:
方塊移動
這部分我採用的方法不是動態真的移動,而是一格一格的跳躍。也就是當走到下一個格子將當前格子的方塊擦掉,在移動的那個格子中再畫一個方塊。選擇方塊是因為方塊更方便擦除,可以根據畫素大小精準擦除。
另外,再移動中要注意不能穿牆、越界。那麼怎麼判斷呢?很好辦,我們再前面會判斷兩個格子是否聯通,如果不連通我們將把這個牆拆開。再拆的時候把這個牆的時候記錄這兩點拆牆可走即可(陣列)
另外,事件的監聽上下左右查一查就可以得到,新增按鈕對一些事件監聽,這些不是最主要的。
為了豐富遊戲可玩性,將方法封裝,可以設定關卡(只需改變迷宮大小)。這樣就可以實現通關了。另外,如果寫成動態存庫那就更好了。
結語
線上嘗試地址,程式碼直接檢視網頁原始碼即可!
筆者前端能力和演算法能力有限,寫的可能不是特別好,還請見諒!當然,筆者歡迎和一起熱愛學習的人共同進步、學習!歡迎關注筆者公眾號:bigsai,後臺回覆java、資料結構、爬蟲、springboot等有精心準備資料一份
。如果感覺不錯,歡迎關注、點贊!蟹蟹!
相關文章
- 走迷宮
- 自動走迷宮小遊戲~遊戲
- 【dawn·資料結構】迷宮問題(C++)資料結構C++
- 迷宮問題【資料結構實驗報告】資料結構
- (C++)資料結構實驗二——迷宮問題C++資料結構
- 藍橋杯-走迷宮(BFS)
- 花了20分鐘,給女朋友們寫了一個web版群聊程式Web
- C語言動態走迷宮C語言
- 用 Canvas + WASM 畫一個迷宮CanvasASM
- 我用開源工具給女朋友寫道歉信!開源工具
- 如何用js寫一個簡單的迷宮和打地鼠遊戲JS遊戲
- 寒假補充專案-回溯法走迷宮
- 用webgl打造自己的3D迷宮遊戲Web3D遊戲
- 【ybt高效進階1-5-1】走迷宮
- [SDOI2012] 走迷宮 題解
- 51nod 1459 迷宮遊戲遊戲
- 內容是超正統的迷宮RPG?PSP遊戲《迷宮旅人2》深度解析遊戲
- 509迷宮
- 用Q-learning演算法實現自動走迷宮機器人演算法機器人
- 尋路者華為雲:在產業AI迷宮裡走直線產業AI
- 女朋友想減肥,程式設計師花了三天寫了個卡路里計數器程式設計師
- 走迷宮 -- 民間圖靈獎參賽者名單和作品圖靈
- Python 精靈模組_迷宮房間類遊戲Python遊戲
- 用C語言解決迷宮問題C語言
- 簡單介紹Python迷宮生成和迷宮破解演算法Python演算法
- PHP 生成迷宮路線PHP
- 【面試】如何找到迷宮出口面試
- JAVA 實現《複雜迷宮》遊戲|CSDN創作打卡Java遊戲
- 強化學習入門之智慧走迷宮-策略迭代演算法強化學習演算法
- 走迷宮從2011算出2012的SQL程式碼SQL
- PHP 解迷宮之 H 最小PHP
- Python迷宮生成器Python
- 回溯法求迷宮問題
- POJ3984-迷宮問題
- 我轉行前端花了11個月前端
- PARL原始碼走讀——使用策略梯度演算法求解迷宮尋寶問題原始碼梯度演算法
- 強化學習入門之智慧走迷宮-價值迭代演算法強化學習演算法
- 用python深度優先遍歷解迷宮問題Python