p5结合做自动井字小游戏

Posted 阿宝逃离地球

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了p5结合做自动井字小游戏相关的知识,希望对你有一定的参考价值。

p5结合做自动井字小游戏

B站上看到的教程:https://www.bilibili.com/video/BV12i4y157wh?p=1
视频教程的作者的关于这个小游戏的Github地址:https://github.com/CodingTrain/website/tree/main/CodingChallenges/CC_149_Tic_Tac_Toe

一、效果展示

在视频教程中,作者是用p5的网页编辑器(需要科学上网才比较好加载出来的样子),我是直接用html+js调用p5的库来制作网页查看效果。

效果如下图所示

二、思路整理

做完之后整理一下思路如下:
1.确定全局所需变量:井字面板内容数组变量(board),存放数组全部元素的下标索引的数组(available),玩家内容(players),当前玩家内容(currentPlayer),绘制井字格的宽高(w,h)
2.绘制背景
3.初始化各个变量的值
4.确度每个方格的位置,遍历面板数组的内容,查看其内容,绘制“X"/“O”,每一个方格出现内容就判断以下有无输赢情况出现,有的话则结束游戏,无的且方格已经填满完成就退出游戏,宣布结局,如果方格还没有被填满的话就进入下一回合。

三、代码部分和源代码理解

1.函数大体框架

//file:sketch.js
let board;//面板内容
let availble;//存储面板元素下标
let players;//玩家
let currentPlayer;//当前玩家
let w,h;

function equals3(a,b,c){}//判断是否构成一条线
function checkWinner(){}//判断胜出玩家
function nextTurn(){} //下一回合

function setup(){}
function draw() {}

2.初始化各个变量和细化函数体

//file:sketch.js
//井字格的元素内容用二维数组来表示board[3][3],一开始元素皆为空''
let board = [
	['','',''],
	['','',''],
	['','',''],
];
let availble = [];//存储面板元素下标,一开始声明为空,在函数setup()中初始化每一个元素的值
let players = ['X','O'];//玩家代表内容'X'或'O'
let currentPlayer;//当前玩家

function setup(){
	//绘制画布大小400*400
	createCanvas(400,400);
	background(200);
	frameRate(1);//指定每秒显示的帧数
	//绘制井字格
	w = width/3;
	h = height/3;
 
	//画背景图层的井字线
	line(w,0,w,height);
	line(w*2,0,w*2,height);
	line(0,h,width,h);
	line(0,h*2,width,h*2);
	
	//初始化available和currentPlayer
	currentPlayer = floor(random(players.length));//随机指定玩家代表内容的下标
	for(let i=0;i<3;i++){
		for(let j=0;j<3;j++){
			available.push([i,j]);//存储board中的每个元素的位置
		}
	}
}

3.绘制背景和遍历面板元素确认其是否输赢以及下一回合

判断成线方格的内容是否相同的函数

//file:sketch.js
function equals3(a,b,c){
	return a==b && b==c && a!='';
}//判断是否构成一条线

判断输赢的函数

function checkWinner(){
	let winner = null;//结果
	
	for(let i=0;i<3;i++){
		//横向判断
		if(equals3(board[i][0],board[i][1],board[i][2])){
			winner = board[i][0];
		}
		//纵向判断
		if(equals3(board[0][i],board[1][i],board[2][i])){
			winner = board[0][i];
		}
	}
	//对角线
	if (equals3(board[0][0],board[1][1],board[2][2])) {
		winner = board[0][0];
	}
	if (equals3(board[2][0],board[1][1],board[0][2])) {
		winner = board[2][0];
	}

	// available.length==0是确保可以填的方格无了
	if (winner == null && available.length == 0) {
		return 'tie';//平局
	}else{
		return winner;//‘X’或‘O’获胜
	}
}

进入下一回合函数

//file:sketch.js
function nextTurn(){
	//floor()函数,向下取最大整数;在数组available长度中随机取一个作为参数
	let index = floor(random(availble.length));
	//利用随机获得的参数作来指定面板中玩家选择绘制‘X’或‘O’的位置(available的元素),并在数组删除该元素
	let spot = available.splice(index,1)[0];
	let i = spot[0];
	let j = spot[1];
	console.log(i,j);//可以查看控制台输出d的i,j,即选择的位置
	//指定玩家在该位置所要的符号
	board[i][j] = players[currentPlayer];
	//更新currentPlayer的值,使其与上一个符号的下标相反
	currentPlayer = (currentPlayer+1)%players.length;
}

进入执行setup()和draw()

//file:sketch.js
function draw(){
	

	//遍历面板元素绘制玩家的下落方格,i是行,j是列,x是横向坐标,y是纵向坐标
	for(let i=0;i<3;i++){
		for(let j=0;j<3;j++){
			//每个方格的中心坐标点
			let x = j*w+w/2;
			let y = i*w+w/2;
			//声明一个坐标点变量,等于面板数组中对应坐标点的值
			let spot = board[i][j];
			//如果面板中对应元素的值为‘O’,则在该坐标点画圆
			if(spot == players[1]){
				ellipse(x, y, w/2);
			}else if (spot == players[0]) {
				//如果面板中对应元素的值为‘X’,则在该坐标点画两条交叉的线
				let xr = w/4;
				line(x - xr,y-xr,x+xr,y+xr);
				line(x + xr,y-xr,x-xr,y+xr);
			}
		}
	}
	
	//判断输赢
	let end = checkWinner();
	if(end!=null){
		noLoop();//停止持续执行draw()中的代码
	    let resultP = createP(''); 用给定的内部HTML在DOM中创建<p></p>元素。
	    resultP.style('font-size', '32pt');
		if(end == 'tie'){
			resultP.html('Tie!');
		}else{
			resultP.html(end +'wins!');
		}
	}else{
		nextTurn();
	}
}

网页文件

<!-- file:index.html -->
<!DOCTYPE html>
<html>
	<head>
		<!-- 调用p5函数库的函数大的第一种方式: 绝对文件路径-->
		<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
		<!--第二种:相对路径,可以在官网下载这个p5.min.js文件,放在同一个文件夹下-->
		<script src="p5.min.js"></script>
		<meta charset="utf-8"/>
	</head>
	<body>
		<script src="sketch.js"></script>
	</body>
</html>

四、一点点修改

增加一个按钮和增加一个对应使用的函数,其它的尝试都失败了,先存着最简单粗暴的办法

<button id="but" onclick="myfun()">Again</button>
//file:sketch.js
function myfun() {
	// location.replace(location.href);
	location.reload();
	console.log("--------");
}

五、收获和总结

  • 了解到JS中的关键词let和var的区别,在这个小游戏中的源码中变量都是用let关键字来声明的,而在之前我一直都习惯用var来声明变量

对于javascript 块作用域

  • 通过 var 关键词声明的变量没有块作用域。在块 {} 内声明的变量可以从块之外进行访问。
    {
    var x = 10;
    }
    // 此处可以使用 x
  • 在 ES2015 之前,JavaScript 是没有块作用域的,可以使用 let 关键词声明拥有块作用域的变量。
    在块 {} 内声明的变量无法从块外访问。
    {
    let x = 10;
    }
    // 此处不可以使用 x

对于重新声明变量

  • 使用 var 关键字重新声明变量会带来问题, 在块中重新声明变量也将重新声明块外的变量。
    var x = 10; // 此处 x 为 10
    {
    var x = 6; // 此处 x 为 6
    }
    // 此处 x 为 6
  • 使用 let 关键字重新声明变量可以解决这个问题,在块中重新声明变量不会重新声明块外的变量
    var x = 10; // 此处 x 为 10
    {
    let x = 6; // 此处 x 为 6
    }
    // 此处 x 为 10
  • 熟悉JS中floor()、splice()、数组的push()函数的使用,在使用中脑海复习了一遍栈的入栈和出栈知识。

  • 了解到这个井字游戏中的实现原理

  • 熟悉了一些p5的函数比如floor()、random()、createP()、loop()、noLopp(),区别其与在JavaScript的同名函数的区别

  • floor():计算最接近的小于或等于参数值的int值。映射到Math.floor()。
    语法:floor(n)。
    而在JavaScript里的floor()调用语法是Math.floor(x)
  • random():返回一个随机浮点数。接受0、1或2个参数。如果没有给出参数,则返回一个从0到(但不包括)1的随机数。如果给定一个参数,并且它是一个数字,则返回一个从0到(但不包括)该数字的随机数。如果给定一个参数,并且它是一个数组,则返回该数组中的随机元素。
    语法:random([min], [max])或者random(choices)
    JavaScript:random() 方法可返回介于 0 ~ 1 之间的一个随机数。语法:Math.random()
  • createP(): 用给定的内部HTML在DOM中创建<p></p>元素。用于段落长度的文本。
    语法:createP([html]),实例:
    let p = createP(‘this is some text’);
    p.style(‘font-size’, ‘16px’);
    p.position(10, 0);

以上是关于p5结合做自动井字小游戏的主要内容,如果未能解决你的问题,请参考以下文章

井字游戏:人类与计算机

为啥我的井字游戏代码无法检测到有人中奖了?蟒蛇 3

带有链表 C++ 的井字游戏

Python - 确定井字游戏赢家

react入门学习:井字棋游戏(官方文档教程)

react入门学习:井字棋游戏(官方文档教程)