我看你骨骼惊奇,送你本武林秘籍--《三子棋至多子棋的扩展》
前言:
这篇文章将会手把手带你从程序设计需求出发,用代码思维来实现三子棋游戏。
每一步都会按照这个流程进行
1.要实现什么(程序设计需求)
例如:三子棋的游戏规则
用户的游玩方式
2.怎么实现
代码设计
首先我们要用到模块化编程
我们初学者一开始的编程都是将代码放在一个.c文件里面 这样不利于代码后续的维护
以及不利于工程的合作(如果要合作完成项目就都得在一个文件里面写),既不方便代码的交流,又拖慢了工程进度,效率和产能都不高。
模块化编程
而把代码分装到.h和.c文件 并且把代码实现的功能分装到不同的.c文件
.h文件放函数的声明
.c文件放函数的定义
在多人合作的时候,各自写的功能不同的函数就可以分装到不同的.c文件里,并在最后整合到一个工程下面,这样提高了效率,又提高了产能,且在后续也极容易维护代码。当要优化某个功能或修改某个功能实现的bug,就从对应的.c文件下更改即可
我们创建三个项目 分别是test.c 用来放游戏的启动代码 game.c用来放实现游戏的函数 game.h用来放函数的声明
Ⅰ编写游戏启动代码
1.实现什么--->玩家先看到游戏菜单---> 菜单中显示 选择进入或退出游戏
---> 主程序做到完成一局游戏后可以继续游玩
2.程序设计
#include <stdio.h>
void menu()
{printf("**********************\n");printf("**1.play 0.exit **\n");printf("**********************\n");
}int main()
{int input = 0; do{menu(); //打印菜单printf("请输入-->> ");scanf("%d", &input);switch (input){case 1: //注意case 1作为分支入口 case 和数字之间是有空格的,如果没有,编译器无法解析game(); //调用游戏函数,开始游戏break;case 0:printf("exit\n");break;default:printf("please try again\n");}} while (input); //注意while后有分号return 0;
}
do while语句来实现玩完一局游戏不过瘾可以继续玩
且实现在代码一运行起来时直接执行一次循环体中的内容而不是先判断。
而do while的循环体内就放整个游戏的启动代码
menu()函数实现菜单的打印
玩家选择----》用switch分支语句实现
这里同时将input(玩家的输入也即选择)作为while循环的入口和switch分支语句的入口
好处是当玩家选择1时可以进入游戏( 进到case 1 调用game()函数)
而当玩家输入非0的数字时仍可以进入循环中,即仍停留在菜单界面。
Ⅱ游戏函数的实现
一.
实现棋盘的打印
1.实现什么---》 棋盘在最开始只有分割线 里面存放空格
2.怎么实现 ---》 从最简单的三子棋开始 就是打印一个3x3的九宫格 对于棋盘的存储功能,我们用二维数组来实现,对于棋盘的格子,线条界面,我们用循环来编写
#include <stdio.h>#define ROW 3 //应输入表达式E0029报错 一般情况是宏定义常量加了分号,去掉就好
#define COL 3void InitBoard(char board[ROW][COL], int row, int col);
void DisplayBoard(char board[ROW][COL], int row, int col);
在game.h声明函数
因为我们后续要扩展为多子棋,所以我们用宏定义常量来表示棋盘的行和列
在传参时,我们要访问到二维数组上的元素,并且要通过行和列来访问(数组遍历)
所以既要传数组名 又要传行和列
因为在test.c 和game.c文件中都会用到printf 函数 都要引用<stdio.h>头文件
且因为宏定义常量的使用也需要引用头文件
引用自己的头文件的格式是
#include "game.h"
用双引号引头文件 ,结尾没有分号
所以为了简洁
我们将所有需要调用的库函数的头文件都放在game.h中 这样一来在test.c 和game.c中只要引用一个game.h文件即可
在game.c中 实现函数的定义和调用
先在game函数中创建数组,然后调用初始化棋盘和打印棋盘函数
void game()
{char board[ROW][COL] = { 0 };InitBoard(board, ROW, COL);//初始化棋盘DisplayBoard(board, ROW, COL);//打印棋盘}
在game.h文件声明
棋盘的初始化
两个for循环遍历行和列 并给数组赋值' ' 字符空格,注意字符要加单引号
void InitBoard(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0; i < ROW; i++){int j = 0;for (j = 0; j < COL; j++){board[i][j] = ' '; //初始化棋盘,将棋盘的位置都设为空格}}
}
棋盘的打印
要打印的棋盘理论上张这样
我们用循环来实现打印
因此要对这个棋盘进行拆分
现在我们先将一行(对ROW行的循环)棋盘分成两个部分
一个是数据行(顾名思义用来存放下的棋子的数据)
一个是分割线
这里还要注意分割线在最后一行是不打印的,所以在循环中可以通过加if()语句来判断
接下来进一步对列(COL)的循环进行拆分
对于数据行我们将 空空空竖杠 即( |) 看作一组 但是在最后一组中竖杠不打印(if语句来实现)
对于分割行 将杠杠杠竖杠 即(---|)看作一组 同样在最后一组不打印竖杠(if语句来实现)
下面进行代码编写
void DisplayBoard(char board[ROW][COL], int row, int col)
{int i = 0;
//数据行的打印for (i = 0; i < ROW; i++){int j = 0;for (j = 0; j < COL; j++){printf(" %c ", board[i][j]);//打印数据if(j < COL - 1) //竖杠在最后一列不打印printf("|");//printf函数打印字符串直接用""引用}printf("\n"); //注意一行完成打印要进行分行//分割线的打印for (j = 0; j < COL; j++){if (i < ROW - 1) //分割线在最后一行不打印{printf("---");if (j < COL - 1) //竖杠最后一列不打印printf("|");}}printf("\n"); //一行分割线打印完成也要进行分行}
}
这里分享个我在编写时的错误
就是在打印字符的时候可以
1. printf("%c", '|');
这样以单字符形式打印
也可以直接按字符串的打印形式
2. printf("|");
这样子打印
但是在1.这种打印的方式中 一定要注意竖杠要加上单引号 表示字符 不然编译器会报错
二.游戏过程的实现
1.要实现什么---》 先玩家输入 ---》 棋盘显示玩家下的棋子 ---》
电脑随机输入 ---》显示电脑下的棋子
在玩家和电脑下完每一步棋子之后都判断游戏是继续还是有一方胜利还是平局
如果是继续 就继续重复上面的步骤
如果是哪一方赢了就打印相应的结果(平局同理)
2.怎么实现
我们继续在game()函数中完善代码,进行整个游戏过程的代码实现
要实现不断下棋,我们就把所有函数都放进while(1)的循环中,当游戏结束就用break跳出循环
跳出循环后我们再判断是哪一种情况
我们用字符类型 ret 来接受 判断输赢的函数 IsWin()的返回值
这样就可以根据返回值来判断结果
这里我们暂时不设置返回值是什么
void game()
{char ret = 0;char board[ROW][COL] = { 0 };InitBoard(board, ROW, COL);//初始化棋盘DisplayBoard(board, ROW, COL);//打印棋盘while (1){//玩家下棋PlayerMove(board, ROW, COL);DisplayBoard(board, ROW, COL);//判断输赢ret = IsWin(board, ROW, COL);if (ret != )break;//电脑下棋ComputerMove(board, ROW, COL);DisplayBoard(board, ROW, COL);//判断输赢ret = IsWin(board, ROW, COL);if (ret != )break;}
if (ret == )printf("player win!\n");else if (ret == )printf("computer win!\n");elseprintf("平局\n");
先在game.h函数声明全部的函数
这里面的<stdlib.h>和<time.h>函数是生成随机数要引用的头文件
我们后续会详说
接着继续在game.c文件完善游戏函数
玩家移动函数
理清思路:
首先提示输入几行几列
我们要考虑到,玩家不知道数组元素下标从0开始,所以在对数组进行访问的时候要做相应的变化
比如玩家输入x=1 y = 2 (一行二列) 对应到实际的二维数组,访问的其实是board[0][1]
上的元素,也即board[x-1][y-1]
判断输入的合法性
1 选择的位置是空格,没有被占用 2.选择的位置是棋盘中的位置
因为存在玩家乱输入值的情况,所以要在合法输入值的情况下才能继续程序
否则就继续循环而当玩家输入的坐标是违规的时候就要给相应提示,并且再次进入到玩家输入的循环中
所以我们依旧用while(1)循环来包含玩家输入的代码
当玩家输入成功就break跳出来
我们将玩家下的棋子定作字符* 也即 '*'
下面是代码实现
void PlayerMove(char board[ROW][COL], int row, int col)
{int x = 0;int y = 0;printf("请玩家输入--> \n");while (1){scanf("%d %d", &x, &y); //sacnf函数输入几个数的时候要用逗号隔开if (x >= 1 && x <= ROW && y >= 1 && y <= COL)//输入坐标在棋盘内{//在玩家输入到棋盘内的位置时判断位置是否被占用if (board[x - 1][y - 1] == ' ') //可落子{board[x - 1][y - 1] = '*'; break;}elseprintf("此处被占用 \n");//选择位置被占用}elseprintf("输入坐标非法,请重试 \n");}
}
电脑移动
理清思路:
要电脑随机下子,我们就要生成随机数
生成随机数在0~ROW-1 0~COL-1 (符合数组访问元素下标)这时就要用到取余%对随机数取ROW 的余 就会得到 0 ~ROW-1 这些数
电脑也要判断落子处是否为空格这是因为生成的随机数坐标是可能恰好在已经下过的地方的
当这一次生成的随机坐标不符号要求时,继续生成随机数坐标
所以将生成随机数坐标放进while(1)循环,当生成合法坐标时落子并break跳出循环
我们将电脑的棋子定作字符# 也即 '#'
随机数的生成
rand 函数会随机生成 0--32767之间的数字
在使用rand()生成随机数函数的时候要先对函数进行初始化,即给生成的随机数定一个起点(这是规定)
也就是在调用rand()函数之前要先用srand()完成随机数的初始化
而srand()的初始化也要接受一个随机数
而要使生成的随机数每次都不同,我们用时间戳来给这个随机数赋值
时间戳就是
调用计算机时间需引用头文件<time.h>
因为我们只需要一个随机数的起点 所以srand只使用一次即可,将其放在test.c文件中的函数启动main函数里面
srand((unsigned int) time(NULL));
这个就是对随机数进行起点的初始化代码
因为time函数的返回值类型是time_t
unsigned int给time强制转换类型;time指针为空指针类型
至于为什么这么设置我们暂时不用深究
对时间戳的解释
在计算机中,「时间戳」一般是指 Unix 时间戳,即自从 Unix 纪元(格林威治时间 1970 年 1 月 1 日 00:00:00)到当前时间的秒数
通俗来讲就是一个当前时间的标志
下面是代码实现
void ComputerMove(char board[ROW][COL], int row, int col)
{while (1){int x = rand() % row; //电脑生成的坐标要在循环里面int y = rand() % col; //不然生成一次不成功又不会继续生成的话,游戏就没法继续下去了if (board[x][y] == ' ')//是空的才能下{board[x][y] = '#';break;}}}
最后就是判断输赢以及游戏是否继续的函数的实现
IsWin函数的实现
我们先设计IsWin()函数的返回值。
玩家赢返回'*'
电脑赢返回'#'
平局返回'Q'
游戏继续返回'C'
将玩家和电脑输出的标志作为返回值的好处是在判断的时候可以直接返回用于判断的任意一个元素
完善game()函数的代码逻辑
void game()
{char ret = 0;char board[ROW][COL] = { 0 };InitBoard(board, ROW, COL);//初始化棋盘DisplayBoard(board, ROW, COL);//打印棋盘while (1){//玩家下棋PlayerMove(board, ROW, COL);DisplayBoard(board, ROW, COL);//判断输赢ret = IsWin(board, ROW, COL);if (ret != 'C')break;//电脑下棋ComputerMove(board, ROW, COL);DisplayBoard(board, ROW, COL);//判断输赢ret = IsWin(board, ROW, COL);if (ret != 'C')break;}if (ret == '*')printf("player win!\n");else if (ret == '#')printf("computer win!\n");elseprintf("平局\n");}
实现IsWin()函数
我们要判断行 列 和 两个斜对角线上的三个(多个)字符是否是相同的
前两个可以用循环来实现,后两个可直接用if函数判断
这里要注意的是在满足三个连成直线的字符相等的时候还要同时满足这个字符不是空格
最后要实现的是平局和继续游戏的判断
对于平局 ,我们可以对整个数组进行遍历 当发现没有一个格子是空格时,就是平局
我们可以额外编写个Is_Full()函数来进行判断
而继续游戏的返回值只要放在最后面即可 因为当前面的任意一个if函数成功返回了相应字符
都会直接结束这个IsWin函数,也就轮不到'c'这个继续游戏标志字符的返回了
反之,当前面的if函数都没有成功返回,代码执行到最后只剩return 'c';
游戏也就自然继续进行了
Is_Full() 函数的实现
int Is_Full(char board[ROW][COL], int row, int col)
{//满了返回1//没满返回0int i = 0;int j = 0;for (i = 0; i < row; i++){for (j = 0; j < col; j++){if (board[i][j] == ' ') //只要找到有一个空格就返回0,没满,结束函数return 0;}}return 1; //当没有一个格子是空格时就满了
}
下面是代码实现,可以看到我们直接用任意一个用户来判断的元素作为返回值,大大简化了判断
char IsWin(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0; i < row; i++){if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' '){return board[i][0]; }}//判断列for (i = 0; i < col; i++){if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' '){return board[0][i]; }}//判断对角线if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')return board[1][1];if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')return board[1][1];if (Is_Full(board, row, col) == 1) return 'Q';return 'C';
}
当然,这样子写判断,其实把代码写死了,只能判断三子棋
接下来我们进行扩展
三子棋扩展为多子棋
思维实现:
多子棋的判断
拆分判断元素 实质为判断相邻的三个(n个)棋子是否相等
拆分为判断相邻的两个棋子是否相等,并且将这个判断放入循环 循环次数比判断的个数小1
我们可以用拼积木的方式去理解
假设有三块相同的积木 而现在要把他们拼成一个整体(一条直线)
每两块积木间需要一个用来连接的积木
而三块积木的拼接就需要两个连接积木、
再转化为三子棋 就是判断胜利的情况就是找到两个等号(==)将数组上三个元素的大小关系建立起来
于是就可以推广到多子棋 无非就是找拼接用的积木数量够不够多而已
在循环体中加入计数器count即可实现
这里我们在前面的基础上只要修改IsWin()函数即可实现多子棋
char IsWin(char board[ROW][COL], int row, int col)
{//判断行int i = 0;for (i = 0; i < row ; i++) //但是行的遍历判断总次数没少,因为要看完所有行嘛{int j = 0;int count = 0; //count 放在这里初始化,在每遍历一行后都会重置for(j = 0; j < col-1;j++ )//判断条件比总数少一,是因为判断三个数相等其实只要两个等号{if (board[i][j] == board[i][j + 1] && board[i][j] != ' '){count++;}if (count == row-1) //count 就是用来计算累积了几个等号的return board[i][j];}}//遍历列//逻辑与判断行相同for (i = 0; i < col ; i++) {int j = 0;int count = 0; for (j = 0; j < row - 1; j++) //注意此时i是列 j是行{if (board[j][i] == board[j + 1][i] && board[j][i] != ' '){count++;}if (count == row - 1) return board[j][i];}}//判断对角线// \对角线int count = 0;int j = 0;for (i = 0; i < row - 1; i++){if (board[i][j] == board[i + 1][j + 1] && board[i][j] != ' '){count++;if(count < row - 1)j++;}if (count == row - 1)return board[i][j];}// /对角线int countb = 0;j = col - 1;for (i = 0; i < row - 1; i++) //三子棋时进行2次循环{if (board[i][j] == board[i + 1][j - 1] && board[i][j] != ' '){countb++;if(countb < row -1) //这样在判断第二个等号完后就不会改变board[i][j]的数j--;}if (countb == row - 1)return board[i][j];}if (Is_Full(board, row, col) == 1) return 'Q';return 'C';
}
改进后的代码在判断行与列的时候还是比较简单的
但是在判断对角线的时候就复杂许多
以其中一个对角线的判断循环为例
因为对角线的每一组等号的判断都涉及两行两列(这点和行,列的判断不同)
所以count(计数器)的初始化不能放在循环体内,否则在下一个循环就会被重置。
其次因为计数器放在for循环外部的原因,且两个对角线的判断都涉及计数器的使用,
所以我们创建count 和countb来分别用于两个对角线的判断
然后在对角线的判断中,我们不能再使用两个for循环来遍历数组
因为有从右上到左下的斜对角线,行和列的坐标变换是不一样(不同步)的,如果单纯用两个循环,很难实现这种复杂的特殊情况的判断
而是只用一个循环,并在if()判断语句中实现下标位置的改变
所以我们的数组列下标 j 也要初始化在for循环外部
而如果要实现一组等号的判定完成后,进行下一组的判定,我们就可以将 j 的变化放进和count/countb 一起的if函数内部
并且为了在完成最后一组等号的计算后,不改变原来的数组下标,即为了不影响最终返回值的结果,我们还要给下标 j 的改变再加个if判断
总结:
三子棋虽然简单,但是运用到的知识非常多,在进行整个游戏的实现时,要时刻保持目的性,即时刻想着我要实现什么,我要怎么实现,可以运用哪些知识。
最后眼看不如手动,许多知识你看似懂了,但是只有自己动手做起来才知道自己的分量。
且不要害怕犯错,多运用调试功能自己解决问题,实在找不到问题再去请教别人。
真不要怕自己会被别人嘲笑,比如我在调试代码的时候就发现我的if函数进不去,而是直接跳过了,自己排了两个小时,没发现问题,就去问老师。最后发现就是if()后面多加了个分号。这是因为现在编译器比较灵敏,当你代码没写完整的时候就会报错,可能当时我看编译器报错了,然后又看到括号后面少了个分号,就完全没在意if()后面是不能加分号的。这两个小时也算是血的教训吧,编写代码的时候真的不能心急,要确保每一步都符合语法规则且符合逻辑。虽然两个小时都没找出bug,但是在这两个小时里面,我重复过了很多遍代码,确定了每一个代码块实现的功能,谁又能说这些“浪费”的时间是完全没用的呢。在调试代码的过程中你会学到很多很多东西,这些都是被人没法教你的。
希望这篇文章对你有所帮助,感谢您的阅读。
可不可以给个点赞呢qwq
下面是完整代码
game.h文件
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 3 //应输入表达式E0029报错 一般情况是宏定义常量加了分号,去掉就好
#define COL 3void InitBoard(char board[ROW][COL], int row, int col);
void DisplayBoard(char board[ROW][COL], int row, int col);
void PlayerMove(char board[ROW][COL], int row, int col);
void ComputerMove(char board[ROW][COL], int row, int col);
char IsWin(char board[ROW][COL], int row, int col);
int Is_Full(char board[ROW][COL], int row, int col);
test.c文件
#include "game.h"void menu()
{printf("**********************\n");printf("**1.play 0.exit **\n");printf("**********************\n");
}int main()
{int input = 0;srand((unsigned int)time(NULL));do{menu(); //打印菜单printf("请输入-->> ");scanf("%d", &input);switch (input){case 1:game(); break;case 0:printf("exit\n");break;default:printf("please try again\n");}} while (input);return 0;
}
game.c文件
#include "game.h"void InitBoard(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0; i < ROW; i++){int j = 0;for (j = 0; j < COL; j++){board[i][j] = ' '; //初始化棋盘,将棋盘的位置都设为空格}}
}void DisplayBoard(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0; i < ROW; i++){int j = 0;for (j = 0; j < COL; j++){printf(" %c ", board[i][j]);if(j < COL - 1)printf("|");}printf("\n");//打印分割线for (j = 0; j < COL; j++){if (i < ROW - 1){printf("---");if (j < COL - 1)printf("|");}}printf("\n");}
}void PlayerMove(char board[ROW][COL], int row, int col)
{int x = 0;int y = 0;printf("请玩家输入--> \n");while (1){scanf("%d %d", &x, &y); if (x >= 1 && x <= ROW && y >= 1 && y <= COL){//在玩家输入到棋盘内的位置时判断位置是否被占用if (board[x - 1][y - 1] == ' ') //可落子{board[x - 1][y - 1] = '*';break;}elseprintf("此处被占用 \n");//选择位置被占用}elseprintf("输入坐标非法,请重试 \n");}
}void ComputerMove(char board[ROW][COL], int row, int col)
{while (1){int x = rand() % row; //电脑生成的坐标要在循环里面int y = rand() % col; //不然生成一次不成功又不会继续生成的话,游戏就没法继续下去了if (board[x][y] == ' ')//是空的才能下{board[x][y] = '#';break;}}}int Is_Full(char board[ROW][COL], int row, int col)
{//满了返回1//没满返回0int i = 0;int j = 0;for (i = 0; i < row; i++){for (j = 0; j < col; j++){if (board[i][j] == ' ')return 0;}}return 1;
}char IsWin(char board[ROW][COL], int row, int col)
{//判断行int i = 0;for (i = 0; i < row ; i++) //但是行的遍历判断总次数没少,因为要看完所有行嘛{int j = 0;int count = 0; //count 放在这里初始化,在每遍历一行后都会重置for(j = 0; j < col-1;j++ )//判断条件比总数少一,是因为判断三个数相等其实只要两个等号{if (board[i][j] == board[i][j + 1] && board[i][j] != ' '){count++;}if (count == row-1) //count 就是用来计算累积了几个等号的return board[i][j];}}//遍历列//逻辑与判断行相同for (i = 0; i < col ; i++) {int j = 0;int count = 0; for (j = 0; j < row - 1; j++) //注意此时i是列 j是行{if (board[j][i] == board[j + 1][i] && board[j][i] != ' '){count++;}if (count == row - 1) return board[j][i];}}//判断对角线// \对角线int count = 0;int j = 0;for (i = 0; i < row - 1; i++){if (board[i][j] == board[i + 1][j + 1] && board[i][j] != ' '){count++;if(count < row - 1)j++;}if (count == row - 1)return board[i][j];}// /对角线int countb = 0;j = col - 1;for (i = 0; i < row - 1; i++) //三子棋时进行2次循环{if (board[i][j] == board[i + 1][j - 1] && board[i][j] != ' '){countb++;if(countb < row -1) //这样在判断第二个等号完后就不会改变board[i][j]的数j--;}if (countb == row - 1)return board[i][j];}if (Is_Full(board, row, col) == 1) return 'Q';return 'C';
}//game函数包含数组的定义初始化,因为数组的使用与棋盘的打印是联系在一起的
void game()
{char ret = 0;char board[ROW][COL] = { 0 };InitBoard(board, ROW, COL);//初始化棋盘DisplayBoard(board, ROW, COL);//打印棋盘while (1){//玩家下棋PlayerMove(board, ROW, COL);DisplayBoard(board, ROW, COL);//判断输赢ret = IsWin(board, ROW, COL);if (ret != 'C')break;//电脑下棋ComputerMove(board, ROW, COL);DisplayBoard(board, ROW, COL);//判断输赢ret = IsWin(board, ROW, COL);if (ret != 'C')break;}if (ret == '*')printf("player win!\n");else if (ret == '#')printf("computer win!\n");elseprintf("平局\n");}
我看你骨骼惊奇,送你本武林秘籍--《三子棋至多子棋的扩展》相关推荐
- 少年, 我看你骨骼惊奇, 送你一套精选 Java 面试题
临近过年, 疫情再次来袭, 大家注意保护好自己和家人, 而各大公司也在年前进行 2020 年最后的人才储备工作, 这里帅帅送大家一套精选的 Java 考试题目希望对大家有所帮助. 如果学不动了, 就想 ...
- 少年,我看你骨骼惊奇,必是练武奇才,将来维护宇宙正义
亲,我看你骨骼惊奇,必是练武奇才, 将来维护宇宙正义与和平的重任就交给你了! 我这有失传已久的武林秘籍,看在咱们有缘, 只卖你五百块钱一本,买两本的话给你打99折, 一次性购买五本以上的话包邮喔亲! ...
- 少侠,看你骨骼惊奇,传你几招IT武林绝技,可好?
年少时的你们 大概都有个武侠梦 金庸古龙小说翻拍的电视剧 也没少看吧 其中的武功绝学 你们学到一招半式了吗 在玩"王者农药"的时候 光靠一堆李白,是不是被 "王昭君+黄忠 ...
- 年轻人,看你骨骼惊奇,我这有一份来自阿里的Android开发学习指南,不仅能让你月入5w,度过中年危机都不是问题!
摘要 很简单,我这有一份来自阿里程序员佛系月薪5w指南,看你骨骼惊奇,印堂光亮,一看就是将要大富大贵.走向人生巅峰之人,就不收你钱了,一个点赞就送给你怎么样? 缘起 为什么写下这篇文章? 疫情自爆发以 ...
- 我看你骨骼惊奇,是块做CTO的材料! 我看还是不要了
(图片摄影:方飞) 作者:Eduards Sizov 来源:公众号码农翻身 编辑:Emma Eduards Sizovs写的这篇关于职业发展的文章,其中一些观点有失偏颇,但是令人耳目一新,分享给大家. ...
- Java 集合经典面试题。少侠,我看你骨骼惊奇,是难得的代码奇才,来看了我的博客那岂不是如虎添翼?
List 为什么 arraylist 不安全? 我们查看源码发现 arraylist 的 CRUD 操作,并没有涉及到锁之类的东西.底层是数组,初始大小为 10.插入时会判断数组容量是否足够,不够的话 ...
- 看你骨骼惊奇,这里有一套 Canvas 粒子动画方案了解一下?
导语:在日常的开发过程中,我们会常常会用到canvas来制作一些动画特效,其中有一个动画种类,需要我们生成一定数量,形状类似且行为基本一致的粒子,通过这些粒子的运动,来展现动画效果,比如:下雨,闪烁的 ...
- Cocos Creator 3D 插件教程(一):看你骨骼惊奇,我带你上车!
本系列教程转载自公众号[许彦峰],作者是 Cocos "插件小王子"许彦峰,策划良久.文章从个人角度,阐述对新插件体系从零入门的全新理解.阅读时间 15 分钟左右,部分教程附带简洁 ...
- 亦正亦邪,骨骼惊奇的LGG
文章目录 亦正亦邪,骨骼惊奇的LGG 对急性肠胃炎没啥用 帮助生骨 未来可期 作者简介 猜你喜欢 写在后面 亦正亦邪,骨骼惊奇的LGG LGG,这既不是你家雪地靴UGG的山寨品牌,也不是"老 ...
最新文章
- Embarcadero公司花2千3百万买下Borland的开发工具业务
- 有限状态机(使用状态模式C++实现)
- 手挽手带你学React:三档 React-router4.x的使用
- Qt之QObjectCleanupHandler使用介绍
- Apollo进阶课程㉗丨Apollo控制技术详解——控制理论
- 高级定时器的各种框图和HAL库重要结构
- 产品半夜发现bug让程序员加班,程序员应如何回应?
- sql server从一个数据库复制一张表到另外一个数据库
- spring boot Junit5单元测试
- CorelDRAWX4的VBA插件开发(八)更改尺寸与移动
- html 画excel表格边框,只需五分钟!用Excel做出美观的表格
- 红警win10黑屏和不显示菜单栏问题 只有声音没有图像的解决
- 一米村长讲故事机器人_主持人李锐推出“村长讲故事”APP,已入驻喜马拉雅
- C语言从入门到精通第17天(指针和数组联用)
- 中国电子学会2022年12月份青少年软件编程Python等级考试试卷三级真题(含答案)
- Xilinx Bit文件格式详解
- canvas雨滴绘制总结(三)
- 入门人工智能该读哪些书?五份AI经典书单
- NSDate-日期类nbsp;OC——第七天(1)
- PotPlayer 无损截取视频片段
热门文章
- 云计算、大数据和人工智能知识普及
- Jmeter组件-线程组(Thread Group)
- WebEx网络视频会议
- js的重点3:轮播图(实现多个图无缝连接播放)、使用Swiper轮播图效果
- linux中sl是什么命令,Linux系统使用sl命令制作火车动态桌面的技巧
- 以太坊后合并时代 15个概念带你深入了解以太坊2.0
- python dataframe增加一行_python - 在pandas.DataFrame中添加一行
- 视频超分、图像超分常用数据集Vimeo90K的下载及处理
- vert.x java post请求无法接收到post请求body中的参数
- 一文弄懂Hive中谓词下推(on与where的区别)