当前位置: 首页 > news >正文

做网站网页优秀营销软文100篇

做网站网页,优秀营销软文100篇,告诉你做网站需要多少钱,怎么做一个网站推广不是,一天点赞就到15了?!好吧,那我更新一下 1. 逻辑 (真的有人会看吗?) 注:本文1.1章为AI生成,如有错误欢迎在评论其指出! 1.1 普通/最初逻辑 这里我们首…

不是,一天点赞就到15了?!好吧,那我更新一下

1. 逻辑

(真的有人会看吗?)

注:本文1.1章为AI生成,如有错误欢迎在评论其指出!

1.1 普通/最初逻辑

这里我们首先考虑最初的逻辑

通常,游戏会有初始化、绘制界面、处理输入、更新游戏状态、判断胜负等部分。所以,可能需要一个二维数组来表示4x4的网格,存储每个格子的数值。然后,需要处理键盘输入,尤其是方向键的控制,这可能需要使用EasyX的键盘事件处理函数。

然后,关于图形绘制部分,每个格子的大小、颜色、数字的显示都需要设计。不同的数字对应不同的颜色,比如2是浅色,4稍深,以此类推。可能需要一个函数根据数字的值返回对应的颜色,然后在绘制时调用。

接下来,处理数字的移动和合并逻辑。这是比较复杂的部分。例如,当用户按下右方向键时,所有数字向右移动,并且相邻相同的数字合并。这个过程需要分步骤处理:先移动,再合并,再移动。每个方向的处理逻辑可能不同,需要分别处理。比如,向右移动的话,每一行从右往左处理,合并相同的数字,并累加分值。这里可能需要四个不同的函数来处理四个方向,或者找到一种统一的方式处理。

另外,每次操作后,需要在空白的位置随机生成一个2或4。需要记录所有空位的位置,随机选择一个位置,并生成数字。这一步需要每次操作后检查是否有空位,没有的话游戏结束。

游戏结束的条件有两个:一是所有格子填满且无法合并,二是有一个格子达到2048,即胜利。需要编写函数来检测这两种情况。

然后,测试各个部分的功能是否正确。比如,移动和合并是否正常,分数是否正确计算,界面是否正常刷新,游戏结束的判断是否准确等。

可能的难点包括正确处理不同方向的移动和合并逻辑,避免重复合并(例如,在一次移动中,合并过的块不能再合并),以及高效地检测游戏是否结束。

另外,用户体验方面,每次操作后需要刷新界面,可能需要添加动画效果,但考虑到EasyX的简单性,可能只做简单的重绘。同时,显示当前分数和最高分数,可能需要全局变量来记录这些信息。

现在,考虑代码的大致结构:

1. 包含头文件:graphics.h用于EasyX,conio.h可能用于键盘输入,time.h和stdlib.h用于随机数生成。

2. 定义常量,如格子大小、窗口尺寸、颜色数组等。

3. 全局变量:网格数组、分数、是否结束等。

4. 初始化函数:初始化窗口,设置随机种子,初始化网格数组,生成初始数字。

5. 绘制函数:根据网格数组绘制每个格子,显示分数。

6. 处理输入的函数:使用GetAsyncKeyState检测方向键,或者使用EasyX的消息循环处理按键。

7. 移动和合并逻辑:四个方向的函数,或者一个通用函数处理不同方向。

8. 生成新数字的函数:随机选择空白位置,生成2或4。

9. 判断游戏结束的函数:检查是否还有移动的可能,即是否有相邻相同的数字或者空位。

10. 主循环:处理输入,更新游戏状态,重绘界面,判断结束条件。

在具体实现时,如何处理方向移动是关键。例如,向右移动时,对于每一行,从右到左遍历,将非零数字移到右边,遇到相同的合并。例如,对于行[2,2,2,2],右移后变成[0,0,4,4]。需要确保每次合并只发生一次,比如四个2在右移时,右边的两个先合并成4,左边的两个再合并成4,所以结果应该是0,0,4,4。

另外,生成新数字的逻辑:每次移动有效(即网格有变化)后才生成新数字,否则不生成。例如,如果用户按下右方向键但所有数字无法移动或合并,那么不生成新数字。

测试案例:比如,当一行是2,2,2,2时,右移后应为0,0,4,4;左移应为4,4,0,0。当有4,4,8,8时,右移合并成0,8,0,16。

在编写移动函数时,可能需要针对每个方向分别处理。例如,右移时,对每一行进行处理,从最右边的列开始,依次向左检查是否有可合并的块。另一种方法是将处理逻辑通用化,比如将行提取出来,处理合并后,再放回去,这样四个方向的处理可以复用同一段代码,只需要调整行列的顺序。

比如,处理一行或一列的函数,参数可以是行的指针或数组,处理完合并后返回新的行和是否有变化。这样四个方向的处理都可以调用同一个函数,但需要调整如何提取行和写回行。

例如,对于右移,每一行处理为从右到左合并;左移则处理为从左到右;上移和下移则需要处理列,可能需要将列转换为行数组处理后再转回去。

这可能需要更复杂的代码结构,但可以减少重复代码。

现在,具体到代码:

定义网格数组:int grid[4][4] = {0};

初始化时,生成两个初始数字,通常是两个2,或者一个2和一个4?

可能首先生成两个2。在初始化函数中,调用生成新数字的函数两次。

生成新数字的函数需要找到所有空位,随机选一个,并随机生成2或4(通常是2的概率更高,比如90%的2,10%的4)。可以使用一个列表保存所有空位的坐标,然后随机选择一个。

绘制函数需要遍历每个格子,根据数字的值绘制不同颜色的矩形,并在中间显示数字。如果数字为0,则不显示。数字的位置可能需要调整,比如居中显示,使用合适的字体大小。

处理方向键输入,比如使用GetAsyncKeyState检测VK_UP、VK_DOWN、VK_LEFT、VK_RIGHT。但需要注意按键的持续检测,可能需要处理为每次按键触发一次移动,而不是按住不放连续移动。或者设置一个延时,避免按键处理过快。

移动处理函数的大致步骤:

对于每个方向,比如右移:

遍历每一行:

将当前行中的非零元素移到右侧,合并相邻相同的数字,并记录是否有移动或合并发生。

例如,处理一行数据:

原始行:2,0,2,4 → 移动后:0,0,4,4,并加分8分(合并两次,2+2=4,4+4=8)。

处理逻辑:

创建一个临时数组,保存处理后的行,初始为0。

从右往左遍历原行,记录上一个非零的值的位置。如果当前元素与上一个相同,则合并,分数增加,并标记合并,然后继续处理剩下的元素。

或者,另一种方法是,先将所有非零元素紧凑排列到右侧,然后遍历合并相邻相同的元素。

比如,步骤1:将行中的非零元素移到右边,如[2,0,2,4] → [0,2,2,4]

然后,从右到左检查相邻元素是否相同:

4和2不同,跳过。2和2相同,合并为4,左边的元素变为0 → [0,0,4,4]。此时,再紧凑一次,但可能不需要,因为合并后的元素已经靠右了。但可能会有中间的空隙,比如[2,2,2,2] → 合并成0,0,4,4,之后不需要再移动。或者,在合并之后,需要再次紧凑元素?

可能需要分步骤处理:

例如,处理右移:

对于每一行:

1. 移除空格:将非零元素移到右边,保持相对顺序。例如,行[2,0,2,4]变为[0,2,2,4]。

2. 合并相邻相同的元素,从右到左:检查当前元素和右边的元素是否相同,如果相同则合并,右边的元素置0,并加分。例如,处理后的行可能变为[0,2, 0, 8],然后再移除空格,变为[0,0,2,8]。

或者,可能需要先紧凑,再合并,再紧凑。

例如,步骤:

原始行:2,0,2,4 → 紧凑后:2,2,4,0 → 从右向左检查合并:

检查位置3(4)和位置2(2),不同;位置2(2)和位置1(2)相同,合并,变为4,位置1置0 → 行变为2,0,4,0 → 再次紧凑到右边:0,0,2,4。这显然不正确。

可能我的处理逻辑有问题。正确的右移合并应该是将两个相邻的相同数字合并一次,比如右边的两个先合并?

或者正确的处理方式应该是这样的:

比如,对于行 [2, 2, 2, 2],右移:

紧凑到右边:四个2 → 没有空格。然后从右往左合并,第三个和第四个合并为4,第三个位置变为4,第四个变为0。然后第二个和第三个合并为4,第二个变为4,第三个变为0。第一个和第二个合并为4,第一个变为4,第二个变为0。结果变成 [0,0,0,8],这显然不对,正确的结果应为 [0,0,4,4]。

所以,正确的合并逻辑应该是在紧凑之后,从右往左或从左往右合并,但每个数字只能合并一次。这可能需要更细致的处理。

正确的处理方式可能是这样的:

在处理一行的时候,先紧凑,将非零元素移到右侧(对于右移的情况),然后从右到左检查相邻元素是否可以合并。例如:

原始行:2,2,2,2 → 紧凑后:2,2,2,2 → 合并:第三个和第四个合并为4,得到2,2,0,4 → 再次紧凑,得到2,2,4,0 → 再合并第二个和第三个?或者可能我的思路错了。

另一种思路是,处理每个元素,只合并一次。例如,对于右移,处理顺序是从右到左:

比如,对于行 [2,2,2,2]:

索引3和2比较,相同,合并为4,索引3为4,索引2置0 → [2,2,0,4]

然后索引1和0比较,相同,合并为4,索引1为4,索引0置0 → [0,4,0,4]

然后再次紧凑,得到 [4,4,0,0],这显然不对。

看来这种方法有问题。正确的做法可能是在处理的时候,每次合并后跳过下一个元素,因为它们已经被处理过了。例如,在合并索引i和i-1之后,i的位置已经被处理,所以下一次处理i-2的位置。

正确的处理步骤可能需要这样的逻辑:

在紧凑之后的行中,从右往左遍历:

例如,处理右移后的紧凑行 [2,2,2,2]:

i从3到0:

- i=3:比较i和i-1的位置(3和2),相同,合并,分数增加,i-1的位置置0,然后i减一,跳过下一个元素。

这样,合并3和2后,i变成2,接着处理i=1:

比较i=1和i=0,合并,得到4,分数增加,i=0。

最后,再次紧凑,得到 [0,0,4,4]。

这可能需要分步骤:

紧凑行 → 合并 → 再次紧凑。

所以,处理行的时候,步骤如下:

1. 紧凑:将非零元素移到右边,中间无空格。

2. 合并相邻的相同元素,从右向左,每个元素只能合并一次。

3. 再次紧凑,填补合并后产生的空格。

例如:

原行:2,0,2,4 → 紧凑后:2,2,4,0 → 合并:检查位置1和2(2和4)不同;位置0和1(2和2)合并为4,位置1置0 → 变为4,0,4,0 → 再次紧凑:4,4,0,0 → 结果为4,4在右侧,即行变成0,0,4,4?

或者,可能我的步骤还是有问题。这时候,可能需要更详细的例子。

比如,原行是 [2, 2, 2, 2],处理右移:

步骤1:紧凑,得到 [2,2,2,2]

步骤2:合并,从右向左:

i=3,比较i=3和i=2,都2,合并为4,i=2置0 → 现在行变为 [2,2,0,4]

然后,i=1,比较i=1和i=0,都2,合并为4,i=0置0 → 行变为 [0,4,0,4]

步骤3:再次紧凑,得到 [4,4,0,0]

这显然不是正确的右移结果。正确的右移结果应该是 [0,0,4,4]。

所以,这说明我的合并逻辑不正确。正确的合并应该是在紧凑之后,从右往左检查,如果相邻元素相同则合并,然后将右边的元素翻倍,左边的元素置0,然后紧凑一次。

例如,正确的处理:

原行:2,2,2,2 → 紧凑后:2,2,2,2 → 合并:

从右往左检查:

i=3和i=2都是2 → 合并,得到4,i=2置0 → 行变为2,2,0,4 → 现在,i=1和i=0都是2 → 合并,得到4,i=0置0 → 行变为0,4,0,4 → 再次紧凑,得到4,4,0,0 → 但这不是正确的结果。正确的结果应该是合并后两次合并,得到0,0,4,4。

哦,问题可能出在合并后的再次紧凑方向。在合并后,可能需要再次向右紧凑,这样在例子中,行变为0,4,0,4 → 再次向右紧凑,得到0,0,4,4。

哦,对的,合并后的步骤是再次向右紧凑。这样,在合并之后,再次将非零元素紧凑到右边。

所以,处理行的步骤是:

1. 向右紧凑(移除空格)

2. 合并相邻的相同元素(从右到左)

3. 再次向右紧凑

这样,在例子中:

原行:2,2,2,2 → 步骤1后:2,2,2,2 → 步骤2:合并i=3和i=2 → 4,i=2=0 → 行变成2,2,0,4 → 合并i=1和i=0 → 4,i=0=0 → 行变成0,4,0,4 → 步骤3:向右紧凑 → 0,0,4,4 → 正确。

所以,在处理每个方向时,都需要进行这三个步骤:紧凑、合并、再紧凑。

现在,如何将这个逻辑应用到每个方向?

例如,处理右移时,每一行都要经历这三个步骤。处理左移时,每一行需要向左紧凑,合并时从左到右,然后再紧凑。处理上下方向时,需要处理列,将其转换为行,处理,再转回去。

这可能需要编写通用函数来处理行或列的合并,根据不同的方向进行调整。

可能的实现步骤:

编写一个函数,处理一个数组(行或列),进行紧凑、合并、再紧凑的操作,并返回是否有任何移动或合并发生。例如:

bool ProcessLine(int line[4], bool reverse) {

// 紧凑:如果reverse为true,则从右往左紧凑,否则从左往右?

// 例如,对于右移,reverse为true,紧凑到右边;左移则reverse为false,紧凑到左边。

// 合并的方向也需要根据reverse来处理。

}

或者,可能需要更明确的参数,如处理方向。

或者,可以将每个行或列转换为一个一维数组,处理完成后再放回去。例如,对于右移和左移,处理行;对于上下移,处理列。

具体来说:

处理右移:

对每一行:

- 创建一个临时数组,将非零元素紧凑到右侧。

- 合并相邻相同的元素(从右到左)。

- 再次紧凑到右侧。

处理左移:

对每一行:

- 紧凑到左侧。

- 合并相邻相同的元素(从左到右)。

- 再次紧凑到左侧。

处理上移:

对每一列:

- 紧凑到上侧。

- 合并相邻相同的元素(从上到下)。

- 再次紧凑到上侧。

处理下移:

对每一列:

- 紧凑到下侧。

- 合并相邻相同的元素(从下到上)。

- 再次紧凑到下侧。

这样,每个方向的逻辑类似,但处理的行或列不同,紧凑的方向不同,合并的方向不同。

为了复用代码,可以编写一个处理一维数组的函数,根据参数决定紧凑的方向和合并的方向。

例如:

// direction参数决定紧凑的方向:0为左,1为右,2为上,3为下?或者用布尔值表示是否反向处理?

或者,更通用地,将数组视为行或列,并指定处理顺序。

可能比较复杂。或者,针对行和列分别处理。

关于主循环的结构:

初始化窗口,初始化网格,生成初始数字。

然后,循环:

- 绘制界面。

- 处理输入。

- 如果有有效的移动,生成新数字。

- 检查游戏是否结束。

在EasyX中,可能需要使用BeginBatchDraw和EndBatchDraw来减少闪烁。

处理输入时,可以使用GetAsyncKeyState来检测按键,但需要注意不要多次触发同一个按键。例如,在用户按下方向键时,只处理一次,直到释放后再按下。

或者,使用EasyX的ExMessage结构来处理消息循环,例如:

ExMessage msg;

while (true) {

if (peekmessage(&msg, EX_KEY)) {

if (msg.message == WM_KEYDOWN) {

switch (msg.vkcode) {

case VK_LEFT:

// 处理左移

break;

case VK_RIGHT:

// 处理右移

break;

case VK_UP:

// 处理上移

break;

case VK_DOWN:

// 处理下移

break;

case VK_ESCAPE:

exit(0);

break;

}

}

}

}

但这种方法可能无法检测到持续按下的键,或者需要处理成每次按键触发一次移动。或者,在每次循环中检测按键状态,而不是通过消息队列。

另一种方法是使用GetAsyncKeyState函数,例如:

if (GetAsyncKeyState(VK_LEFT) & 0x8000) {

// 处理左移

}

同样的问题是如何防止多次触发。可以设置一个标志,表示键是否已经被处理,在键释放后重置。

或者,每次处理完移动后,等待按键释放。这可能不太友好,因为玩家可能需要快速操作。

可能更简单的方式是在每次循环中处理输入,但设置一个延时,比如每次移动后等待100毫秒,避免过于频繁的移动。

但这样可能影响游戏的响应速度。另一种方法是记录上一次处理的时间,只有当时间间隔超过一定值时才处理新的输入。

这可能比较复杂,对于简单的2048游戏,可以接受每次按键触发一次移动,即使按住不放也会有一定间隔。

回到代码,当处理方向键时,调用对应的处理函数,例如:

处理左移:

bool moved = false;

for (int i=0; i<4; i++) {

int originalRow[4];

memcpy(originalRow, grid[i], sizeof(originalRow));

ProcessRowForLeft(grid[i], score);

if (memcmp(originalRow, grid[i], sizeof(originalRow)) !=0) {

moved = true;

}

}

if (moved) {

GenerateNewNumber();

if (CheckGameOver()) {

// 游戏结束

}

}

类似地,其他方向的处理。

现在,生成新数字的函数:

void GenerateNewNumber() {

vector<pair<int, int>> emptyCells;

for (int i=0; i<4; i++) {

for (int j=0; j<4; j++) {

if (grid[i][j] ==0) {

emptyCells.emplace_back(i, j);

}

}

}

if (emptyCells.empty()) {

return;

}

int index = rand() % emptyCells.size();

int value = (rand() %10 ==0) ?4 :2; // 10%的概率生成4

grid[emptyCells[index].first][emptyCells[index].second] = value;

}

检查游戏是否结束:

bool CheckGameOver() {

// 检查是否有空位

for (int i=0; i<4; i++) {

for (int j=0; j<4; j++) {

if (grid[i][j] ==0) {

return false;

}

}

}

// 检查是否有相邻相同的数字

for (int i=0; i<4; i++) {

for (int j=0; j<4; j++) {

int current = grid[i][j];

// 检查右边

if (j <3 && grid[i][j+1] == current) {

return false;

}

// 检查下边

if (i <3 && grid[i+1][j] == current) {

return false;

}

}

}

return true;

}

另外,胜利条件需要检查是否有2048出现:

在每次生成新数字后,检查是否达到2048,如果有,则提示胜利,但游戏可能继续,直到无法移动。或者根据游戏规则,一旦有2048即胜利,可以选择继续游戏。这里可能需要根据用户需求来处理,但通常2048游戏允许玩家继续合并直到无法移动。

所以,在生成新数字后,可以检查是否有2048,并标记游戏胜利。

现在,关于绘制函数:

void Draw() {

cleardevice();

setbkcolor(RGB(250, 248, 239)); // 设置背景色

cleardevice();

int cellSize = 100;

int margin = 10;

for (int i=0; i<4; i++) {

for (int j=0; j<4; j++) {

int x = j * (cellSize + margin) + margin;

int y = i * (cellSize + margin) + margin;

int value = grid[i][j];

COLORREF color = GetColor(value);

setfillcolor(color);

fillrectangle(x, y, x + cellSize, y + cellSize);

if (value !=0) {

TCHAR str[10];

_stprintf(str, _T("%d"), value);

settextstyle(40, 0, _T("Arial"));

setbkmode(TRANSPARENT);

int textWidth = textwidth(str);

int textHeight = textheight(str);

int tx = x + (cellSize - textWidth)/2;

int ty = y + (cellSize - textHeight)/2;

outtextxy(tx, ty, str);

}

}

}

// 显示分数

TCHAR scoreStr[50]

_stprintf(scoreStr, _T("Score: %d"), score);

settextstyle(20, 0, _T("Arial"));

outtextxy(10, 460, scoreStr);

// 如果游戏结束

if (gameOver) {

settextstyle(40, 0, _T("Arial"));

outtextxy(100, 200, _T("Game Over!"));

}

FlushBatchDraw();

}

其中,GetColor函数根据数值返回颜色:

COLORREF GetColor(int value) {

switch (value) {

case 0: return RGB(205, 193, 180);

case 2: return RGB(238, 228, 218);

case 4: return RGB(237, 224, 200);

case 8: return RGB(242, 177, 121);

case 16: return RGB(245, 149, 99);

case 32: return RGB(246, 124, 95);

case 64: return RGB(246, 94, 59);

case 128: return RGB(237, 207, 114);

case 256: return RGB(237, 204, 97);

case 512: return RGB(237, 200, 80);

case 1024: return RGB(237, 197, 63);

case 2048: return RGB(237, 194, 46);

default: return RGB(60, 58, 50);

}

}

现在,将这些部分整合起来,形成完整的代码。

测试过程中需要注意的问题:

1. 是否正确处理移动和合并逻辑?

2. 分数是否正确累加?

3. 是否在移动后生成新数字?

4. 游戏结束条件是否准确?

5. 颜色和显示是否正确?

可能的错误点:

- 在合并时,分数累加是否正确?例如,合并两个2得到4,应加4分,而不是2分?

根据2048的规则,合并后的数字即为所得的分数。例如,2+2=4,得分+4;4+4=8,得分+8,等等。所以在代码中,当合并时,应将合并后的值加到分数中。

例如,在ProcessRowForRight函数中:

当合并两个2时,生成4,分数 +=4;

在代码中的score += temp[i]; 因为temp[i]在合并后被设置为原来的两倍,所以是正确的。

例如,原来的两个2合并为4,此时temp[i] =4,所以score +=4,是正确的。

另一个可能的问题是,在合并时,是否跳过了下一个元素以避免重复合并。例如,在合并i和i-1后,i--,使得循环中的i递减,从而跳过下一个元素。

在ProcessRowForRight的合并循环中:

for (int i =3; i >0; i--) {

if (temp[i] == temp[i-1] && temp[i] !=0) {

temp[i] *=2;

score += temp[i];

temp[i-1] =0;

merged = true;

i--; // 跳过下一个,避免重复合并

}

}

这样,当i=3时,如果合并了i=3和i=2,那么i会减到2,然后循环继续时i变成1,处理i=1的情况。这样避免了重复合并。

这可能正确。

现在,编写主函数:

int main() {

initgraph(500, 600); // 窗口大小500x600,下面显示分数

srand((unsigned)time(NULL));

InitializeGrid();

score =0;

gameOver = false;

bool win = false;

BeginBatchDraw();

while (true) {

Draw();

// 处理输入

if (_kbhit()) {

char ch = _getch();

if (ch == 27) { // ESC退出

break;

}

}

if (GetAsyncKeyState(VK_LEFT) & 0x8000) {

bool moved = false;

for (int i=0; i<4; i++) {

int original[4];

memcpy(original, grid[i], sizeof(original));

ProcessRowForLeft(grid[i], score);

if (memcmp(original, grid[i], sizeof(original)) !=0) {

moved = true;

}

}

if (moved) {

GenerateNewNumber();

if (CheckGameOver()) {

gameOver = true;

}

}

Sleep(100); // 防止连续按键处理过快

}

else if (GetAsyncKeyState(VK_RIGHT) & 0x8000) {

bool moved = false;

for (int i=0; i<4; i++) {

int original[4];

memcpy(original, grid[i], sizeof(original));

ProcessRowForRight(grid[i], score);

if (memcmp(original, grid[i], sizeof(original)) !=0) {

moved = true;

}

}

if (moved) {

GenerateNewNumber();

if (CheckGameOver()) {

gameOver = true;

}

}

Sleep(100);

}

else if (GetAsyncKeyState(VK_UP) & 0x8000) {

bool moved = false;

for (int j=0; j<4; j++) {

int col[4];

for (int i=0; i<4; i++) {

col[i] = grid[i][j];

}

int original[4];

memcpy(original, col, sizeof(original));

ProcessColumnForUp(col, score);

if (memcmp(original, col, sizeof(original)) !=0) {

moved = true;

for (int i=0; i<4; i++) {

grid[i][j] = col[i];

}

}

}

if (moved) {

GenerateNewNumber();

if (CheckGameOver()) {

gameOver = true;

}

}

Sleep(100);

}

else if (GetAsyncKeyState(VK_DOWN) & 0x8000) {

bool moved = false;

for (int j=0; j<4; j++) {

int col[4];

for (int i=0; i<4; i++) {

col[i] = grid[i][j];

}

int original[4];

memcpy(original, col, sizeof(original));

ProcessColumnForDown(col, score);

if (memcmp(original, col, sizeof(original)) !=0) {

moved = true;

for (int i=0; i<4; i++) {

grid[i][j] = col[i];

}

}

}

if (moved) {

GenerateNewNumber();

if (CheckGameOver()) {

gameOver = true;

}

}

Sleep(100);

}

if (gameOver) {

// 可以添加重新开始或退出逻辑

// 例如,按空格键重新开始

if (GetAsyncKeyState(VK_SPACE) & 0x8000) {

InitializeGrid();

score =0;

gameOver = false;

}

}

另外,初始化函数:

void InitializeGrid() {

for (int i=0; i<4; i++) {

for (int j=0; j<4; j++) {

grid[i][j] =0;

}

}

// 生成两个初始数字

GenerateNewNumber();

GenerateNewNumber();

}

可能存在的问题:

在上下移动的处理中,ProcessColumnForUp和ProcessColumnForDown是否正确处理了列数据,并在移动后写回grid?

例如,对于上移:

原列是 [2,2,0,0],处理后应变为 [4,0,0,0],所以将修改后的col写回grid的列。

代码中,当处理列j时:

for (int j=0; j<4; j++) {

int col[4];

for (int i=0; i<4; i++) {

col[i] = grid[i][j];

}

ProcessColumnForUp(col, score);

// 检查是否变化,如果变化则写回

if (memcmp(original, col, ...) {

for (int i=0; i<4; i++) {

grid[i][j] = col[i];

}

moved = true;

}

}

这是正确的。

综上,将这些函数整合起来,应该可以实现2048的基本逻辑。可能还需要调整颜色、字体大小、窗口尺寸等细节,以及处理胜利条件的显示。

最后,测试各个方向移动是否正常,合并是否正确,分数是否正确,游戏结束条件是否触发。

1.2 改进/进阶逻辑

  1. 增加数字:4096、8192……65536
  2. 绘制方块时,绘制圆角
  3. 当格子满时,有的时候可以再次消除
  4. 玩家操作时,用GetAsynckeyState来代替case
  5. 将字符设得大一点
  6. 方块颜色,颜色应遵照原版
  7. 有的时候,颜色偏深,字体应该改成白色;有的时候,颜色偏浅,字体应该改成黑色
  8. 可循环游戏:游戏结束后弹窗,询问是否再来一局
  9. 将分数、最大方块和最高分显示。生成文件,每次开始游戏读入,游戏结束时将数据录入

1.3 完善/再次改进(目前未完成)

  1. 动画:合并方块时的动画及移动方块时的动画   已完成
  2. 文件:文件录入时可能会出bug                          已完成
  3. 最大方块:无法显示最大方块                            已完成
  4. 背景:自行选择背景色主题(白色或黑色)
  5. 出现方块时方块刷新可能有问题

2. 代码

2.1 整体代码

#include <graphics.h>
#include <conio.h>
#include <time.h>
#include <stdlib.h>
#include <vector>
#include <windows.h>
#include <fstream>
#include <sstream>
using namespace std;// 常量定义
const int CELL_SIZE = 120;
const int MARGIN = 15;
const int WINDOW_WIDTH = 600;
const int WINDOW_HEIGHT = 700;
const int CORNER_RADIUS = 20;
const int INFO_HEIGHT = 150;// 颜色定义
const COLORREF SCORE_BG = RGB(158, 174, 187);    // 0x9eaebb
const COLORREF TEXT_LIGHT = RGB(219, 230, 238);  // 0xdbe6ee
const COLORREF TEXT_DARK = RGB(112, 123, 131);   // 0x707b83// 全局变量
int grid[4][4] = { 0 };
int score = 0;
int maxTile = 0;
int highScore = 0;
bool gameOver = false;// 文件操作
void SaveRecord() {ofstream file("2048.save");if (file) {file << highScore << endl;file << maxTile << endl;}
}void LoadRecord() {ifstream file("2048.save");if (file) {file >> highScore;file >> maxTile;}else {highScore = 0;maxTile = 0;}
}// 根据数字获取颜色
COLORREF GetColor(int value) {switch (value) {case 0:    return RGB(205, 193, 180);case 2:    return RGB(238, 228, 218);case 4:    return RGB(237, 224, 200);case 8:    return RGB(242, 177, 121);case 16:   return RGB(245, 149, 99);case 32:   return RGB(246, 124, 95);case 64:   return RGB(246, 94, 59);case 128:  return RGB(237, 207, 114);case 256:  return RGB(237, 204, 97);case 512:  return RGB(237, 200, 80);case 1024: return RGB(237, 197, 63);case 2048: return RGB(237, 194, 46);case 4096: return RGB(180, 220, 90);case 8192: return RGB(150, 200, 80);case 16384:return RGB(120, 180, 70);case 32768:return RGB(90, 160, 60);case 65536:return RGB(60, 140, 50);default:   return RGB(60, 58, 50);}
}// 初始化网格
void InitializeGrid() {for (int i = 0; i < 4; i++)for (int j = 0; j < 4; j++)grid[i][j] = 0;score = 0;maxTile = 2;gameOver = false;vector<pair<int, int>> emptyCells;for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {if (grid[i][j] == 0) {emptyCells.emplace_back(i, j);}}}if (!emptyCells.empty()) {int index = rand() % emptyCells.size();grid[emptyCells[index].first][emptyCells[index].second] = 2;index = rand() % emptyCells.size();grid[emptyCells[index].first][emptyCells[index].second] = 2;}
}// 生成新数字
void GenerateNewNumber() {vector<pair<int, int>> emptyCells;for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {if (grid[i][j] == 0) {emptyCells.emplace_back(i, j);}}}if (emptyCells.empty()) return;int index = rand() % emptyCells.size();int value = (rand() % 10 == 0) ? 4 : 2;grid[emptyCells[index].first][emptyCells[index].second] = value;
}// 检查游戏是否结束
bool CheckGameOver() {for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {if (grid[i][j] == 0) return false;if (j < 3 && grid[i][j] == grid[i][j + 1]) return false;if (i < 3 && grid[i][j] == grid[i + 1][j]) return false;}}return true;
}// 文字居中绘制辅助函数
void DrawCenteredText(int x, int y, int width, int height, const wchar_t* str) {int textWidth = textwidth(str);int textHeight = textheight(str);int tx = x + (width - textWidth) / 2;int ty = y + (height - textHeight) / 2;outtextxy(tx, ty, str);
}// 绘制界面
void Draw() {cleardevice();setbkcolor(RGB(250, 248, 239));cleardevice();// ==== 分数面板 ====const int scorePanelWidth = 152;const int scorePanelHeight = 89;// 当前分数setfillcolor(SCORE_BG);solidroundrect(MARGIN, MARGIN, MARGIN + scorePanelWidth, MARGIN + scorePanelHeight, 10, 10);settextstyle(28, 0, _T("Arial"));settextcolor(TEXT_LIGHT);DrawCenteredText(MARGIN, MARGIN + 10, scorePanelWidth, 30, _T("SCORE"));wchar_t scoreStr[20];swprintf_s(scoreStr, L"%d", score);settextstyle(44, 0, _T("Arial"));settextcolor(WHITE);DrawCenteredText(MARGIN, MARGIN + 45, scorePanelWidth, 44, scoreStr);// 最高分数setfillcolor(SCORE_BG);solidroundrect(MARGIN * 2 + scorePanelWidth, MARGIN,MARGIN * 2 + scorePanelWidth * 2, MARGIN + scorePanelHeight, 10, 10);settextstyle(28, 0, _T("Arial"));settextcolor(TEXT_LIGHT);DrawCenteredText(MARGIN * 2 + scorePanelWidth, MARGIN + 10, scorePanelWidth, 30, _T("BEST"));wchar_t bestStr[20];swprintf_s(bestStr, L"%d", highScore);settextstyle(44, 0, _T("Arial"));settextcolor(WHITE);DrawCenteredText(MARGIN * 2 + scorePanelWidth, MARGIN + 45, scorePanelWidth, 44, bestStr);// 提示信息settextstyle(24, 0, _T("Arial"));settextcolor(TEXT_DARK);wchar_t tipStr[50];swprintf_s(tipStr, L"目标:合成 %d 方块!", maxTile * 2);DrawCenteredText(0, INFO_HEIGHT - 40, WINDOW_WIDTH, 40, tipStr);// ==== 游戏网格 ====for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {int x = j * (CELL_SIZE + MARGIN) + MARGIN;int y = i * (CELL_SIZE + MARGIN) + MARGIN + INFO_HEIGHT;int value = grid[i][j];setfillcolor(GetColor(value));fillroundrect(x, y, x + CELL_SIZE, y + CELL_SIZE,CORNER_RADIUS, CORNER_RADIUS);if (value != 0) {wchar_t str[10];swprintf_s(str, L"%d", value);int fontSize = 75;if (value >= 10000) fontSize = 60;else if (value >= 1000) fontSize = 65;else if (value >= 100) fontSize = 70;settextstyle(fontSize, 0, _T("Arial"));setbkmode(TRANSPARENT);int textWidth = textwidth(str);int textHeight = textheight(str);int tx = x + (CELL_SIZE - textWidth) / 2;int ty = y + (CELL_SIZE - textHeight) / 2;settextcolor(RGB(150, 150, 150));outtextxy(tx + 3, ty + 3, str);settextcolor(WHITE);outtextxy(tx, ty, str);}if (value > maxTile) {maxTile = value;}}}// 游戏结束提示if (gameOver) {settextstyle(48, 0, _T("微软雅黑"));settextcolor(RGB(255, 100, 100));outtextxy(150, 300, _T("游戏结束!"));}FlushBatchDraw();
}// 移动处理函数 
bool MoveLeft() {bool moved = false;for (int i = 0; i < 4; i++) {int temp[4] = { 0 };int index = 0;for (int j = 0; j < 4; j++) {if (grid[i][j] != 0) {temp[index++] = grid[i][j];}}for (int j = 0; j < 3; j++) {if (temp[j] == temp[j + 1] && temp[j] != 0) {temp[j] *= 2;score += temp[j];temp[j + 1] = 0;j++;}}int newRow[4] = { 0 };index = 0;for (int j = 0; j < 4; j++) {if (temp[j] != 0) {newRow[index++] = temp[j];}}if (memcmp(grid[i], newRow, sizeof(newRow))) {memcpy(grid[i], newRow, sizeof(newRow));moved = true;}}return moved;
}bool MoveRight() {bool moved = false;for (int i = 0; i < 4; i++) {int temp[4] = { 0 };int index = 3; // 从右向左填充// 紧凑到右侧for (int j = 3; j >= 0; j--) {if (grid[i][j] != 0) {temp[index--] = grid[i][j];}}// 合并相同数字(从右向左合并)for (int j = 3; j > 0; j--) {if (temp[j] == temp[j - 1] && temp[j] != 0) {temp[j] *= 2;score += temp[j];temp[j - 1] = 0;j--; // 跳过已合并的位置}}// 再次紧凑到右侧int newRow[4] = { 0 };index = 3;for (int j = 3; j >= 0; j--) {if (temp[j] != 0) {newRow[index--] = temp[j];}}// 检查是否变化if (memcmp(grid[i], newRow, sizeof(newRow)) != 0) {memcpy(grid[i], newRow, sizeof(newRow));moved = true;}}return moved;
}bool MoveDown() {bool moved = false;for (int j = 0; j < 4; j++) { // 按列处理int temp[4] = { 0 };int index = 3; // 从下向上填充// 紧凑到下方for (int i = 3; i >= 0; i--) {if (grid[i][j] != 0) {temp[index--] = grid[i][j];}}// 合并相同数字(从下向上合并)for (int i = 3; i > 0; i--) {if (temp[i] == temp[i - 1] && temp[i] != 0) {temp[i] *= 2;score += temp[i];temp[i - 1] = 0;i--; // 跳过已合并的位置}}// 再次紧凑到下方int newCol[4] = { 0 };index = 3;for (int i = 3; i >= 0; i--) {if (temp[i] != 0) {newCol[index--] = temp[i];}}// 检查是否变化for (int i = 0; i < 4; i++) {if (grid[i][j] != newCol[i]) {moved = true;break;}}if (moved) {for (int i = 0; i < 4; i++) {grid[i][j] = newCol[i];}}}return moved;
}bool MoveUp() {bool moved = false;for (int j = 0; j < 4; j++) { // 按列处理int temp[4] = { 0 };int index = 0;// 紧凑到上方for (int i = 0; i < 4; i++) {if (grid[i][j] != 0) {temp[index++] = grid[i][j];}}// 合并相同数字for (int i = 0; i < 3; i++) {if (temp[i] == temp[i + 1] && temp[i] != 0) {temp[i] *= 2;score += temp[i];temp[i + 1] = 0;i++; // 跳过已合并的位置}}// 再次紧凑int newCol[4] = { 0 };index = 0;for (int i = 0; i < 4; i++) {if (temp[i] != 0) {newCol[index++] = temp[i];}}// 检查是否变化for (int i = 0; i < 4; i++) {if (grid[i][j] != newCol[i]) {moved = true;break;}}if (moved) {for (int i = 0; i < 4; i++) {grid[i][j] = newCol[i];}}}return moved;
}int main() {initgraph(WINDOW_WIDTH, WINDOW_HEIGHT);srand((unsigned)time(NULL));LoadRecord();InitializeGrid();BeginBatchDraw();while (true) {while (!gameOver) {Draw();bool moved = false;if (GetAsyncKeyState(VK_LEFT) & 0x8000 || GetAsyncKeyState('A') & 0x8000) {moved = MoveLeft();}else if (GetAsyncKeyState(VK_RIGHT) & 0x8000 || GetAsyncKeyState('D') & 0x8000) {moved = MoveRight();}else if (GetAsyncKeyState(VK_UP) & 0x8000 || GetAsyncKeyState('W') & 0x8000) {moved = MoveUp();}else if (GetAsyncKeyState(VK_DOWN) & 0x8000 || GetAsyncKeyState('S') & 0x8000) {moved = MoveDown();}if (moved) {GenerateNewNumber();gameOver = CheckGameOver();Sleep(150);}if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) break;Sleep(10);}if (score > highScore) {highScore = score;SaveRecord();}Draw();int ret = MessageBox(GetHWnd(), L"再来一局?", L"游戏结束", MB_YESNO);if (ret == IDYES) {InitializeGrid();}else {break;}}EndBatchDraw();closegraph();return 0;
}

2.2 代码解释

含注释版代码:

#include <graphics.h>
#include <conio.h>
#include <time.h>
#include <stdlib.h>
#include <vector>
#include <windows.h>
#include <fstream>
#include <sstream>
using namespace std;// 常量定义
const int CELL_SIZE = 120;    // 每个方格的大小
const int MARGIN = 15;        // 方格之间的边距
const int WINDOW_WIDTH = 600; // 窗口宽度
const int WINDOW_HEIGHT = 700;// 窗口高度
const int CORNER_RADIUS = 15; // 方格圆角半径
const int INFO_HEIGHT = 150;  // 显示分数等信息的高度// 颜色定义
const COLORREF SCORE_BG = RGB(158, 174, 187);    // 分数背景颜色
const COLORREF TEXT_LIGHT = RGB(219, 230, 238);  // 浅色文字颜色
const COLORREF TEXT_DARK = RGB(112, 123, 131);   // 深色文字颜色// 全局变量
int grid[4][4] = { 0 }; // 游戏网格,初始值为0
int score = 0;          // 当前得分
int maxTile = 0;        // 最大数字
int highScore = 0;      // 最高分
bool gameOver = false;  // 游戏是否结束标志// 文件操作:保存记录
void SaveRecord() {ofstream file("2048.save");if (file) {file << highScore << endl; // 写入最高分file << maxTile << endl;   // 写入最大数字}
}// 文件操作:加载记录
void LoadRecord() {ifstream file("2048.save");if (file) {file >> highScore; // 读取最高分file >> maxTile;   // 读取最大数字}else {highScore = 0; // 默认最高分为0maxTile = 2;   // 默认最大数字为2}
}// 根据数字获取对应的颜色
COLORREF GetColor(int value) {switch (value) {case 0:    return RGB(205, 193, 180);case 2:    return RGB(238, 228, 218);case 4:    return RGB(237, 224, 200);case 8:    return RGB(242, 177, 121);case 16:   return RGB(245, 149, 99);case 32:   return RGB(246, 124, 95);case 64:   return RGB(246, 94, 59);case 128:  return RGB(237, 207, 114);case 256:  return RGB(237, 204, 97);case 512:  return RGB(237, 200, 80);case 1024: return RGB(237, 197, 63);case 2048: return RGB(237, 194, 46);case 4096: return RGB(180, 220, 90);case 8192: return RGB(150, 200, 80);case 16384:return RGB(120, 180, 70);case 32768:return RGB(90, 160, 60);case 65536:return RGB(60, 140, 50);default:   return RGB(60, 58, 50);}
}// 初始化游戏网格
void InitializeGrid() {for (int i = 0; i < 4; i++)for (int j = 0; j < 4; j++)grid[i][j] = 0; // 将所有方格初始化为0score = 0;       // 分数清零gameOver = false; // 游戏状态设为未结束vector<pair<int, int>> emptyCells;for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {if (grid[i][j] == 0) {emptyCells.emplace_back(i, j); // 记录空方格位置}}}if (!emptyCells.empty()) {int index = rand() % emptyCells.size();grid[emptyCells[index].first][emptyCells[index].second] = 2; // 在随机空方格中放置一个2index = rand() % emptyCells.size();grid[emptyCells[index].first][emptyCells[index].second] = 2; // 再放置一个2}
}// 生成新数字(2或4)
void GenerateNewNumber() {vector<pair<int, int>> emptyCells;for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {if (grid[i][j] == 0) {emptyCells.emplace_back(i, j); // 记录空方格位置}}}if (emptyCells.empty()) return; // 如果没有空方格则返回int index = rand() % emptyCells.size();int value = (rand() % 10 == 0) ? 4 : 2; // 有10%的概率生成4,否则生成2grid[emptyCells[index].first][emptyCells[index].second] = value; // 在随机空方格中放置新数字
}// 检查游戏是否结束
bool CheckGameOver() {for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {if (grid[i][j] == 0) return false; // 存在空方格则游戏未结束if (j < 3 && grid[i][j] == grid[i][j + 1]) return false; // 相邻方格相同则游戏未结束if (i < 3 && grid[i][j] == grid[i + 1][j]) return false; // 相邻方格相同则游戏未结束}}return true; // 所有条件都不满足则游戏结束
}// 辅助函数:在指定区域内居中文本绘制
void DrawCenteredText(int x, int y, int width, int height, const wchar_t* str) {int textWidth = textwidth(str); // 获取文本宽度int textHeight = textheight(str);// 获取文本高度int tx = x + (width - textWidth) / 2; // 计算文本左上角x坐标int ty = y + (height - textHeight) / 2; // 计算文本左上角y坐标outtextxy(tx, ty, str); // 绘制文本
}
void Draw() {cleardevice(); // 清除设备上的所有内容setbkcolor(RGB(250, 248, 239)); // 设置背景颜色cleardevice(); // 再次清除设备上的所有内容// ==== 分数面板 ====const int scorePanelWidth = 152; // 分数面板宽度const int scorePanelHeight = 89; // 分数面板高度// 当前分数setfillcolor(SCORE_BG); // 设置填充颜色为分数背景色solidroundrect(MARGIN, MARGIN, MARGIN + scorePanelWidth, MARGIN + scorePanelHeight, 10, 10); // 绘制圆角矩形settextstyle(28, 0, _T("Arial")); // 设置文本样式settextcolor(TEXT_LIGHT); // 设置文本颜色为浅色DrawCenteredText(MARGIN, MARGIN + 10, scorePanelWidth, 30, _T("SCORE")); // 居中绘制“SCORE”wchar_t scoreStr[20]; // 定义存储当前分数的字符串swprintf_s(scoreStr, L"%d", score); // 将当前分数转换为字符串settextstyle(44, 0, _T("Arial")); // 设置文本样式settextcolor(WHITE); // 设置文本颜色为白色DrawCenteredText(MARGIN, MARGIN + 45, scorePanelWidth, 44, scoreStr); // 居中绘制当前分数// 最高分数setfillcolor(SCORE_BG); // 设置填充颜色为分数背景色solidroundrect(MARGIN * 2 + scorePanelWidth, MARGIN,MARGIN * 2 + scorePanelWidth * 2, MARGIN + scorePanelHeight, 10, 10); // 绘制圆角矩形settextstyle(28, 0, _T("Arial")); // 设置文本样式settextcolor(TEXT_LIGHT); // 设置文本颜色为浅色DrawCenteredText(MARGIN * 2 + scorePanelWidth, MARGIN + 10, scorePanelWidth, 30, _T("BEST")); // 居中绘制“BEST”wchar_t bestStr[20]; // 定义存储最高分的字符串swprintf_s(bestStr, L"%d", highScore); // 将最高分转换为字符串settextstyle(44, 0, _T("Arial")); // 设置文本样式settextcolor(WHITE); // 设置文本颜色为白色DrawCenteredText(MARGIN * 2 + scorePanelWidth, MARGIN + 45, scorePanelWidth, 44, bestStr); // 居中绘制最高分// 最大方块setfillcolor(GetColor(maxTile)); // 设置填充颜色为最大方块的颜色fillroundrect(349, 15, 349 + 90, 15 + 90, 10, 10); // 绘制圆角矩形wchar_t str[10]; // 定义存储最大方块数值的字符串swprintf_s(str, L"%d", maxTile); // 将最大方块数值转换为字符串int fontSize = 60; // 初始字体大小if (maxTile >= 10000) fontSize = 35; // 根据数值调整字体大小else if (maxTile >= 1000) fontSize = 42;else if (maxTile >= 100) fontSize = 55;settextstyle(fontSize, 0, _T("Arial")); // 设置文本样式int textWidth = textwidth(str); // 获取文本宽度int textHeight = textheight(str); // 获取文本高度int tx = 349 + (90 - textWidth) / 2; // 计算文本左上角x坐标int ty = 15 + (90 - textHeight) / 2; // 计算文本左上角y坐标settextcolor(RGB(150, 150, 150)); // 设置文本颜色为灰色阴影outtextxy(tx + 2, ty + 2, str); // 绘制带阴影的文字settextcolor(WHITE); // 设置文本颜色为白色outtextxy(tx, ty, str); // 绘制文字// 提示信息settextstyle(24, 0, _T("Arial")); // 设置文本样式settextcolor(TEXT_DARK); // 设置文本颜色为深色wchar_t tipStr[50]; // 定义存储提示信息的字符串swprintf_s(tipStr, L"目标:合成 %d 方块!", maxTile * 2); // 构造提示信息DrawCenteredText(0, INFO_HEIGHT - 40, WINDOW_WIDTH, 40, tipStr); // 居中绘制提示信息// ==== 游戏网格 ====for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {int x = j * (CELL_SIZE + MARGIN) + MARGIN; // 计算方格左上角x坐标int y = i * (CELL_SIZE + MARGIN) + MARGIN + INFO_HEIGHT; // 计算方格左上角y坐标int value = grid[i][j]; // 获取方格中的数值setfillcolor(GetColor(value)); // 设置填充颜色为方格数值对应的颜色fillroundrect(x, y, x + CELL_SIZE, y + CELL_SIZE, CORNER_RADIUS, CORNER_RADIUS); // 绘制圆角矩形if (value != 0) { // 如果方格中有数字wchar_t str[10]; // 定义存储方格数值的字符串swprintf_s(str, L"%d", value); // 将方格数值转换为字符串int fontSize = 75; // 初始字体大小if (value >= 10000) fontSize = 50; // 根据数值调整字体大小else if (value >= 1000) fontSize = 60;else if (value >= 100) fontSize = 70;settextstyle(fontSize, 0, _T("Arial")); // 设置文本样式setbkmode(TRANSPARENT); // 设置背景模式为透明int textWidth = textwidth(str); // 获取文本宽度int textHeight = textheight(str); // 获取文本高度int tx = x + (CELL_SIZE - textWidth) / 2; // 计算文本左上角x坐标int ty = y + (CELL_SIZE - textHeight) / 2; // 计算文本左上角y坐标settextcolor(RGB(150, 150, 150)); // 设置文本颜色为灰色阴影outtextxy(tx + 3, ty + 3, str); // 绘制带阴影的文字settextcolor(WHITE); // 设置文本颜色为白色outtextxy(tx, ty, str); // 绘制文字}if (value > maxTile) {maxTile = value; // 更新最大方块数值}}}// 游戏结束提示if (gameOver) {settextstyle(48, 0, _T("微软雅黑")); // 设置文本样式settextcolor(RGB(255, 100, 100)); // 设置文本颜色为红色outtextxy(150, 300, _T("游戏结束!")); // 绘制“游戏结束!”}FlushBatchDraw(); // 刷新绘图缓冲区
}
bool MoveLeft() {bool moved = false;for (int i = 0; i < 4; i++) {int temp[4] = { 0 }; // 临时数组用于存储当前行的非零值int index = 0; // 临时数组的索引// 将当前行的非零值紧凑到左侧for (int j = 0; j < 4; j++) {if (grid[i][j] != 0) {temp[index++] = grid[i][j];}}// 合并相邻相同的数字(从左向右合并)for (int j = 0; j < 3; j++) {if (temp[j] == temp[j + 1] && temp[j] != 0) {temp[j] *= 2; // 合并相同数字score += temp[j]; // 更新分数temp[j + 1] = 0; // 清空合并后的单元格j++; // 跳过已合并的位置}}// 再次将合并后的结果紧凑到左侧int newRow[4] = { 0 };index = 0;for (int j = 0; j < 4; j++) {if (temp[j] != 0) {newRow[index++] = temp[j];}}// 检查是否发生了移动if (memcmp(grid[i], newRow, sizeof(newRow))) {memcpy(grid[i], newRow, sizeof(newRow)); // 更新网格moved = true;}}return moved;
}bool MoveRight() {bool moved = false;for (int i = 0; i < 4; i++) {int temp[4] = { 0 }; // 临时数组用于存储当前行的非零值int index = 3; // 临时数组的索引,从右向左填充// 将当前行的非零值紧凑到右侧for (int j = 3; j >= 0; j--) {if (grid[i][j] != 0) {temp[index--] = grid[i][j];}}// 合并相邻相同的数字(从右向左合并)for (int j = 3; j > 0; j--) {if (temp[j] == temp[j - 1] && temp[j] != 0) {temp[j] *= 2; // 合并相同数字score += temp[j]; // 更新分数temp[j - 1] = 0; // 清空合并后的单元格j--; // 跳过已合并的位置}}// 再次将合并后的结果紧凑到右侧int newRow[4] = { 0 };index = 3;for (int j = 3; j >= 0; j--) {if (temp[j] != 0) {newRow[index--] = temp[j];}}// 检查是否发生了移动if (memcmp(grid[i], newRow, sizeof(newRow)) != 0) {memcpy(grid[i], newRow, sizeof(newRow)); // 更新网格moved = true;}}return moved;
}bool MoveDown() {bool moved = false;for (int j = 0; j < 4; j++) { // 按列处理int temp[4] = { 0 }; // 临时数组用于存储当前列的非零值int index = 3; // 临时数组的索引,从下向上填充// 将当前列的非零值紧凑到下方for (int i = 3; i >= 0; i--) {if (grid[i][j] != 0) {temp[index--] = grid[i][j];}}// 合并相邻相同的数字(从下向上合并)for (int i = 3; i > 0; i--) {if (temp[i] == temp[i - 1] && temp[i] != 0) {temp[i] *= 2; // 合并相同数字score += temp[i]; // 更新分数temp[i - 1] = 0; // 清空合并后的单元格i--; // 跳过已合并的位置}}// 再次将合并后的结果紧凑到下方int newCol[4] = { 0 };index = 3;for (int i = 3; i >= 0; i--) {if (temp[i] != 0) {newCol[index--] = temp[i];}}// 检查是否发生了移动for (int i = 0; i < 4; i++) {if (grid[i][j] != newCol[i]) {moved = true;break;}}if (moved) {for (int i = 0; i < 4; i++) {grid[i][j] = newCol[i]; // 更新网格}}}return moved;
}bool MoveUp() {bool moved = false;for (int j = 0; j < 4; j++) { // 按列处理int temp[4] = { 0 }; // 临时数组用于存储当前列的非零值int index = 0; // 临时数组的索引// 将当前列的非零值紧凑到上方for (int i = 0; i < 4; i++) {if (grid[i][j] != 0) {temp[index++] = grid[i][j];}}// 合并相邻相同的数字(从上向下合并)for (int i = 0; i < 3; i++) {if (temp[i] == temp[i + 1] && temp[i] != 0) {temp[i] *= 2; // 合并相同数字score += temp[i]; // 更新分数temp[i + 1] = 0; // 清空合并后的单元格i++; // 跳过已合并的位置}}// 再次将合并后的结果紧凑到上方int newCol[4] = { 0 };index = 0;for (int i = 0; i < 4; i++) {if (temp[i] != 0) {newCol[index++] = temp[i];}}// 检查是否发生了移动for (int i = 0; i < 4; i++) {if (grid[i][j] != newCol[i]) {moved = true;break;}}if (moved) {for (int i = 0; i < 4; i++) {grid[i][j] = newCol[i]; // 更新网格}}}return moved;
}int main() {initgraph(WINDOW_WIDTH, WINDOW_HEIGHT); // 初始化图形窗口srand((unsigned)time(NULL)); // 设置随机种子LoadRecord(); // 加载记录InitializeGrid(); // 初始化游戏网格BeginBatchDraw(); // 开始批量绘图while (true) {while (!gameOver) {Draw(); // 绘制游戏界面bool moved = false;// 处理键盘输入if (GetAsyncKeyState(VK_LEFT) & 0x8000 || GetAsyncKeyState('A') & 0x8000) {moved = MoveLeft(); // 左移}else if (GetAsyncKeyState(VK_RIGHT) & 0x8000 || GetAsyncKeyState('D') & 0x8000) {moved = MoveRight(); // 右移}else if (GetAsyncKeyState(VK_UP) & 0x8000 || GetAsyncKeyState('W') & 0x8000) {moved = MoveUp(); // 上移}else if (GetAsyncKeyState(VK_DOWN) & 0x8000 || GetAsyncKeyState('S') & 0x8000) {moved = MoveDown(); // 下移}if (moved) {GenerateNewNumber(); // 生成新数字gameOver = CheckGameOver(); // 检查游戏是否结束Sleep(150); // 延迟一段时间}if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) break; // 按Esc键退出Sleep(10); // 延迟一段时间}if (score > highScore) {highScore = score; // 更新最高分SaveRecord(); // 保存记录}Draw(); // 最后一次绘制游戏界面int ret = MessageBox(GetHWnd(), L"再来一局?", L"游戏结束", MB_YESNO); // 弹出确认对话框if (ret == IDYES) {InitializeGrid(); // 重新初始化游戏网格}else {break; // 退出游戏}}EndBatchDraw(); // 结束批量绘图closegraph(); // 关闭图形窗口return 0;
}

3.后记

点赞破30火速更下一期。第一期点赞量:15

目前为第二期,本期更新内容:见本文1.3

去我的主页查看更多小游戏,链接:EasyX游戏合集【最新版】_easyx小游戏-CSDN博客

哦对了,本系列为限时免费,如果你们点赞快的话,也许系列更完了都还是免费的,所以点赞冲冲冲!详见评论区

http://www.khdw.cn/news/20541.html

相关文章:

  • 免费国内socks5代理seo技术
  • pc网站做移动端适配网站关键词如何快速上首页
  • 社交网站开发难度重庆seo外包平台
  • 化妆品网站建设的维护夸克浏览器网页版入口
  • 莱芜民生网站免费推广平台排行
  • 高端上海网站设计公司网络优化app哪个好
  • 漯河做网站推广引流app推广软件
  • 咸阳网站建设专业公司哪家好职业培训机构资质
  • 用糖做的网站企业网站代运营
  • 揭阳企业自助建站系统精准推广
  • 网站开发点赞收藏设计思路整合营销经典案例
  • 中山 在门户网站推广关键词优化网站排名
  • 品牌网站建设有什么作用南昌网站开发公司
  • 青州网站建设公司营销策划的重要性
  • 为赌博网站做代理来客seo
  • 网站备案信息页面性价比高seo排名优化的
  • 中国空间站vr全景成都百度网站排名优化
  • 河北省中级经济师考试seo关键词排名优化软件怎么选
  • 2345浏览器手机版seo优化中以下说法正确的是
  • 山东嘉邦家居用品公司网站 加盟做经销商多少钱 有人做过吗平台推广销售话术
  • 怎么做网站免费重庆森林讲的什么内容
  • 山东济南市莱芜区疫情最新消息北京网站优化排名
  • 免费双语网站模板西安企业做网站
  • 广州英文网站建设百度经验app
  • wordpress图片分页插件网络优化工具app手机版
  • 邯郸做wap网站找谁申请一个网站
  • 自己做传奇sf网站2345网址导航是病毒吗
  • wordpress批量定时更新温州seo排名优化
  • 网站建设公司发展营销型网站特点
  • 简洁大气传媒公司名字衡阳seo外包