前段时间学弟来问五子棋的课程设计,找到了之前写的,效果如下:
cheet
//创建一个1024*768大小的画布,填充颜色#e0c590
这里采用的canvas绘制出棋盘,使用table或div也可达到同样效果;使用moveTo/lineTo画线条,arc画圆,beginPath/closePath防止画的点线之间的相互影响,算好坐标就可以绘制出较为标准的棋盘
ctx.font = "30px Arial";
ctx.fillStyle = "#000";
ctx.fillText("不知道叫什么名字的五子棋游戏",100,60); //绘制实心标题文字
for (var i = 0; i < 15; i++) {
ctx.beginPath();
ctx.moveTo(100, 100 + 40 * i);
ctx.lineTo(660, 100 + 40 * i);
ctx.stroke();
ctx.closePath(); //循环绘制棋盘横线
ctx.beginPath();
ctx.moveTo(100 + 40 * i, 100);
ctx.lineTo(100 + 40 * i, 660);
ctx.stroke();
ctx.closePath(); //循环绘制棋盘竖线
}
ctx.beginPath();
ctx.arc(220,220,5,0,2*Math.PI);
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.arc(540,220,5,0,2*Math.PI);
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.arc(380,380,5,0,2*Math.PI);
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.arc(220,540,5,0,2*Math.PI);
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.arc(540,540,5,0,2*Math.PI);
ctx.fill();
ctx.closePath(); //绘制棋盘5个小黑点
ctx.font = "10px Arial";
function textStyle(num){
if (num < 10)
return " " + num;
else
return num;
}
var letter = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O']
for (var i = 15; i >= 1; i--) {
ctx.fillText(textStyle(i), 80, 105 + 40 * (15 - i)); //绘制左侧数字
ctx.fillText(letter[i - 1], 655 - 40 * (15 - i), 680); //绘制下方字母
}
通过createLinearGradient和addColorStop实现了颜色渐变的效果,通过gameType判断游戏类型,其值存入本地cookie中,通过函数cookie(key, value, options)读取,特别注意:
谷歌浏览器不支持本地cookie操作(在google上无法实现游戏功能,需要换其他浏览器),改代码使用localstorage或其他方式存储gameType的值
var gameType = cookie("gameType");
if (gameType == null) gameType =1;
var grd = ctx.createLinearGradient(750, 0, 910, 0);
grd.addColorStop(0,"#e0c590");
grd.addColorStop(0.5,"#ffa500");
grd.addColorStop(1,"#e0c590");
var grd2 = ctx.createLinearGradient(750, 0, 910, 0);
grd2.addColorStop(0,"#e0c590");
grd2.addColorStop(0.5,"#74cdeb");
grd2.addColorStop(1,"#e0c590");
ctx.font = "30px Arial";
ctx.beginPath();
if (gameType == 1) {
ctx.fillStyle = grd2;
ctx.fillRect(750,140,160,80);
ctx.fillStyle = "#f00";
ctx.fillText("人人对战",770,190);
}else{
ctx.fillStyle = grd;
ctx.fillRect(750,140,160,80);
ctx.fillStyle = "#000";
ctx.strokeText("人人对战",770,190);
}
ctx.closePath();
ctx.beginPath();
if (gameType == 2) {
ctx.fillStyle = grd2;
ctx.fillRect(750,260,160,80);
ctx.fillStyle = "#f00";
ctx.fillText("人机对战",770,310);
}else{
ctx.fillStyle = grd;
ctx.fillRect(750,260,160,80);
ctx.fillStyle = "#000";
ctx.strokeText("人机对战",770,310);
}
ctx.closePath();
ctx.beginPath();
ctx.fillStyle = grd;
ctx.fillRect(750,380,160,80);
ctx.fillStyle = "#000";
ctx.strokeText("重新开始",770,430);
ctx.closePath();
function cookie(key, value, options) {
if (typeof value === "undefined") { // 读取
var cookies = document.cookie.split("; ");
for (var i = 0, len = cookies.length; i < len; i++) {
var cookie = cookies[i].split("=");
if (decodeURIComponent(cookie[0]) === key) {
return decodeURIComponent(cookie[1]);
}
}
return null;
}
options = options || {};
var cookie = encodeURIComponent(key) + "=" + encodeURIComponent(value);
if ((typeof options.expires) !== "undefined") {
if (typeof options.expires === "number") {
var days = options.expires,
t = options.expires = new Date();
t.setDate(t.getDate() + days);
}
cookie += ";expires=" + options.expires.toUTCString();
}
if (typeof options.path !== "undefined")
cookie += ";path=" + options.path;
if (typeof options.domain !== "undefined")
cookie += ";domain=" + options.domain;
if (options.secure)
cookie += ";secure";
document.cookie = cookie;
}
现在棋盘绘制完成,开始实现逻辑功能,将棋盘各个位置同二维数组对应,初始化棋盘信息
for (var i = 0; i < 15; i++) {
position[i] = new Array();
for (var j = 0; j < 15; j++) {
position[i][j] = 0; //0表示所在位置无棋子
}
}
利用e.clientX与e.clientY获取事件源坐标,通过处理和Math.round实现每个棋子对应的有效点击范围,gameType的值可以判断游戏类型,通过传入坐标以及棋子颜色drawChess(x,y,chessColor)绘制对应棋子
function chessCheck(e){
e = e || arguments.callee.caller.arguments[0] || window.event;
//arguments.callee.caller.arguments[0]为firefox的兼容处理
var ordinateX = e.clientX - 100 + getScroll("left"),
ordinateY = e.clientY - 100 + getScroll("top"), //获取鼠标点击坐标点
x = Math.round(parseFloat(ordinateX / 40)),
y = Math.round(parseFloat(ordinateY / 40));
//将坐标点转换为旗子位置
clickType(ordinateX, ordinateY);
if (ordinateX < 0 || ordinateY < 0 || ordinateX > 560 || ordinateY > 560 || position[x][y] != 0) {
//console.log("error");
//排除棋子位置超出棋盘和当前位置棋子已经存在的情况
return;
}
if (gameState === "over") {
if (countNum % 2 == 0) alert("游戏结束,白色方获胜!");
else alert("游戏结束,黑色方获胜!");
return;
}
if (gameType == 1) { //人人对战,判断当前棋子颜色
if (countNum % 2 == 0) chessColor = "#000";
else chessColor = "#fff";
countNum ++;
drawChess(x,y,chessColor);
}else if(gameType ==2){ //人机对战
chessColor = "#000";
drawChess(x,y,chessColor);
AIplay();
}
}
function drawChess(x,y,chessColor){
ctx.fillStyle = chessColor;
ctx.beginPath();
ctx.arc(100 + x * 40, 100 + y * 40, 12, 0, 2 * Math.PI, true)
ctx.fill();
ctx.closePath();
if (chessColor == "#000") {
position[x][y] = 2;
isWin(x, y, chessColor);
//if(isWin(x,y,chessColor)) win();
//console.log("黑落子的位置位于:",x,y);
}
if (chessColor == "#fff") {
position[x][y] = 1;
isWin(x, y, chessColor);
//if(isWin(x,y,chessColor)) win();
//console.log("白落子的位置位于:",x,y);
}
}
每次落子之后进行游戏是否结束的判断,包括横竖左斜右斜四个方向的判断,任何一个方向连成5子游戏结束,这里以横方向作为介绍。当落子后从本身开始向左遍历有多少相同颜色的棋子,再从本身开始向右遍历有多少相同颜色的棋子,两次结果想加大于6(落下的棋子加了两次)结束游戏,否则依次进行竖左斜右斜的判断。胜利条件成立执行isOver()实现五子连线以及结束提示
function heng(temp, x, y) {
var arr = new Array(4),
count = 0;
for (var i = x; i >= 0; i--) {
arr[0] = i;
arr[1] = y;
if (position[i][y] == temp) count++;
else {
arr[0] = ++i;
break;
}
}
for (var i = x; i <= 14; i++) {
arr[2] = i;
arr[3] = y;
if (position[i][y] == temp) count++;
else {
arr[2] = --i;
break;
}
}
if(count >= 6)
isOver(arr[0], arr[1], arr[2], arr[3], temp);
} //分别向落子两边遍历结果想加
同样通过点击获取的坐标判断右侧功能键(人人,人机,重新开始)的触发
function clickType(ordinateX,ordinateY){ //实现右侧选择项功能
var x = ordinateX,
y = ordinateY;
if (x >= 650 && x <= 810 && y >= 40 && y <= 120) {
cookie("gameType", 1, {expires:7});
location.reload();
}
if (x >= 650 && x <= 810 && y >= 160 && y <= 240) {
cookie("gameType", 2, {expires:7});
location.reload();
}
if (x >= 650 && x <= 810 && y >= 280 && y <= 360) {
if (confirm("重新开始游戏?"))
location.reload();
}
}
人机功能博主对五子棋原理也不是太懂,实现的算法忘了以前在哪摘录的,大致是对每个点落子进行一个评估,本身会定义一个分值表,对不同的连子会有不同的估分,电脑会在估分最高的点下子。但本算法的不足之处在于只能对当前情况进行预估,而我们下棋往往会对棋局发展进行评估,向后看几步决定最有利的走法。而这套算法显然不能做到这一点导致电脑并不是很强,数据结构的大神可以编写更好的算法来供大家分享。写的太啰嗦了,看完的估计没几个吧,博主也是有空了消遣下,以后也会进行别的更新,大家可以进行技术的讨论,更好的提升能力。