这个小项目写了挺久的。
之所以会如此,
首先这些小游戏项目的结构实际上比服务器更加复杂、抽象;
其次是因为自己本身对JavaScript不熟悉;
再次是常常因为这些事那些事耽误了。
个人觉得:
尽管JavaScript的功能很强大,但是实际上是一种对程序员不友好的语言。程序员体验极差。
比如说,网页刷新(8) + 弹出对话框(9)一共有72种组合。再用户体验良好的网页或者APP中,我们总是尽可能地减少界面的按钮,以减少令用户选择的机会,以提升“用户体验”。
我们将JavaScript比作一个应用程序,程序员是用户,当面对这种语法不严谨、编程思想含糊不清的语言时,体验实在是不好。
在我看来,尽管JavaScript据说存在封装、类之类的说法,但实在是有点强行蹭面子的感觉。JavaScript的类更像是C语言的结构体,JavaScript也是更接近C语言的脚本语言。
所以,我想终有一天会出现比JavaScript更好的脚本语言取代它。
0926 总结:
即使先把思路写好,依然要写下:
今天实现了什么功能;
仍然需要实现什么功能;
下一步需要做什么功能。
不然隔了几天总会没了节奏。
0927
采用递归一直出错的原因:
画了图之后发现,第一次往左调用函数后,又会往右边调用一次,如此循环反复,无法停止
等完全实现再上代码。
由于个人水平有限,以及JavaScript本身是一种更倾向于面线过程的语言,所以这个脚本代码可读性略差,见谅
minesweeper.jsp/minesweeper.html
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="basepath.jsp" %>
<%@ page isELIgnored="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>扫雷</title>
<link rel="stylesheet" type="text/css"
href="${pageContext.request.contextPath}/minesweeper/minesweeper.css" >
</head>
<body>
<div id="JMS_main" class="main">
<table id="landMine"></table>
<div id="operation">
<div class="tip">剩余雷数:<span class="light red" id="landMineCount">0</span> 个</div>
<div class="tip">持续时间: <span class="light f60" id="costTime">0</span> 秒</div>
<fieldset>
<legend>难度选择:</legend>
<!-- 初级 -->
<input type="radio" name="level" id="llevel" checked="checked"
value="10" οnchange="javascript:change()"/>
<label for="llevel">初级(10*10)</label><br />
<!-- 中级 -->
<input type="radio" name="level" id="mlevel"
value="15" οnchange="javascript:change()"/>
<label for="mlevel">中级(15*15)</label><br />
<!-- 高级 -->
<input type="radio" name="level" id="hlevel"
value="20" οnchange="javascript:change()"/>
<label for="hlevel">高级(20*20)</label><br />
</fieldset>
<input type="button" id="begin" value="开始游戏" οnclick="javascript:start()"/><br />
<div class="tip txtleft">提示:
<ul>
<li>1、点击“开始游戏”游戏开始计时</li>
<li>2、游戏过程中点击“开始游戏”将开始新游戏</li>
</ul>
</div>
</div>
</div>
<!-- <input type = "button" οnclick="javascript:printParameter()"> -->
<script src="${pageContext.request.contextPath}/minesweeper/jms.js" ></script>
<script type="text/javascript" src="${pageContext.request.contextPath }/minesweeper/minesweeper.js"></script>
<!-- <script src="index.js"></script> -->
</body>
</html>
jms.js
/**
* 绘制游戏界面gamePanel
*
* 思路:
* 1、radio 添加onChange 事件;
* 2、
*
*/
var first =true;
/** 绘制 landMine (游戏面板)
* 思路:
* 1、获取 table ;
* 2、获取 table.rows;(表格行数);
* 3、删除原来 table 的row 与col;
* 4、获取 当前 radios 的 value;
* 5、根据 value 绘制 table;
*
*/
function draw(){
var table = document.getElementById("landMine");
var rows = table.rows.length;
//删除所有行数
for(var i=rows-1; i>=0; i--){
table.deleteRow(i);
}
var radios = document.getElementsByName("level");
for(var i=0 ; i<radios.length ;i++){
if(radios[i].checked){
var value = radios[i].value;
}
}
for(var i=0 ;i<value; i++){
var row = table .insertRow(i);
for(var j=0;j<value;j++){
var col = row.insertCell(j);
col.innerHTML =" ";
}
}
}
function change(){
window.location.reload();
draw();
}
if(first){
draw();
first =false;
}
minesweeper.js
/**
* 功能:实现“扫雷”游戏功能
* 基本思路:
*
* 一、初始化游戏数据
* 1、创建一个数组对象arrayMines[value][value] 用于保存 地雷数据
* 0~8分别代表 附近地雷数字
* 9 代表 地雷
* landMineCount:剩余地雷数;
* costTime :花费时间
*
* 2、
* 初始化游戏数据应该放在加载界面时
*
* 二、给table中的cell添加点击事件
* cell.value代表的三种状态:
* 0:不存在旗子 可插旗
* 1:存在旗子 可拔旗
* 2:不可修改
*
* 左键:
* 1、获取 arrayMine 对应数字;
* 2、判断数字
* 0: 显示相应数字,(生成一块区域)
* 1-8: 显示相应数字,并令其为空
* 9: 游戏结束
* 3、遍历数组,如果仅剩0、9 游戏成功
*
* 右键:
* 插旗 landMineCount--;
* 取旗 landMineCount++;
*
* 游戏成功出口:
* arrayMines中仅剩下0、9
*
*
*/
/** JS 初始化阶段
*
*/
/**
* 去掉鼠标默认contextMenu事件,否则会和右键一起出现
*/
document.oncontextmenu = function(e){
e.preventDefault();
};
var landMineCount =document.getElementById("landMineCount");
var mineCount ;
var realMineCount; //真正地雷数
var costTime = document.getElementById("costTime");
var table = document.getElementById("landMine");
var radios = document.getElementsByName("level");
var value;
var squareValue;
for (var i=0;i<radios.length;i++){
if(radios[i].checked){
value = radios[i].value;
}
}
//根据value 给mineCount 赋值
switch (value) {
case "10": mineCount = 10;
break;
case "15": mineCount = 40;
break;
default: mineCount = 99;
break;
}
realMineCount = mineCount;
squareValue = value*value ;
//初始化数组
var arrayMine = new Array();
//A 先给数组arrayMine 全部设置为 0
for(var i=0 ; i <value; i++){
arrayMine[i] = new Array();
for(var j=0 ; j<value ;j++){
arrayMine[i][j] = 0;
}
}
/**
* 给 mine 周边 的 arrayMine 元素赋值
* @param x
* @param y
*/
function arrayValue(x,y){
for(var i=x-1; i<=x+1 ;i++){
for(var j=y-1 ; j<=y+1 ;j++){
if( i>=0 && i<value && j>=0 && j<value && arrayMine[i][j]!= 9){
arrayMine[i][j] ++;
}
}
}
}
/**
* 初始化:
* 1、随即产生mineCount个炸弹;
* 2、通过arrayValue 给arrayMine修改 mine 周边 的arrayMine的值
*/
function init(){
var count = 0;
while(count < mineCount){
var x = Math.floor(Math.random() * value);
var y = Math.floor(Math.random() * value);
if(arrayMine[x][y] != 9 ){
arrayMine[x][y] = 9;
count++;
arrayValue(x,y); //调用arrayValue方法
}
}
//alert("x:"+x+" y:"+y+" count:"+count);
}
//直接 调用初始化 函数
init();
//函数 :计时器
var t
var c=0;
function timeCount()
{
costTime.innerHTML = c ;
c=c+1
t=setTimeout("timeCount()",1000)//未来某时执行某方法 (方法名,时间)
}
/**
* 左键处理
* 如果 obj 是雷区 game over
* 如果 obj 不是雷区
* 如果 obj 为 0 显示一片区域
* 如果 obj 为 数值 显示该数值
* 通过递归函数来打印
*
* **修改背景色
* 设置obj.value = 2;
*
*/
function printArray(x,y){
/*alert("148");*/
if(x>=0 && x<value && y>=0 && y<value){
/*alert("printArray");*/
if(table.rows[x].cells[y].value != 2 && arrayMine[x][y] != 9){
if(arrayMine[x][y] == 0){
table . rows[x].cells[y]. innerHTML = " ";
}else{
table . rows[x].cells[y]. innerHTML = arrayMine[x][y];
}
table . rows[x].cells[y].setAttribute('style','background-color: #AAA !important');
table . rows[x].cells[y].value = 2;
if(arrayMine[x][y] == 0){
printArray(x-1, y-1);
printArray(x-1, y);
printArray(x-1, y+1);
printArray(x, y-1);
printArray(x, y+1);
printArray(x+1, y-1);
printArray(x+1, y);
printArray(x+1, y+1);
}
}
}
/*alert("166");*/
}
function onLeftHandler(obj){
//获得cell 在table 的行列值
var x = obj.parentNode.rowIndex ;
var y = obj.cellIndex;
switch(arrayMine[x][y]){
case 9://雷区 gameover
for(var i = 0 ; i<value ; i++){
for(var j=0 ; j<value ; j++){
if( arrayMine [i][j] ==9 ){
//table . rows[i].cells[j]. innerHTML = arrayMine[i][j];
table.rows[i].cells[j].innerHTML = " ";//清空旗子
var img = document.createElement("img");
img.src = "minesweeper/img/mine.png";
table.rows[i].cells[j].appendChild(img);
}
}
}
alert("游戏失败");
window.location.reload();//刷新 据说弹出对话框的方法有九种,刷新的方法有八种,一个组合功能有七十二种写法fuck
break;
case 0:
//printZero(x, y);
//break;
default:
printArray(x,y);
break;
}
var isSuccess = 0;
//游戏成功出口:value !=2 值与正确炸弹数相等
for(var i=0 ; i<value ;i++){
for(var j=0 ; j<value ; j++){
if(table.rows[i].cells[j] .value == 2){
isSuccess ++;
}
}
}
//alert(isSuccess);
//value * value == isSuccess + realMineCount ;
if( squareValue == (isSuccess + realMineCount)){
alert("游戏成功");
window.location.reload();
}
}
/**
* 鼠标对table.row.cell 的点击事件的具体操作
*/
var handler = function(event){
//if(evt) 经测验 evt 存在
var mouseClick = event.button;
if(mouseClick == 0){ //鼠标左键
if( this.value != 2 ){
onLeftHandler(this);
}
}
/*if(mouseClick == 1){//鼠标中键
alert(1);
}*/
if(mouseClick == 2){//鼠标右键 :landMineCount --;插旗;拔旗
switch(this.value){
case 1://1代表存在旗子 可拔旗子 此处为拔旗操作
this.innerHTML =" ";
mineCount ++;
landMineCount.innerHTML = mineCount;
this.value = 0;
break;
case 2://不可修改
break;
case 0://0 代表不存在旗子 可插旗 此处为插旗操作
default :
var img = document.createElement("img");
img.src = "minesweeper/img/flag.png";
this.appendChild(img);
this.value = 1;
mineCount --;
landMineCount.innerHTML = mineCount;
break;
}
}
}
/**
*
*/
function addCellListener(){
/**
* DOM2事件处理程序
* 给每个cell 添加 单击监听事件
* //true 代表事件捕捉模型
* //false代表事件冒泡阶段处理
*/
for(var i=0 ; i<value ;i++){
for(var j=0 ; j<value ; j++){
table.rows[i].cells[j].addEventListener('mousedown',handler,true);
}
}
}
/**
* Button begin 的点击事件
*/
function start(){
//显示landMineCount、costTime
landMineCount.innerHTML = mineCount ;
//显示costTime
timeCount();
addCellListener();
//测试代码 用于打印数组
/*for(var i=0 ;i<value; i++){
for(var j=0;j<value;j++){
if( arrayMine [i][j] ==9 ){
//table . rows[i].cells[j]. innerHTML = arrayMine[i][j];
var img = document.createElement("img");
img.src = "minesweeper/img/mine.png";
table.rows[i].cells[j].appendChild(img);
}else{
if(arrayMine[i][j] != 0)
table . rows[i].cells[j]. innerHTML = arrayMine[i][j];
}
}
}*/
}
//table . rows[i].cells[j]. innerHTML = arrayMine[i][j];
/*function print(){
for(var i=0 ;i<value; i++){
for(var j=0;j<value;j++){
document.writeln(arrayMine[i][j]);
}
}
}*/
minesweeper.css
@CHARSET "UTF-8";
.main {
margin:10px auto;
padding:20px;
background:#EEE;
width:60%;
zoom:1;
}
.main table {
background:#CCC;
float:left;
}
.main table td {
border:2px outset #EEE;
font-size:20px;
width:32px;
height:32px;
text-align:center;
cursor:pointer;
}
/* 深灰色 选择对应元素时 改变 元素样式 */
.main table td:hover {
background-color:#AAA;
}
.main #operation {
width:180px;
float:right;
text-align:center;
}
.landMine {
margin:0 auto;
background-image:url(mine.png);
background-position:center;
background-repeat:no-repeat;
}
.main table td.normal {
border:2px solid #EEE;
background-color:#AAA;
}
.main table td.normal:hover {
background-color:#AAA;
}
.flag {
background-image:url(flag.png);
background-position:center;
background-repeat:no-repeat;
}
.main:after {
clear: both;
display: block;
content: "";
line-height: 0;
height: 0;
visibility:hidden;
}
.main .tip {
font-size:14px;
margin:5px;
}
.main .tip ul {
}
.main .tip ul li {
margin:5px 0;
line-height:20px;
}
.main .light{
font-size:30px;
}
.main .red {
color:red;
}
.main .f60 {
color:#F60;
}
.main input[type=button] {
padding:2px 10px;
margin:5px;
font-size:20px;
cursor:pointer;
}
.main .txtleft {
text-align:left;
}
.main input[type='radio'],
.main fieldset label {
cursor:pointer;
}
.main fieldset {
margin:10px 0;
line-height:25px;
}