算法题刷累了就来试试游戏吧----2048小游戏----C语言实现

news2025/1/9 2:24:35

目录

1. 代码前的准备 

2. 游戏思路及代码分析

 2.1 game.h 代码分析

2.2 test.cpp代码分析

3. 完整代码

3.1 game.h

3.2 game.cpp

3.3 test.cpp


嘿嘿嘿,写游戏还是挺高兴的撒,如果你还不知道2048这个小游戏的规则,那么快去试试吧。不然你怎么会写得出来嘞!

2048 游戏 - 在线玩在线玩 2048 游戏!使用箭头键移动瓷砖并尝试获得 2048 瓷砖!https://2048.gg/zh

1. 代码前的准备 

本游戏使用了C++的EasyX图形库,咱就默认你对其有一定的了解哈!!

这里有EasyX的参考文档:EasyX 文档 - solidroundrecthttps://docs.easyx.cn/zh-cn/solidroundrect

 在写代码之前你需要下载EasyX的图形库:EasyX Graphics Library for C++https://easyx.cn/

下载完成后安装:

系统会自动检测你的电脑上可以安装EasyX的IDE,选择你要安装的即可。你也可以安装EasyX的参考文档。 

2. 游戏思路及代码分析

 1):我们还是选择创建一个头文件:game.h,两个.cpp文件:game.cpp和 test.cpp,EasyX不支持.c文件哦!!!

2):我们需要把项目的字符集改成“多字节字符集”,不然的话EasyX图形库里面的某些函数会报错的哦!

 2.1 game.h 代码分析

 在game.h这个文件中我们需要包含头文件,定义宏,声明函数等等。

3-10行:包含必须的头文件 。

12-21行:定义宏。关于棋盘的计算肯定是难不到大家的相关的大小我就帮大家测量好了。

24-39行:将不同数字的颜色弄到枚举类型里面,RGB就是Red,Green,Blue,一种表示颜色的方式,不懂的可以参考文档,然后再把枚举类型的成员装到数组里面(这部分代码在后面一点),这样我们就可以通过下标访问枚举类型的成员啦。

two_10:2的10次方。

42行往后就是函数的声明。 

2.2 test.cpp代码分析

 2行:包含game.h才能用声明的枚举类型等等。

5行:将枚举类型的每一个成员装到一个数组里面。作用后面一点点讲。

8行:得讲讲游戏思路才能理解,当我们按下按键把数字向一个方向移动时会存在相同数字合并的情况。因为不同的数字对应不同的颜色,我们需要对合并成的数字进行判断,这时我们就可以遍历num这个数组,找到和合并产生的数字相等的下标,再在arr数组里面找到该下标的枚举类型成员就找到了该数字的颜色。

10行:POINT就是EasyX定义好的一个结构体:按住Ctrl 点击 POINT 转到他的定义,我们可以看到就是一个x坐标,一个y坐标。用其创建一个二维数组方便后续画出每一个格子,后面一点将哈。

14行:定义一个bool类型的变量,来判断是否产生新的数字。

我们都知道2048这个游戏只有数字产生了移动,或者出现了数字的合并才会生成一个新的数字。具体用法后面一点讲。 

58-65行:没有啥好讲的,不懂请参考EasyX文档 。

67行:棋盘就是一个二维数组,我们将数字的改变通过二维数组存储,然后打印棋盘。

69行:MapInit函数:在game.cpp中定义的哈

 遍历POINT pos 这个二维数组,并赋初值。初始化就是将每个格子左上角点的x坐标,y坐标存储到pos这个二维数组里面,以后在画格子的时候,只要再次 遍历pos数组,再在每个x,y的基础上加上格子的宽度就是格子右下角的的坐标了!

42行:生成2或者4数字装到map数组里面,因为是一开始游戏,所以生成两个。这部分代码比较简单可以看最后面完整代码里面的注释理解。你们肯定也写得出来撒。

70行:一个循环,我们需要重复移动嘛,直到2048,嘿嘿。

72行:DrawMap函数:画棋盘的函数,同样在game.cpp中定义的哈。

我们遍历装数字的map数组 ,然后在里面遍历num数组,找到相应数字的下标,通过下标找到对应数字的颜色,然后就是画格子,再把数字画到格子的中间位置就可以了。

怎样把数字弄到格子的中间位置,想必学前端的伙伴肯定知道。

 得到了偏移量就可以将数字也画上去啦。同样下面的画法有不懂的请参考EasyX文档。

73行:ControlGame函数:这个函数在test.cpp中就行。

 18行:从键盘读取一个字符。然后通过switch语句不同的键进行不同方向的移动就行。

因为移动的方法大同小异,咱分析一个就行以MoveUp为例哈,该函数定义在game.cpp中哈。

向上移动的整个步骤我们拆分着来:

1)合并:就拿向上移动来说,我们就需要对map数组的每一列进行向上移动的操作,所以需要在向上移动的外面套一层循环,对每一列进行向上移动。我们在对每一列操作时,维护两个数begin和end不同的值指向每一列的不同位置。我们先让begin指向map数组该列中第一个不为0的位置,即对该列进行检索,可以定义一个函数FindUpIndex来检索该列第一个不为0的位置,(注意:向上移动就是从该列行下标为0的位置开始检索,向下的话就从格子数MAX_GRID_NUM - 1的位置开始),然后以同样的方法从begin+1的位置检索,给给end如果该列的begin和end下标位置的数字相等,那么就合并同时将end的位置改成0,如果过程中没有检索到begin或者end那么,就不合并即可。

2)移动:这部分代码的 解释在完整代码里面的注释有详解。

 74行:Judge函数:用来判断是否产生新的数字。

 flag1在ControlGame函数对数字移动过程中,如果发生了数字的合并,数字的移动就会被改成true,如果flag1为true那么满足产生新的数字的条件,生成一个新的数字。

3. 完整代码

3.1 game.h

#pragma once

//包含easyx的头文件
#include<graphics.h>
//包含监听键盘输入的头文件
#include<conio.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<stdbool.h>

//每行的格子数目
#define MAX_GRID_NUM 4
//格子的宽度
#define GRID_WIDTH 100
//格子之间的间隙
#define GAP 15
//整个棋盘宽度
#define MAP_WIDTH_SIZE MAX_GRID_NUM * GRID_WIDTH + GAP * (MAX_GRID_NUM + 1)
//赢的数字
#define WIN_NUMBER 2048


//枚举格子的颜色,不同数字对应不同颜色
enum GridColor
{
	zero = RGB(205, 193, 180), //无数字的颜色
	two_1 = RGB(238, 228, 218), //数字2的颜色
	two_2 = RGB(237, 224, 200), //数字4的颜色
	two_3 = RGB(242, 177, 121), //数字8的颜色
	two_4 = RGB(245, 149, 99),  //数字16的颜色
	two_5 = RGB(246, 124, 95),  //数字32的颜色
	two_6 = RGB(246, 94, 59),  //数字64的颜色
	two_7 = RGB(237, 207, 114), //数字128的颜色
	two_8 = RGB(237, 204, 97), //数字256的颜色
	two_9 = RGB(255, 0, 128), //数字512的颜色
	two_10 = RGB(145, 0, 72), //数字1024的颜色
	two_11 = RGB(242, 17, 158), //数字2048的颜色
};


//棋盘的初始化
void MapInit(int map[MAX_GRID_NUM][MAX_GRID_NUM]);

//随机的初始数字2或者4
int GetInitNum();

//生成随机的2或者4
void CreateNumber(int map[MAX_GRID_NUM][MAX_GRID_NUM]);

//画出棋盘
void DrawMap(int map[MAX_GRID_NUM][MAX_GRID_NUM]);

//对是否产生移动进行判断
void Judge(int map[MAX_GRID_NUM][MAX_GRID_NUM]);

//向上移动
void MoveUp(int map[MAX_GRID_NUM][MAX_GRID_NUM]);

//向下移动
void MoveDown(int map[MAX_GRID_NUM][MAX_GRID_NUM]);

//向左移动
void MoveLeft(int map[MAX_GRID_NUM][MAX_GRID_NUM]);

//向右移动
void MoveRight(int map[MAX_GRID_NUM][MAX_GRID_NUM]);

//判断游戏是否结束,返回-1代表你输了,1代表你赢了,0代表游戏继续。
int GameOver(int map[MAX_GRID_NUM][MAX_GRID_NUM]);

3.2 game.cpp

#define _CRT_SECURE_NO_WARNINGS

#include"game.h"

extern GridColor arr[12];

extern int num[];

extern POINT position[MAX_GRID_NUM][MAX_GRID_NUM];

//判断移动
extern bool flag1;

void CreateNumber(int map[MAX_GRID_NUM][MAX_GRID_NUM])
{
	while (1)
	{
		int x = rand() % MAX_GRID_NUM;
		int y = rand() % MAX_GRID_NUM;
		if (map[x][y] == 0)
		{
			map[x][y] = GetInitNum();
			break;
		}
	}
}

//初始化棋盘
void MapInit(int map[MAX_GRID_NUM][MAX_GRID_NUM])
{
	int i, j;
	for (i = 0; i < MAX_GRID_NUM; i++)
	{
		for (j = 0; j < MAX_GRID_NUM; j++)
		{
			position[i][j].x = j * GRID_WIDTH + (j + 1) * GAP;
			position[i][j].y = i * GRID_WIDTH + (i + 1) * GAP;
		}
	}

	//生成随机的数,一开始产生两个
	CreateNumber(map);

	CreateNumber(map);

}

//随机的初始数字2或者4
int GetInitNum()
{
	//设置产生4的几率为八分之一
	if (rand() % 8 == 0)
	{
		return 4;
	}
	else
	{
		return 2;
	}
}

//画出棋盘
void DrawMap(int map[MAX_GRID_NUM][MAX_GRID_NUM])
{
	int i, j;
	for (i = 0; i < MAX_GRID_NUM; i++)
	{
		for (j = 0; j < MAX_GRID_NUM; j++)
		{
			for (int m = 0; m < 12; m++)
			{
				if (map[i][j] == num[m])
				{
					setfillcolor(arr[m]);
					solidrectangle(position[i][j].x, position[i][j].y,
						position[i][j].x + GRID_WIDTH, position[i][j].y + GRID_WIDTH);
					if (map[i][j] != 0)
					{
						char ret[5];
						sprintf(ret, "%d", map[i][j]);
						settextstyle(50, 0, "Courier New", 0, 0, 700, false, false, false);
						settextcolor(RGB(119, 110, 101));
						setbkmode(TRANSPARENT);
						int width_move = GRID_WIDTH / 2 - textwidth(ret) / 2;
						int height_move = GRID_WIDTH / 2 - textheight(ret) / 2;
						outtextxy(position[i][j].x + width_move, position[i][j].y + height_move, ret);
					}
				}
			}
		}
	}
}

//上移时找map数组值不为0的下标
int FindUpIndex(int map[MAX_GRID_NUM][MAX_GRID_NUM], int begin, int i)
{
	//找不到就改变begin,如果找完一行或者一列都没找到返回-1
	while (map[begin][i] == 0 && begin < MAX_GRID_NUM)
	{
		begin++;
	}
	if (begin == MAX_GRID_NUM)
	{
		return -1;
	}
	else
	{
		return begin;
	}
}

//下移时找map数组值不为0的下标
int FindDownIndex(int map[MAX_GRID_NUM][MAX_GRID_NUM], int begin, int i)
{
	//找不到就改变begin,如果找完一行或者一列都没找到返回-1
	while (map[begin][i] == 0 && begin >= 0)
	{
		begin--;
	}
	if (begin < 0)
	{
		return -1;
	}
	else
	{
		return begin;
	}
}

//左移时找map数组值不为0的下标
int FindLeftIndex(int map[MAX_GRID_NUM][MAX_GRID_NUM], int begin, int i)
{
	//找不到就改变begin,如果找完一行或者一列都没找到返回-1
	while (map[i][begin] == 0 && begin < MAX_GRID_NUM)
	{
		begin++;
	}
	if (begin == MAX_GRID_NUM)
	{
		return -1;
	}
	else
	{
		return begin;
	}
}

//右移时找map数组值不为0的下标
int FindRightIndex(int map[MAX_GRID_NUM][MAX_GRID_NUM], int begin, int i)
{
	//找不到就改变begin,如果找完一行或者一列都没找到返回-1
	while (map[i][begin] == 0 && begin >= 0)
	{
		begin--;
	}
	if (begin < 0)
	{
		return -1;
	}
	else
	{
		return begin;
	}
}

//向上合并
void MergeUp(int map[MAX_GRID_NUM][MAX_GRID_NUM])
{
	//维护双变量方便合并
	int i, begin, end;
	for (i = 0; i < MAX_GRID_NUM; i++)
	{
		//尝试找begin
		begin = FindUpIndex(map, 0, i);
		//如果返回值不为-1就找到了begin
		if (begin != -1)
		{
			//找到begin的前提下从begin的下面开始找end
			end = FindUpIndex(map, begin + 1, i);
			//如果end为-1无法进入循环
			while (end >= 0 && map[end][i])
			{
				//end不为-1表明找到了两个map数组值不为零的位置,判断他们的关系
				if (map[begin][i] == map[end][i])
				{
					//相等合并
					map[begin][i] += map[end][i];
					map[end][i] = 0;
					//合并则代表下一轮可以产生新的数字
					flag1 = true;
					//合并后end的位置被改成0,从end+1的位置找更新begin
					begin = FindUpIndex(map, end + 1, i);
					if (begin != -1)
					{
						//在找到新的begin的前提下再去找end
						end = FindUpIndex(map, begin + 1, i);
					}
					else
					{
						//找不到end退出循环
						break;
					}
				}
				else
				{
					//begin和end的map数组值不相等,将end给begin,从begin+1的位置找新的end
					begin = end;
					end = FindUpIndex(map, begin + 1, i);
				}
			}
		}
	}
}

//向下合并
void MergeDown(int map[MAX_GRID_NUM][MAX_GRID_NUM])
{
	int i, begin, end;
	for (i = 0; i < MAX_GRID_NUM; i++)
	{
		begin = FindDownIndex(map, MAX_GRID_NUM - 1, i);
		if (begin != -1)
		{
			end = FindDownIndex(map, begin - 1, i);
			while (end >= 0 && map[end][i])
			{
				if (map[end][i] == map[begin][i])
				{
					map[begin][i] += map[end][i];
					map[end][i] = 0;
					flag1 = true;
					begin = FindDownIndex(map, end - 1, i);
					if (begin != -1)
					{
						end = FindDownIndex(map, begin - 1, i);
					}
					else
					{
						break;
					}
				}
				else
				{
					begin = end;
					end = FindDownIndex(map, begin - 1, i);;
				}
			}
		}
	}
}

//向左合并
void MergeLeft(int map[MAX_GRID_NUM][MAX_GRID_NUM])
{
	int i, begin, end;
	for (i = 0; i < MAX_GRID_NUM; i++)
	{
		begin = FindLeftIndex(map, 0, i);
		if (begin != -1)
		{
			end = FindLeftIndex(map, begin + 1, i);
			while (end >= 0 && map[i][end])
			{
				if (map[i][begin] == map[i][end])
				{
					map[i][begin] += map[i][end];
					map[i][end] = 0;
					flag1 = true;
					begin = FindLeftIndex(map, end + 1, i);
					if (begin != -1)
					{
						end = FindLeftIndex(map, begin + 1, i);
					}
					else
					{
						break;
					}
				}
				else
				{
					begin = end;
					end = FindLeftIndex(map, begin + 1, i);
				}
			}
		}
	}
}

//向右合并
void MergeRight(int map[MAX_GRID_NUM][MAX_GRID_NUM])
{
	int i, begin, end;
	for (i = 0; i < MAX_GRID_NUM; i++)
	{
		begin = FindRightIndex(map, MAX_GRID_NUM - 1, i);
		if (begin != -1)
		{
			end = FindRightIndex(map, begin - 1, i);
			while (end >= 0 && map[i][end])
			{
				if (map[i][end] == map[i][begin])
				{
					map[i][begin] += map[i][end];
					map[i][end] = 0;
					flag1 = true;
					begin = FindRightIndex(map, end - 1, i);
					if (begin != -1)
					{
						end = FindRightIndex(map, begin - 1, i);
					}
					else
					{
						break;
					}
				}
				else
				{
					begin = end;
					end = FindRightIndex(map, begin - 1, i);
				}
			}
		}
	}
}

//向上移动
void MoveUp(int map[MAX_GRID_NUM][MAX_GRID_NUM])
{
	MergeUp(map);
	int i, begin;
	for (i = 0; i < MAX_GRID_NUM; i++)
	{
		int temp = 0;
		for (begin = 1; begin < MAX_GRID_NUM; begin++)
		{
			if (map[begin][i] != 0)
			{
				if (map[temp][i] == 0)
				{
					map[temp][i] = map[begin][i];
					map[begin][i] = 0;
					//产生移动
					flag1 = true;
				}
				else
				{
					map[temp + 1][i] = map[begin][i];
					if (temp + 1 != begin)
					{
						map[begin][i] = 0;
						//产生移动
						flag1 = true;
					}
				}
				temp++;
			}
		}
	}
}

//向下移动
void MoveDown(int map[MAX_GRID_NUM][MAX_GRID_NUM])
{
	//合并
	MergeDown(map);
	int i, begin;
	for (i = 0; i < MAX_GRID_NUM; i++)
	{
		int temp = MAX_GRID_NUM - 1;
		for (begin = MAX_GRID_NUM - 2; begin >= 0; begin--)
		{
			if (map[begin][i] != 0)
			{
				if (map[temp][i] == 0)
				{
					map[temp][i] = map[begin][i];
					map[begin][i] = 0;
					//产生移动
					flag1 = true;
				}
				else
				{
					map[temp - 1][i] = map[begin][i];
					//代表他们之间有0的格子,才移动
					if (temp - 1 != begin)
					{
						map[begin][i] = 0;
						//产生移动
						flag1 = true;
					}
				}
				temp--;
			}
		}
	}
}

//向左移动
void MoveLeft(int map[MAX_GRID_NUM][MAX_GRID_NUM])
{
	MergeLeft(map);
	int i, begin;
	for (i = 0; i < MAX_GRID_NUM; i++)
	{
		int temp = 0;
		for (begin = 1; begin < MAX_GRID_NUM; begin++)
		{
			if (map[i][begin] != 0)
			{
				if (map[i][temp] == 0)
				{
					map[i][temp] = map[i][begin];
					map[i][begin] = 0;
					//产生移动
					flag1 = true;
				}
				else
				{
					map[i][temp + 1] = map[i][begin];
					if (temp + 1 != begin)
					{
						map[i][begin] = 0;
						//产生移动
						flag1 = true;
					}
				}
				temp++;
			}
		}
	}
}

//向右移动
void MoveRight(int map[MAX_GRID_NUM][MAX_GRID_NUM])
{
	MergeRight(map);
	int i, begin;
	for (i = 0; i < MAX_GRID_NUM; i++)
	{
		int temp = MAX_GRID_NUM - 1;
		for (begin = MAX_GRID_NUM - 2; begin >= 0; begin--)
		{
			if (map[i][begin] != 0)
			{
				if (map[i][temp] == 0)
				{
					map[i][temp] = map[i][begin];
					map[i][begin] = 0;
					//产生移动
					flag1 = true;
				}
				else
				{
					map[i][temp - 1] = map[i][begin];
					if (temp - 1 != begin)
					{
						map[i][begin] = 0;
						//产生移动
						flag1 = true;
					}
				}
				temp--;
			}
		}
	}
}

//判断是否产生新的数字
void Judge(int map[MAX_GRID_NUM][MAX_GRID_NUM])
{
	if (flag1)
	{
		CreateNumber(map);
		flag1 = false;
	}
}

//判断游戏是否结束,返回-1代表你输了,1代表你赢了,0代表游戏继续。
int GameOver(int map[MAX_GRID_NUM][MAX_GRID_NUM])
{
	int flag = 0;
	int i, j;
	for (i = 0; i < MAX_GRID_NUM; i++)
	{
		for (j = 0; j < MAX_GRID_NUM; j++)
		{
			if (map[i][j] == 0)
			{
				flag = 1;
			}
			else if (map[i][j] == WIN_NUMBER)
			{
				return 1;
			}
		}
	}
	//flag==1代表有空位置,游戏继续
	if (flag == 1)
	{
		return 0;
	}
	else
	{
		int media[MAX_GRID_NUM][MAX_GRID_NUM];
		int k, m;
		for (k = 0; k < MAX_GRID_NUM; k++)
		{
			for (m = 0; m < MAX_GRID_NUM; m++)
			{
				media[k][m] == map[k][m];
			}
		}
		MergeUp(media);
		MergeDown(media);
		MergeLeft(media);
		MergeRight(media);
		if (flag1)
		{
			flag = false;
			return 0;
		}
		else
		{
			return 1;
		}
	}
}

3.3 test.cpp

#define _CRT_SECURE_NO_WARNINGS
#include"game.h"

//将枚举类型的成员装到数组里面方便后续改变棋盘颜色
GridColor arr[12] = { zero,two_1,two_2 ,two_3 ,two_4 ,two_5 ,two_6 ,two_7 ,two_8 ,two_9 ,two_10 ,two_11 };

//方便对棋盘的数字进行判断,并且与GridColor arr[12]数组关联方便填色
int num[] = { 0,2,4,8,16,32,64,128,256,512,1024,2048 };

//调用一个存储x,y坐标的结构体,创建一个数组
POINT position[MAX_GRID_NUM][MAX_GRID_NUM];

//用来判断是否产生新的数字
bool flag1 = false;

void ControlGame(int map[MAX_GRID_NUM][MAX_GRID_NUM])
{
	char key = _getch();
	switch (key)
	{
	case 'W':
	case 'w':
	case 72:
		MoveUp(map);
		break;
	case 's':
	case 'S':
	case 80:
		MoveDown(map);
		break;
	case 'a':
	case 'A':
	case 75:
		MoveLeft(map);
		break;
	case 'd':
	case 'D':
	case 77:
		MoveRight(map);
		break;
	}
}

void ClearArray(int map[MAX_GRID_NUM][MAX_GRID_NUM])
{
	int i, j;
	for (i = 0; i < MAX_GRID_NUM; i++)
	{
		for (j = 0; j < MAX_GRID_NUM; j++)
		{
			map[i][j] = 0;
		}
	}
}

void Test()
{
	//设置随机数的种子
	srand((unsigned int)time(NULL));
	//创建窗口
	initgraph(MAP_WIDTH_SIZE, MAP_WIDTH_SIZE, 0);
	//设置背景颜色
	setbkcolor(RGB(187, 173, 160));
	//用背景色清空屏幕
	cleardevice();
	//棋盘的数组
	int map[MAX_GRID_NUM][MAX_GRID_NUM] = { 0 };
	//棋盘的初始化
	MapInit(map);
	while (1)
	{
		DrawMap(map);
		ControlGame(map);
		Judge(map);
	}
}
int main()
{
	Test();
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/118566.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目管理误区:项目不确定性≠项目风险

项目失败的原因千千万万&#xff0c;罪魁祸首肯定跟“它”脱不了关系&#xff01; 前段时间&#xff0c;偶然看到一个求助贴引发了各路网友&#xff08;项目经理&#xff09;的热议。求助人的问题是&#xff1a;“如何管理项目中的不确定性&#xff1f;” 下面的回复已然偏离答…

Linux系统运行时参数命令--文件IO性能监控

目录 4 文件IO性能监控 4.1 I/O 的两种方式(缓存 I/O 和直接 I/O) 1 缓存 I/O 2 直接 I/O 4.2 监控磁盘I/O的命令 1 iostat IO状态 2 swapon查看分区使用情况 3 df硬盘使用情况 4 du目录文件大小 4.3 文件IO写入频繁案例分析 C/CLinux服务器开发/后台架构师【零声教育…

IU8689+IU5706 单声道100W/立体声60W同步升压+功放IC大功率拉杆音箱应用组合方案

引言 目前中大功率拉杆音箱主要采用12V铅酸电池为供电电源&#xff0c;在电源直供的时候&#xff0c;一般的功放芯片输出功率在20W左右&#xff08;喇叭为4欧、THD10%&#xff09;。超过50W的功率现阶段市场上主要采用升压芯片TPA3116的组合解决方案。 随着竞争的加剧&#x…

如何进行数据可视化图表设计?

如何进行数据可视化图表设计&#xff1f; 对数据人来说&#xff0c;数据可视化是分析理解数据&#xff0c;并最终呈现数据的必修课。本文从以下几个点来说明&#xff0c;如何进行数据可视化图表设计。1、数据背后的故事2、充分理解数据3、多种图表类型设计指南 1数据背后的故…

安克创新能否锚定全球家用储能市场 隆起新的增长极?

提到能源储存&#xff0c;似乎应该是涉及一个国家或者地区的宏大概念。但事实上&#xff0c;储能正在走向家用领域。 近年来&#xff0c;全球能源价格持续高涨&#xff0c;但家用储能的成本却随着锂电等新能源技术的发展在逐渐下降&#xff0c;经济性开始凸显。家用储能在海外…

用HTML制作独一无二的2022回忆旋转相册

目录 前言 效果展示 流程 前言 元旦即将来临&#xff0c;展望2022&#xff0c;我们可以制作一个自己的2022回忆的旋转相册&#xff0c;通过下面的方法来学习吧 效果展示 制作好后&#xff0c;十张相册会在下面旋转&#xff0c;可以改为自己想要放的照片和音乐&#xff0c;制…

史上最简单的推箱子(AS3.0版)

我最熟悉的语言是 AS3.0&#xff0c;现在主要用C&#xff0c;还想学学Python&#xff0c;因此&#xff0c;最近一段时间先用AS3.0实现了最简版的推箱子、贪吃蛇和俄罗斯方块&#xff0c;然后换Python实现&#xff0c;算是熟悉了一下Python的基本用法&#xff0c;最后用C实现&a…

微信小程序 Spdier - OfferShow 反编译逆向(一)

微信小程序 Spdier - OfferShow 反编译逆向&#xff08;一&#xff09; 文章目录微信小程序 Spdier - OfferShow 反编译逆向&#xff08;一&#xff09;前言一、任务说明1.尝试反编译分析出js_code参数的生成方式&#xff0c;用来获取token2.将小程序搜索出来的数据保存至本地e…

【Java开发】Spring Cloud 01 :微服务前提精要

算是新开了一个 Spring Cloud 的坑&#xff0c;本文来源于姚半仙的《Spring Cloud 微服务项目实战》课程&#xff0c;大部分文字内容基于该课程&#xff0c;我的工作可能就是梳理归纳和拓展&#xff0c;希望尽快搞懂相对来说较为简单的 Spring Cloud Alibaba 微服务框架&#x…

决策树及分类原理与划分依据:信息熵、信息增益、信息增益率、基尼值和基尼指数

一、决策树及分类原理 决策树&#xff1a;是一种树形结构&#xff0c;其中每个内部节点表示一个属性上的判断&#xff0c;每个分支代表一个判断结果的输出&#xff0c;最后每个叶节点代表一种分类结果&#xff0c;本质是一颗由多个判断节点组成的树 熵(Entropy) &#xff1a;…

PostgreSQL数据库查询执行——SeqScan节点执行

SeqScan节点代码处于src/backend/executor/nodeSeqscan.c文件中&#xff0c;包含了4个重要函数&#xff1a;ExecInitSeqScan、ExecSeqScan、ExecReScanSeqScan和 ExecEndSeqScan。 ExecInitSeqScan src/backend/executor/nodeSeqscan.c文件中的ExecInitSeqScan函数&#xff0c…

数智化转型给企业带来了什么?

数智化转型的核心价值之一在于对企业创智型活动的赋能&#xff0c;从用户需求趋势预测到创意快速验证&#xff0c;数智化应用具有巨大的价值前景。 因此&#xff0c;尽管目前数智化研发还存在着一定程度的复杂性高、不确定性大等问题&#xff0c;但因数智化研发所具有极大的潜…

C# 网络信息获取

一 网络信息浏览 1 HTTP协议 2 客户端与服务器 3 Request与Response 4 Stream 5 Get与Post 二 一些查看工具 1 Fiddler2 http://www.fidddler2.com 2 其他工具 如NetworkMoniter、Visula Sniffer、httpwatch、WireShark 3 Chrom/FireFox等浏览器F12 ① Chrome 中按F…

泛微齐业成,一文告诉你如何实现全程数字化的预算管理

新一代全程数字化费控管理软件-齐业成预算管理&#xff0c;实现从预算建模、预算编制、预算审批、预算管控、预算变更、预算分析、预算考评的全程数字化管理。 随着数字经济和数字社会的高速发展&#xff0c;预算管理已成为组织内部最重要的经营管理活动之一&#xff0c;其过程…

MySQL窗口函数 和 阿里云日志留存率统计脚本实现

窗口函数的官方描述&#xff1a;窗口函数对一组查询行执行类似聚合的操作。但是&#xff0c;虽然聚合操作将查询行分组为单个结果行&#xff0c;但窗口函数会为每个查询行生成一个结果&#xff0c;发生函数评估的行称为当前行&#xff0c;与发生函数评估的当前行相关的查询行构…

docker镜像导出和导入

1.容器镜像导出 我们先通过docker images查看需要导出的镜像 然后我们使用镜像导出命令 docker save -o /home/备份包名.tar 镜像id或镜像名 # -o(即output) 或 > 表示输出到文件备份镜像可以同时备份多个&#xff0c;空格分隔&#xff0c;这里建议使用镜像名备份&#xff…

DeepMind:用 GNN 学习通用推理算法

文 | 智商掉了一地小孩子才做选择&#xff0c;我的模型全&#xff01;都&#xff01;要&#xff01;近年来&#xff0c;基于深度神经网络的机器学习系统取得了巨大进步&#xff0c;尤其是在以感知为主的任务上。这一领域表现突出的模型通常要在分布中进行泛化&#xff0c;意味着…

Keras深度学习实战(43)——深度Q学习算法

Keras深度学习实战&#xff08;43&#xff09;——深度Q学习算法0. 前言1. Q 学习简介2. 使用 Q 学习进行 FrozenLake 游戏2.1 FrozenLake 环境分析2.2 模型分析2.3 使用 Q 学习算法解决 FrozenLake 问题3. 使用深度 Q 学习进行 CartPole 游戏3.1 问题分析3.2 模型分析3.3 使用…

通讯录怎么恢复?在 手机上检索找回已删除的电话号码的3种方式

不幸的是&#xff0c;我从手机中删除了一些号码&#xff0c;因此它也从帐户中删除了。我想恢复它们或将我的帐户恢复到一周前我拥有这些号码的日期。— 来自 Android 用户 像上述用户一样&#xff0c;您可能已经删除了一些电话号码&#xff0c;但希望有一天能恢复它们。这种事故…

python数据分析及可视化(十八)Power BI(数据获取、整理、清洗以及可视化、Power Query的基本操作、删除及增加列)

Power BI 微软推出的数据分析和可视化工具&#xff0c;用于在组织中提供见解&#xff0c;是商业分析工具&#xff0c;让视觉对象分析触手可及&#xff0c;可以创建交互式数据可视化效果和报表&#xff0c;连接数百个数据源、简化、准备数据等&#xff0c;并提供相应的分析&…