开发概述
前期开发概述
前期开发已实现:
1、进行游戏:玩家输入名字(设有默认值)数字(数字隐藏显示为•,并强制要求输入),并通过计算得出黄金点和赢家,然后可进行下一局(可无限进行)
2、游戏存档:每一局完成后将每位玩家的数字和每局的黄金点和赢家存入数据库
3、查看游戏记录:可以分别查看每位玩家在每一局游戏输入的数字和每局的黄金点和赢家,并且可以按照制定的分类标准(场次或玩家)进行查看
4、继续游戏:在重新启动应用后,可以从数据库中获取以前游戏的信息,并自动给出相应数量的输入框
5、新游戏:提示用户输入玩家数量(必填,理论上可覆盖整个int范围),从数据库中删除原有信息,并在游戏界面显示相应数量的输入框
本阶段开发概述
本阶段开发实现了:
1、输入控制:对用户输入(或粘贴)的内容进行强制性限制,比如必须输入正整数或必须输入小数,对于输入非法的字符直接立刻使之消失
2、数字检查与提示:检查玩家输入的数字是否在0~100的范围内,如果有玩家输入的数字超出了范围,则停止后续操作,并显示所有输出超过范围的玩家的名字
3、数据可视化:用折线图展示黄金点的变化趋势,用条形统计图展示每一位玩家赢的次数
阶段开发成果展示
因为涉及到的有些功能是动态的,所以用了动图来展示
玩家数量输入控制
在输入玩家数量时进行强制性输入控制,使非法输入的字符立刻消失。如果直接向输入框内粘贴也是一样的。
示例:最开始在输入框输入“-”,输入的“-”马上就消失了;然后输入12.2,结果马上变成了122
数字输入控制
在玩家输入数字时进行强制性输入控制,使非法输入的字符立刻消失。
示例:不停地输入字母,输入的内容立刻消失
数字输入检查
如果有玩家输入的数字不在0~100的范围内,点击完成后将提示玩家输入的数字超出了范围,并且会明确给出是哪一位玩家超出了范围,此时输入的数据不会存入数据库,然后可以点击返回重新填写。
示例:玩家0输入-3,点击完成后界面提示玩家0输入超出范围,点击重新填写,返回游戏界面。
黄金点趋势可视化
在查看结果的界面中将每局游戏的黄金点以折线图的形式展示
玩家游戏结果统计可视化
在查看结果的界面中将每位玩家赢的次数以条形统计图的形式展示
实现方法
输入控制
玩家数量的输入控制:
通过在input标签里面使用onkeyup和onafterpaste用JavaScript语句分别检查输入和粘贴的内容里面有没有正则表达式之外的字符,如果有则用空字符替代。
new_game.jsp
<form id="add_user_num" name="add_user_num" action="gaming.jsp">
请输入玩家数量: <input type="number" id="user_num" name="user_num" value="" required
onkeyup="if(this.value.length==1){this.value=this.value.replace(/[^1-9]/g,'')}else{this.value=this.value.replace(/\D/g,'')}"
onafterpaste="if(this.value.length==1){this.value=this.value.replace(/[^1-9]/g,'')}else{this.value=this.value.replace(/\D/g,'')}"/>
<input type="submit" value="完成" >
</form>
玩家数字的输入控制:
原理与上面的差不多,只不过在这里改用了onkeypress、onkeyup和onblur
gaming.jsp
<form id="add_num" name="add_num" action="add_ok.jsp">
<table>
<thead>
<tr>
<th>玩家</th>
<th>数字</th>
</tr>
</thead>
<%
for (int i = 0; i < user_num; i++) {
out.print(
"<tr><td><input type='text' id='user' name='user' value='玩家"+i+"' required ></td><td><input type='password' id='num' name='num' value='' required
onkeypress='if(!this.value.match(/^[\\+\\-]?\\d*?\\.?\\d*?$/))this.value=this.t_value;else this.t_value=this.value;if(this.value.match(/^(?:[\\+\\-]?\\d+(?:\\.\\d+)?)?$/))this.o_value=this.value'
onkeyup='if(!this.value.match(/^[\\+\\-]?\\d*?\\.?\\d*?$/))this.value=this.t_value;else this.t_value=this.value;if(this.value.match(/^(?:[\\+\\-]?\\d+(?:\\.\\d+)?)?$/))this.o_value=this.value'
onblur='if(!this.value.match(/^(?:[\\+\\-]?\\d+(?:\\.\\d+)?|\\.\\d*?)?$/))this.value=this.o_value;else{if(this.value.match(/^\\.\\d+$/))this.value=0+this.value;if(this.value.match(/^\\.$/))this.value=0;this.o_value=this.value}'></td></tr>");
}
%>
<tr>
<td><input type="button" name="record" value="历史记录"
onclick="window.location='history.jsp'">
<td><input type="submit" name="submit_btn" value="完成"></td>
</tr>
</table>
</form>
数字输入检查
为了实现对每一位玩家的数字进行检查,重构了add_ok.jsp的框架(原来的代码可以参考上一篇博客)
大致的改动是:增加了一个if-else分支语句,定义一个标志变量illegalnum,初始值为false;首先获取上一个页面传过来的数字,然后遍历检查每一位玩家输入的数字是否在0~100范围内,如果超出范围则显示玩家名字并将illegalnum标志置位true;遍历完成后如果illegalnum==true则显示“重新输入”按钮,οnclick="javascript:history.back(-1);"返回上一个页面,否则按照原add_ok.jsp的正常流程进行。
<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>
<%@page
import="java.sql.*,java.io.*,java.util.ArrayList,java.lang.Math,java.lang.Double"%>
<html>
<head>
<title>保存增加</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<%
String[] user = request.getParameterValues("user");
String[] strnum = (request.getParameterValues("num"));
ArrayList<Double> num=new ArrayList<Double>();
int len=strnum.length;
boolean illegalnum=false;
for(int i=0;i<len;i++){
num.add(new Double(Double.parseDouble(strnum[i])));
if(num.get(i)<0 || num.get(i)>100){
out.print("玩家:'"+user[i]+"'输入数字超出范围!<br>");
illegalnum=true;}
}
if(illegalnum){
%>
<input type='button' value='重新输入'
onclick="javascript:history.back(-1);">
<%
}
else{
request.setCharacterEncoding("UTF-8");
//out.println("页面传递过来的数据获取完毕");
System.out.println("页面传递过来的数据获取完毕");
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException classnotfoundexception) {
classnotfoundexception.printStackTrace();
}
//out.println("加载了JDBC驱动");
System.out.println("加载了JDBC驱动");
try {
Connection conn = DriverManager
.getConnection("jdbc:mysql://localhost:3306/mydb?user=DataUser&password=1q2w3e&useUnicode=true&characterEncoding=UTF-8");
System.out.println("准备statement。");
Statement statement = conn.createStatement();
System.out.println("已经链接上数据库!");
//out.println("Connect Database Ok!!!<br>");
int term=0;
ResultSet rs = statement.executeQuery("select max(term) from game");
if(rs.next()){term=(rs.getInt("max(term)"));}
term+=1;
ArrayList<String> sql =new ArrayList<>();
double sum=0;
for(int i=0;i<len;i++){
sum+=num.get(i).doubleValue();
sql.add("insert into game(term,user,num) values("+ term + ",'" + user[i]+ "'," +num.get(i).doubleValue() + ")");
//out.println("即将执行的SQL语句是:"+sql.get(i));
System.out.println("即将执行的SQL语句是:"+sql.get(i));
statement.executeUpdate(sql.get(i));
}
double goldpoint;
goldpoint=0.618*sum/len;
ArrayList<Double> d=new ArrayList<>();
for(int i=0;i<len;i++){
d.add((Math.abs(num.get(i).doubleValue()-goldpoint)));}
double m=num.get(0).doubleValue();
int k=0;
for(int i=0;i<len;i++){if (num.get(i).doubleValue()<m) {m=num.get(i).doubleValue();k=i;}}
out.println("goldpoint:"+goldpoint+"\nwinner:"+user[k]);
String sql2=new String("insert into outcome(term,goldpoint,winner) values("+ term +","+goldpoint+ ",'" + user[k]+ "')");
//out.println("即将执行的SQL语句是:"+sql.get(i));
System.out.println("即将执行的SQL语句是:"+sql2);
statement.executeUpdate(sql2);
statement.close();
conn.close();
//out.println("Database Closed!!!<br>");
System.out.println("操作数据完毕,关闭了数据库!");
%>
<br>
<input type="button" name="listBtn" value="下一回合"
onclick="window.location='gaming.jsp'">
<%
} catch (SQLException sqlexception) {
sqlexception.printStackTrace();
%>
<br>
<input type="button" name="listBtn" value="游戏失败"
onclick="window.location='gaming.jsp'">
<%
}
//out.println("页面执行完毕!");
System.out.println("页面执行完毕!");
}
%>
</body>
</html>
数据可视化
为了实现数据可视化,重构了record_list2.js(原用于传递每一局游戏的黄金点和赢家,原代码见上一篇博客),借助record_list.js的框架,嵌入了实现可视化的代码。可视化主要是借助echarts工具实现的。
1、黄金点趋势折线图
由于该JavaScript文件本来就是接收get_record2.jsp(用于读取outcome数据表中的内容并传递给record_list2.js,outcome数据表为(回合,黄金点,赢家),源代码参考上一篇博客)用JSON传过来的每一局游戏的黄金点和赢家数据,如图
于是就从这个JSON里面获取黄金点数据,然后将黄金点作为参数进行可视化。
2、赢家统计图
赢家统计图的横坐标为每一位玩家的名字,纵坐标为赢的次数,由于原来的record_list2.js只调用了get_record2.jsp获取了(回合,黄金点,赢家)关系,其中并没有包含所有玩家的名字,所以还要通过
$.getJSON("get_record.jsp?device_id=001",
function(data) {
var i=0;
while(data.aaData[i][0]==1){
gamer[i]=data.aaData[i][1];
i++;
}
});
调用get_record.jsp(用于读取game数据表中的内容并传递给record_list.js,game数据表为(回合,玩家,数字),源代码参考上一篇博客)获取每一位玩家的姓名存入gamer中,如图
然后再在处理get_record2.jsp返回的JSON的function中,将每一局的赢家姓名与所有玩家的姓名进行比较,如果相等,则该玩家的赢数加1.
$.getJSON("get_record2.jsp?device_id=001",
function(data) {
var myChart2 = echarts.init(document.getElementById('main2'));
var g=new Array(gamer.length).fill(0);
for(var i=0;i<data.aaData.length;i++){
for(var j=0;j<gamer.length;j++){
if(data.aaData[i][2]==gamer[j])
g[j]++;
}
}
record_list2.js的完整代码如下:
jQuery(document).ready(function() {
Record.init();
});
/* ================================================================================ */
var MyPage = function() {
var initPageStyle = function() {
$(".page-content-single").css("background-color","#fff");
$(".page-content-single").css("margin-left","0px");
$(".page-content-single").css("margin-top","0px");
$(".page-content-single").css("min-height","600px");
$(".page-content-single").css("padding","25px 20px 10px 20px");
}
return {
init: function() {
initPageStyle();
initLeftMenu("gis");
}
};
}();
var Record = function() {
var html="";
var initRecordStyle = function() {
};
var gamer=new Array();
$.getJSON("get_record.jsp?device_id=001",
function(data) {
var i=0;
while(data.aaData[i][0]==1){
gamer[i]=data.aaData[i][1];
i++;
}
});
var initRecordList=function(){
$('.datatable').dataTable( {
"paging":true,
"searching":false,
"oLanguage": {
"aria": {
"sortAscending": ": activate to sort column ascending",
"sortDescending": ": activate to sort column descending"
},
"sProcessing": "处理中...",
"sLengthMenu": "_MENU_ 记录/页",
"sZeroRecords": "没有匹配的记录",
"sInfo": "显示第 _START_ 至 _END_ 项记录,共 _TOTAL_ 项",
"sInfoEmpty": "显示第 0 至 0 项记录,共 0 项",
"sInfoFiltered": "(由 _MAX_ 项记录过滤)",
"sInfoPostFix": "",
"sSearch": "过滤:",
"oPaginate": {
"sFirst": "首页",
"sPrevious": "上页",
"sNext": "下页",
"sLast": "末页"
}
},
"aoColumns": [{},{},{}],
"aLengthMenu": [[5,10,15,20,25,40,50,-1],[5,10,15,20,25,40,50,"所有记录"]],
"fnDrawCallback": function(){$(".checkboxes").uniform();$(".group-checkable").uniform();},
//"sAjaxSource": "get_record.jsp"
"sAjaxSource": "get_record2.jsp?device_id=001"
});
$.getJSON("get_record2.jsp?device_id=001",
function(data) {
var myChart1 = echarts.init(document.getElementById('main1'));
var myChart2 = echarts.init(document.getElementById('main2'));
var t=new Array();
var g=new Array(gamer.length).fill(0);
var n=new Array();
for(var i=0;i<data.aaData.length;i++){
t[i]=data.aaData[i][0];
n[i]=parseFloat(data.aaData[i][1]);
for(var j=0;j<gamer.length;j++){
if(data.aaData[i][2]==gamer[j])
g[j]++;
}
}
var option2 = {
title: {
text: '赢家统计图'
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'category',
data: gamer
},
yAxis: {
minInterval: 1,
type: 'value'
},
series: [{
data: g,
type: 'bar'
}]
};
var option1 = {
title: {
text: '黄金点变化图'
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: t
},
yAxis: {
type: 'value'
},
series: [
{
type: 'line',
data: n
}
]
};
myChart1.setOption(option1);
myChart2.setOption(option2);
//alert("ID:" + data.aaData);
});
$('.datatable').find('.group-checkable').change(function () {
var set = jQuery(this).attr("data-set");
var checked = jQuery(this).is(":checked");
jQuery(set).each(function () {
if (checked) {
$(this).attr("checked", true);
$(this).parents('tr').addClass("active");
} else {
$(this).attr("checked", false);
$(this).parents('tr').removeClass("active");
}
});
jQuery.uniform.update(set);
});
$('.datatable').on('change', 'tbody tr .checkboxes', function () {
$(this).parents('tr').toggleClass("active");
});
}
return {
init: function() {
initRecordList();
initRecordStyle();
}
};
}();
最后在用于显示的页面record_list.jsp中加入以下语句,定义图像显示的区域
其中main1是黄金点趋势图,main2是赢家统计图
<span id="main1" style="width: 1000px;height:400px;"></span>
<span id="main2" style="width:1000px;height:400px;"></span>