0 参考链接
1 JS代码修改运行方法
首先下载的文件夹如下:
双击打开maze.html文件。
键盘按F12快捷键。
查看代码如下,右键txt打开maze.html。
修改:
迷宫小游戏到迷宫大游戏
修改保存后,刷新浏览器。
环境测试完毕!
2 用力梳理知识点
2.1 停止更新条件
游戏连续玩赢3局,就会停止更新棋盘。
这里是show()函数的代码停止条件导致。
var aa = 14;
// stop();
aa=aa+2;
if(aa==20){ rangeinsert(); stop();}
2.2 tree和isling傻傻分不清楚
tree和isling数组初始化一样,用途不一样,tree是并查集算法必备的数据结构。
多余的isling,是为了得到逻辑上的联通的路径。
即:判定随机的上下左右是否相连条件后,二维数组isling中相连的格子数值为1。
值为1的格子就算是迷宫中的联通路径。
var tree = [];//存放是否联通
var isling=[];//判断是否相连
2.3 doKeyDown(e)函数
doKeyDown(e)函数中,[(x-20)/30*aa+(y-20)/30]中的30是迷宫行高,20是蓝色红色格子的高或宽。
这个是在生成后的迷宫中,对红色格子的位置x和y,进行是否在联通路径中的判定。
涉及到了坐标对应转换,迷宫坐标和逻辑坐标的转换。
if (keyID === 38 || keyID === 87) { // W键以及上键的移动方向
if(y-30<0){}
else if(isling[(x-20)/30*aa+(y-20)/30][((x-20)/30)*aa+(y-20)/30-1]!=1) {}
else {
clearCanvas();
y = y - 30;
Context.fillRect(x, y, 20, 20);
e.preventDefault();
gameover();
show();
}
}
2.4 路径压缩
查找途中能不能路径压缩
每次查询,自下向上。当我们调用递归的时候,可以顺便压缩路径(将当前数组的值等于递归返回的根节点的值),我们查找一个元素只需要直接找到它的祖先,所以当它距离祖先近那么下次查询就很快。并且压缩路径的代价并不大!
试想一下,如果一个分支的深度为1000,不压缩路径那么这个分支每个节点平均查找次数为500,压缩一次下次再查找就是1次。
学会路径压缩,你基本可以秒杀大部分并查集的题。
在移动中要注意不能隔空穿墙、越界。那么怎么判断呢?很好办,移动前目标方格,我们判断其是否直接联通,注意是直接联通而不是联通(很可能绕一圈联通但不能直接越过去,所以这里并查集不能压缩路径哦),如果直接不连通,那么不进行操作,否则进行方块移动。
路径压缩代码:
public int search(int a)//返回头节点的数值
{
if(tree[a]>0)//说明是子节点
{
return tree[a]=search(tree[a]);//压缩路径(将当前数组的值等于递归返回的根节点的值)
}
else
return a;
}
非路径压缩代码:
function search(a)//找到根节点
{
if(tree[parseInt(a/aa)][a%aa]>0)//说明是子节点
{
return search(tree[parseInt(a/aa)][a%aa]);//不能压缩路径
}
else
return a;
}
2.5 某处迷宫墙随机生成
随机选取上下左右的迷宫墙代码:
ran是[0,3]中的一个。
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];
}
2.6 drawline()函数
drawline()函数是绘制白色的边框线,再擦除格子线条。
context.strokeStyle = 'white';
context.clearRect(29+x3*30, y3*30+16,2,28);
2.7 Canvas API
Canvas API 提供了一个通过JavaScript 和 HTML的元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。
canvas提供了三种方法绘制矩形:
fillRect(x, y, width, height)
绘制一个填充的矩形
strokeRect(x, y, width, height)
绘制一个矩形的边框
clearRect(x, y, width, height)
清除指定矩形区域,让清除部分完全透明。
fillStyle = color
设置图形的填充颜色。
strokeStyle = color
设置图形轮廓的颜色。
2.8 css样式表文件
下面的a是在maze.css中定义的。
<a href="https://blog.csdn.net/qq_40693171">女神</a>
maze.css中的a
a {
text-decoration:none;
color:#000;
}
3 代码赏析
import java.util.Scanner;
public class DisjointSet {
static int tree[]=new int[100000];//假设有500个值
public DisjointSet() {
set(this.tree);}
public DisjointSet(int tree[])
{
this.tree=tree;
set(this.tree);
}
//初始化所有都是-1 有两个好处,这样他们指向-1说明是自己,
//第二,-1代表当前森林有-(-1)个
public void set(int a[])
{
int l=a.length;
for(int i=0;i<l;i++)
{
a[i]=-1;
}
}
public int search(int a)//返回头节点的数值
{
if(tree[a]>0)//说明是子节点
{
return tree[a]=search(tree[a]);//压缩路径(将当前数组的值等于递归返回的根节点的值)
}
else
return a;
}
public int value(int a)//返回a所在树的大小(个数)
{
if(tree[a]>0)
{
return value(tree[a]);
}
else
return -tree[a];
}
public void union(int a,int b)//表示 a,b所在的树合并
{
int a1=search(a);//a根
int b1=search(b);//b根
if(a1==b1) {
System.out.println(a+"和"+b+"已经在一棵树上");}
else {
if(tree[a1]<tree[b1])//这个是负数,为了简单减少计算,不在调用value函数
{
tree[a1]+=tree[b1];//个数相加 注意是负数相加
tree[b1]=a1; //b树成为a的子树,直接指向a;
}
else
{
tree[b1]+=tree[a1];//个数相加 注意是负数相加
tree[a1]=b1; //b树成为a的子树,直接指向a;
}
}
}
public static void main(String[] args)
{
DisjointSet d=new DisjointSet();
d.union(1,2);
d.union(3,4);
d.union(5,6);
d.union(1,6);
d.union(22,24);
d.union(3,26);
d.union(36,24);
System.out.println(d.search(6)); //头
System.out.println(d.value(6)); //大小
System.out.println(d.search(22)); //头
System.out.println(d.value(22)); //大小
}
}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" type="text/css" href="css/maze.css">
</head>
<body>
<nav class="navbar">
<div class="navbar-content-1">
<a href="https://blog.csdn.net/qq_40693171"> </a>
</div>
<div class=" container navbar-content">
<a href="https://blog.csdn.net/qq_40693171">女神</a>
<a href="https://juejin.im/user/5c944a8bf265da6102098c31">520</a>
<a href="https://github.com/javasmall">快乐</a>
<a href="https://github.com/javasmall">bigsai的祝福</a>
</div>
<div class="navbar-content-2">
<a >66</a>
</div>
</nav>
<div class="container">
<h1>迷宫小游戏</h1>
<h4></h4>
<div class="news-list">
<div class="news-list-left">
<canvas id="mycanvas" width="600px" height="600px"></canvas>
</div>
<div class="news-list-right">
<div class="about">
<h4>关于我们</h4>
<div class="about-des">
<span id="mytime"></span>
<button onclick="start()" >开始游戏</button>
<button onclick="stop()">暂停</button>
<button onclick="range()">排行榜</button>
<button onclick="renovates()">重新开始</button>
</div>
<br>
<img src="image/nushen.jpg" width="100%" height="35%">
</div>
</div>
<footer class="copyright">
Copyright 2018 All rights reserved.
<div class="col-xs-12">本站总访问量:666</div>
<a target="_blank" href="http://wpa.qq.com/msgrd?v=3&uin=1315426911&site=qq&menu=yes">联系我们 </a>
<div><a href=http://www.miitbeian.gov.cn></a>苏ICP备18027982号</div>
</footer>
</div>
</div>
<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;}
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);
}
}
var a=aa*30-10,b=aa*30-10;
var x = 20, y =20;
function load() {
var canvas = document.getElementById("mycanvas");
Context = canvas.getContext("2d");
Context.fillStyle = "blue";
Context.fillRect(x, y, 20, 20);
context.fillStyle = "red";
context.fillRect(a, b, 20, 20);
Context.fillStyle = "blue";
canvas.addEventListener('keydown', doKeyDown, true);
canvas.focus();
window.addEventListener('keydown', doKeyDown, true);
}
load();
function doKeyDown(e) {
//alert(x+" "+y);
// console.log(x+" "+y);测试
var keyID = e.keyCode ? e.keyCode : e.which;//获取按键的Unicode代码值
if(i==1){
if (keyID === 38 || keyID === 87) {
// W键以及上键的移动方向
if(y-30<0){
}
else if(isling[(x-20)/30*aa+(y-20)/30][((x-20)/30)*aa+(y-20)/30-1]!=1) {
}
else {
clearCanvas();
y = y - 30;
Context.fillRect(x, y, 20, 20);
e.preventDefault();
gameover();
show();
}
}
if (keyID === 39 || keyID === 68) {
// D键以及you键的移动方向
if(x+30>15+30*aa){
}
else if(isling[(x-20)/30*aa+(y-20)/30][((x-20)/30)*aa+(y-20)/30+aa]!=1) {
}
else{
clearCanvas();
x=x+30;
Context.fillRect(x, y, 20, 20);
e.preventDefault();
gameover();
show();
}
}
if (keyID === 40 || keyID === 83) {
// S键以及下键的移动方向
if(y+30>15+30*aa){
}
else if(isling[(x-20)/30*aa+(y-20)/30][((x-20)/30)*aa+(y-20)/30+1]!=1) {
}
else{
clearCanvas();
y = y + 30;
Context.fillRect(x, y, 20, 20);
e.preventDefault();
gameover();
show();
}
}
if (keyID === 37 || keyID === 65) {
// A键以及zuo向
if(x-30<0){
}
else if(isling[(x-20)/30*aa+(y-20)/30][((x-20)/30)*aa+(y-20)/30-aa]!=1) {
}
else{
clearCanvas();
x = x - 30;
Context.fillRect(x, y, 20, 20);
e.preventDefault();
gameover();
show();
}}
}
}
function clearCanvas() {
//清除之间的痕迹
Context.clearRect(x-2, y-2, 25, 25)
}
var end=false;
function gameover()
{
if(x>=a&&y>=b)
{
end=true;
}
}
function show()
{
if(end==true)
{
// stop();
aa=aa+2;
if(aa==20){
rangeinsert(); stop();}
else{
end=false;
Context.clearRect(0, 0, 600, 600);
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
}
}
drawChessBoard();//绘制棋盘
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);
}
}
a=aa*30-10,b=aa*30-10;
x = 20, y =20;
load();
// start();
}
// alert("游戏成功!共用时:"+str);
}
}
var h=m=s=ms= 0; //定义时,分,秒,毫秒并初始化为0;
var time=0;
var i=0;
function timer(){
//定义计时函数
ms=ms+50; //毫秒
if(ms>=1000){
ms=0;
s=s+1; //秒
}
if(s>=60){
s=0;
m=m+1; //分钟
}
if(m>=60){
m=0;
h=h+1; //小时
}
str =toDub(h)+"时"+toDub(m)+"分"+toDub(s)+"秒"+toDubms(ms)+"毫秒";
mytime = document.getElementById('mytime');
mytime.innerHTML = str;
// document.getElementById('mytime').innerHTML=h+"时"+m+"分"+s+"秒"+ms+"毫秒";
}
function reset(){
//重置
i=1;
time=setInterval(timer,50);
}
function start(){
//开始
i=1;
time=setInterval(timer,50);
}
function stop(){
//暂停
i=0;
clearInterval(time);
}
function toDub(n){
//补0操作
if(n<10){
return "0"+n;
}
else {
return ""+n;
}
}
function toDubms(n){
//给毫秒补0操作
if(n<10){
return "00"+n;
}
else {
return "0"+n;
}
}
function renovates(){
document.location.reload();
}
var req;//创建对象
function range()
{
var url = "";//ajax
//创建一个XMLHttpRequest对象req
if(window.XMLHttpRequest) {
//IE7, Firefox, Opera支持
req = new XMLHttpRequest();
}else if(window.ActiveXObject) {
//IE5,IE6支持
req = new ActiveXObject("Microsoft.XMLHTTP");
}
req.open("GET", url, true);
//onreadystatechange属性存有处理服务器响应的函数,有5个取值分别代表不同状态
req.onreadystatechange = callback;
//send函数发送请求
req.send(null);
}
function rangeinsert()
{
var url = "insertrange?id=" +(m*60+ s);
//创建一个XMLHttpRequest对象req
if(window.XMLHttpRequest) {
//IE7, Firefox, Opera支持
req = new XMLHttpRequest();
}else if(window.ActiveXObject) {
//IE5,IE6支持
req = new ActiveXObject("Microsoft.XMLHTTP");
}
req.open("GET", url, true);
//onreadystatechange属性存有处理服务器响应的函数,有5个取值分别代表不同状态
req.onreadystatechange = callback;
//send函数发送请求
req.send(null);
}
function callback() {
if(req.readyState == 4 && req.status == 200) {
var check = req.responseText;
alert(check);
}
}
</script>
</body>
</html>
maze.css样式表文件:
canvas{
display: block;
margin: 50px auto;
box-shadow: -2px -2px 2px #F3F2F2, 5px 5px 5px #6F6767;
}
body {
margin: 0;
padding: 0;
font-family:"Microsoft YaHei", "微软雅黑", "consolas";
/*background-image: url("");*/
background-attachment: fixed;
}
a {
text-decoration:none;
color:#000;
}
.navbar {
background-color:#000;
width:100%;
height:50px;
}
.container {
width: 1000px;
margin: 0 auto;
}
.navbar .navbar-content-1 a {
float: left;
color: #FFF;
line-height: 50px;
display: inline-block;
width: 90px;
white-space: nowrap;
text-align: left;
}
.navbar .navbar-content-2 a {
float: right;
color: #FFF;
line-height: 50px;
display: inline-block;
width: auto;
white-space: nowrap;
text-align: right;
}
.navbar .navbar-content a {
float: left;
color: #FFF;
line-height: 50px;
display: inline-block;
width: 90px;
white-space: nowrap;
text-align: center;
}
.navbar .navbar-content a:hover {
color: #CCC;
}
.news-list {
margin: 50px 0;
background-color: #FFF;
border-radius: 15px;
border: 1px solid #DDD;
padding: 30px 20px;
min-height: 300px;
}
.news-list:hover {
box-shadow: 0 0 5px 3px #CCC;
}
.about .about-des {
border-left: 5px solid #abc;
margin-top: 15px;
}
.about .about-des p {
padding-left: 10px;
line-height: 28px;
text-indent: 2em;
}
.news-list-left {
float: left;
width: 729px;
margin-bottom: 50px;
}
.news-list-right {
float: right;
width: 229px;
}
.news-title i {
display: inline-block;
width: 47px;
height: 43px;
margin-right: 10px;
/*background: url('image/54a1137bbeaa9.jpg') left center no-repeat;*/
vertical-align: middle;
}
.news-title a {
color: green;
}
.news-title a:hover {
text-decoration: underline;
}
.copyright {
clear: both;
text-align: center;
color: gray;
border-top: 2px solid #CCC;
margin-top: 50px;
padding: 20px 0;
}
button {
cursor: pointer;
width: 150px;
height: 44px;
margin-top: 25px;
padding: 0;
background: #ef4300;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
border-radius: 6px;
border: 1px solid #ff730e;
-moz-box-shadow:
0 15px 30px 0 rgba(255,255,255,.25) inset,
0 2px 7px 0 rgba(0,0,0,.2);
-webkit-box-shadow:
0 15px 30px 0 rgba(255,255,255,.25) inset,
0 2px 7px 0 rgba(0,0,0,.2);
box-shadow:
0 15px 30px 0 rgba(255,255,255,.25) inset,
0 2px 7px 0 rgba(0,0,0,.2);
font-family: 'PT Sans', Helvetica, Arial, sans-serif;
font-size: 14px;
font-weight: 700;
color: #fff;
text-shadow: 0 1px 2px rgba(0,0,0,.1);
-o-transition: all .2s;
-moz-transition: all .2s;
-webkit-transition: all .2s;
-ms-transition: all .2s;
}
button:hover {
-moz-box-shadow:
0 15px 30px 0 rgba(255,255,255,.15) inset,
0 2px 7px 0 rgba(0,0,0,.2);
-webkit-box-shadow:
0 15px 30px 0 rgba(255,255,255,.15) inset,
0 2px 7px 0 rgba(0,0,0,.2);
box-shadow:
0 15px 30px 0 rgba(255,255,255,.15) inset,
0 2px 7px 0 rgba(0,0,0,.2);
}
button:active {
-moz-box-shadow:
0 15px 30px 0 rgba(255,255,255,.15) inset,
0 2px 7px 0 rgba(0,0,0,.2);
-webkit-box-shadow:
0 15px 30px 0 rgba(255,255,255,.15) inset,
0 2px 7px 0 rgba(0,0,0,.2);
box-shadow:
0 5px 8px 0 rgba(0,0,0,.1) inset,
0 1px 4px 0 rgba(0,0,0,.1);
border: 0px solid #ef4300;
}