Canvas作图原生Js五子棋

前段时间学弟来问五子棋的课程设计,找到了之前写的,效果如下:

Canvas作图原生Js五子棋_第1张图片
绘制一个1024*768大小的画布,画布背景颜色填充为#e0c590



	
	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();
			}
		}
人机功能博主对五子棋原理也不是太懂,实现的算法忘了以前在哪摘录的,大致是对每个点落子进行一个评估,本身会定义一个分值表,对不同的连子会有不同的估分,电脑会在估分最高的点下子。但本算法的不足之处在于只能对当前情况进行预估,而我们下棋往往会对棋局发展进行评估,向后看几步决定最有利的走法。而这套算法显然不能做到这一点导致电脑并不是很强,数据结构的大神可以编写更好的算法来供大家分享。写的太啰嗦了,看完的估计没几个吧,博主也是有空了消遣下,以后也会进行别的更新,大家可以进行技术的讨论,更好的提升能力。
下载链接http://download.csdn.net/download/u014066084/9767671

你可能感兴趣的:(游戏)