C语言用面向对象的思想写贪吃蛇

时间:2021-05-13 22:21:21
C语言用面向对象的思想写贪吃蛇

  大概一年前这时候,接触C语言一个月,那时候知之甚少,对面向对象只觉”可远观而不可亵玩“,而且会看到很多言论说C语言就是面向过程的语言,C++就是面向对象的语言。不过,不记得什么时候在网上看到过一篇博文,大概是说如何优雅的写C语言。其中颇有印象的就是通过结构的函数指针模拟C++中的类。

  今天粗略尝试了一下,写的是之前写过的贪吃蛇。的确,用面向对象的思维写让我的思维变的更加清晰,因为这个游戏(以及大多数显示事物)的天然属性就是对象。

  此外,这次从最关键最核心的写起,如此写起来真是越写越轻松。因为如果关键部分写不出来,其他写的再好也没用。所以这次的顺序大概是:能移动的蛇--->能吃食物并变长--->能判断自己是否死亡--->其他修饰功能。

  最后,调试无误后。在发布代码前写了写注释。发现自己现在命名编码规范了不少,所以注释也并不多。曾经看到一种观点就是,注释关键部分就足够了,最好的注释就是良好的命名习惯、编码风格和清晰的逻辑。

  不过,由于没有太多的规划,都是自己在路上零散构思的,代码还是不够紧凑,逻辑还可以优化。

  头文件

 #ifndef _HEAD_H_
#define _HEAD_H_
#include <stdio.h>
#include <windows.h>//
int Score = -;
typedef struct node1
{
int x;
int y;
struct node1 *next;
}SnakeBody;
typedef struct node2
{
int x;
int y;//location
char Status;
char Direction;
void (*MoveUp)(struct node2 *);
void (*MoveDown)(struct node2 *);
void (*MoveRight)(struct node2 *);
void (*MoveLeft)(struct node2 *);
void (*GrowUp)(struct node2 *);
int (*IsDie)(struct node2 *);
SnakeBody *next;
}SnakeHead;
void HideCursor()
{
CONSOLE_CURSOR_INFO cursor_info = { , };
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
void SetPosition(int x, int y)
{
COORD pos;
HANDLE hOutput;
pos.X = x;
pos.Y = y;
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOutput, pos);
}
void Print(SnakeHead *H)
{
SetPosition(H->x, H->y);
printf("@");//mean snake head
SnakeBody *temp = H->next;
while(NULL != temp)
{
SetPosition(temp->x, temp->y);
printf("+");//means snake body
temp = temp->next;
}
}
void Move(SnakeHead *H)
{
SnakeBody *temp = H->next;
while (NULL != temp->next->next)
{
temp = temp->next;
}
temp->next->next = H->next;
H->next = temp->next;
SetPosition(temp->next->x, temp->next->y);
printf(" ");
temp->next = NULL;
H->next->x = H->x;
H->next->y = H->y;
}
void _MoveUp(SnakeHead *H)
{
Move(H);
H->y--;
}
void _MoveDown(SnakeHead *H)
{
Move(H);
H->y++;
}
void _MoveRight(SnakeHead *H)
{
Move(H);
H->x++;
}
void _MoveLeft(SnakeHead *H)
{
Move(H);
H->x--;
}
void _GrowUp(SnakeHead *H)
{
SnakeBody *temp = (SnakeBody *)malloc(sizeof(SnakeBody));
temp->x = H->x;
temp->y = H->y;
temp->next = H->next;
H->next = temp;
switch (H->Direction)
{
case 'U':
{
H->y--;
break;
}
case 'D':
{
H->y++;
break;
}
case 'L':
{
H->x--;
break;
}
case 'R':
{
H->x++;
break;
}
default:
{
break;
}
}
Print(H);
}
const int Left = ;
const int Right = ;
const int Top = ;
const int Bottom = ;
void BuildWall()
{
for (int x = Left; x <= Right; x++)
{
SetPosition(x, Top);
printf("-");
SetPosition(x, Bottom);
printf("-");
}
for (int y = Top; y <= Bottom; y++)
{
SetPosition(Left, y);
printf("|");
SetPosition(Right, y);
printf("|");
}
//some hint
SetPosition(, );
printf("Snake");
SetPosition(, );
printf("zhaoyu");
SetPosition(, );
printf("F1:Slow Down");
SetPosition(, );
printf("F2:Speed Up");
SetPosition(, );
printf("F3:Speed Up++");
SetPosition(, );
printf("Score:");
}
void CreateFood(int *x, int *y, SnakeHead *H)
{
int flag = ;
int i = , j = ;
//make sure in the wall && not on snake
SnakeBody *temp = H->next;
while ( == flag)
{
i = rand()%;
i += ;
j = rand()%;
j += ;
flag = ;
if (i == H->x && j == H->y)
{
flag = ;
continue;
}
temp = H->next;
while (NULL != temp)
{
if (i == temp->x && j == temp->y)
{
flag = ;
break;
}
temp = temp->next;
}
} *x = i;
*y = j;
SetPosition(i, j);
printf("$");//dollar mead food!
SetPosition(, );
//once food created ,score plus 1
printf("%d", ++Score);
}
int Die(SnakeHead *H)
{
//out the wall will die
if (H->x <= Left || H->x >= Right || H->y >= Bottom || H->y <= Top)
{
return ;//die
}
// crush into itself will die
SnakeBody *temp = H->next;
while (NULL !=temp)
{
if (H->x == temp->x && H->y == temp->y)
{
return ;
}
temp = temp->next;
}
// 0 mean not die
return ;
}
#endif

  源文件

 #include "head.h"

 void StartGame()
{
//Create a snake
SnakeBody *temp = (SnakeBody *)malloc(sizeof(SnakeBody));
SnakeHead *Head = (SnakeHead *)malloc(sizeof(SnakeHead));
Head->Direction = 'L';
Head->Status = ;
Head->x = ;
Head->y = ;
//assign some functions to snake
Head->MoveUp = _MoveUp;
Head->MoveDown = _MoveDown;
Head->MoveRight = _MoveRight;
Head->MoveLeft = _MoveLeft;
Head->GrowUp = _GrowUp;
Head->IsDie = Die;
temp->x = ;
temp->y = ;
temp->next = NULL;
Head->next = temp;
// create snake body
for (int i = ; i < ; i++)
{
SnakeBody *move = (SnakeBody *)malloc(sizeof(SnakeBody));
move->x = temp->x + ;
move->y = temp->y;
temp->next = move;
temp = move;
move->next = NULL;
}
//Init food
int Foodx = , Foody = ;
CreateFood(&Foodx, &Foody, Head);
int Speed = ;
while ( == Head->Status)
{
//print the snake
Print(Head);
//judge is or not die
if((*Head->IsDie)(Head))
{
break;
}
//get direction
if(GetAsyncKeyState(VK_UP) && 'D' != Head->Direction)
{
Head->Direction = 'U';
}
else if (GetAsyncKeyState(VK_DOWN) && 'U' != Head->Direction)
{
Head->Direction = 'D';
}
else if (GetAsyncKeyState(VK_RIGHT) && 'L' != Head->Direction)
{
Head->Direction = 'R';
}
else if (GetAsyncKeyState(VK_LEFT) && 'R' != Head->Direction)
{
Head->Direction = 'L';
}
//move
switch (Head->Direction)
{
case 'U':
{
//judge is there a food
if (Head->x == Foodx && Head->y - == Foody)
{
(*Head->GrowUp)(Head);
CreateFood(&Foodx, &Foody, Head);
}
else//Just move
{
(*Head->MoveUp)(Head);
}
break;
}
case 'D':
{
if (Head->x == Foodx && Head->y + == Foody)
{
(*Head->GrowUp)(Head);
CreateFood(&Foodx, &Foody, Head);
}
else
{
(*Head->MoveDown)(Head);
}
break;
}
case 'R':
{
if (Head->x + == Foodx && Head->y == Foody)
{
(*Head->GrowUp)(Head);
CreateFood(&Foodx, &Foody, Head);
}
else
{
(*Head->MoveRight)(Head);
} break;
}
case 'L':
{
if (Head->x - == Foodx && Head->y == Foody)
{
(*Head->GrowUp)(Head);
CreateFood(&Foodx, &Foody, Head);
}
else
{
(*Head->MoveLeft)(Head);
}
break;
}
default:
{
break;
}
}
//control
if (GetAsyncKeyState(VK_F1) && Speed > )
{
Speed -= ;
}
else if (GetAsyncKeyState(VK_F2) && Speed < )
{
Speed += ;
}
else if (GetAsyncKeyState(VK_F3))
{
Speed += ;
}
if ('L' == Head->Direction ||'R' == Head->Direction)
{
Sleep(/Speed);
}
else
{
Sleep(/Speed);
}
}
}
int main(int argc, char const *argv[])
{
//init
system("cls");
system("mode con cols=50 lines=30");//no space around equal sign
BuildWall();
HideCursor();
StartGame();
SetPosition(, );
printf("Enter to exit!");
getchar();
return ;
}

  运行:

    C语言用面向对象的思想写贪吃蛇

  后记:

  1、函数指针与函数名

  函数名与函数指针都是一样的,即都是函数指针。函数名是一个函数指针常量,而函数指针是一个函数数指针变量。

  一般的C语言书籍都不会谈及这些近乎奇淫技巧的东东。

  2、GetAsyncKeyState函数

  这个函数按下之后其状态会一直存在,我在写俄罗斯方块就大受其害。以下面代码为例。你只需要按一下F1就可以跳出循环了,而不是1000下。因为按下一次之后其状态就始终为真了,除非你按下别的键。

 #include <stdio.h>
#include <windows.h>
int main(int argc, char const *argv[])
{
int i = ;
while ()
{
if (GetAsyncKeyState(VK_F1))
{
i++;
}
if (i >= )
{
break;
}
}
printf("%d\n", i);
return ;
}