目录
链表是数据结构课程一个重要概念,为巩固练习,用链表写一个五子棋游戏。可以有白棋黑棋、可以判断胜负,使用到了C语言的图形库EasyX。
一、新建坐标系统
新建坐标系统使用数组存储,利用结构体创建一个15 x 15坐标系,包括横坐标和纵坐标。
#define CROSSINGLINES 15
#define VERTICALLINES 15
struct Board {
int cro[CROSSINGLINES];
int ver[VERTICALLINES];
};
二、初始化棋盘,并且利用C语言图形库绘线函数绘制棋盘
void InitBoard(Board board)
{
for (int i = 0; i < CROSSINGLINES; i++)
{
line(board.cro[i], board.ver[0], board.cro[i], board.ver[VERTICALLINES - 1]); //竖线
line(board.cro[0], board.ver[i], board.cro[CROSSINGLINES - 1], board.ver[i]); //横线
}
}
初始化绘制棋盘结束后,需要判断每一个棋盘线上每一个交点是否已经有棋子,初始化每一个交点均没有棋子,因此赋值为false,使用二维数组即可完成赋值
for (int i = 0; i < CROSSINGLINES; i++)
{
board.cro[i] = (i + 1) * BOARD_INTERVAL; //BOARD_INTERVAL为棋盘间距,避免从图形左上顶点开始,以棋盘间距与左上顶点隔开
board.ver[i] = (i + 1) * BOARD_INTERVAL;
for (int j = 0; j < VERTICALLINES; j++)
flag[i][j] = false;
}
三、生成棋子,并初始化
棋子包含属性有坐标,以及角色。因此结构体可以设置
struct Player{
int x;
int y;
char player;
Player* next;
};
初始化棋子,棋子头结点统一置为空节点,利用头插法每次点击给相应的棋手添加节点位置和坐标
Player* player_white;
Player* player_black;
player_white = (Player*)malloc(sizeof(Player*));
player_black = (Player*)malloc(sizeof(Player*));
if (!player_white || !player_black)
exit(OVERFLOW);
player_white->x = player_white->y = 0;
player_black->x = player_black->y = 0;
player_white->next = NULL;
player_black->next = NULL;
player_white->player = 'W';
player_black->player = 'B';
因为用户不一定能够准确点击在棋盘纵横线的交点上,所以寻找距离用户点击最近的节点,用户点击位置整除棋盘间距+1即可找到横坐标上的交点位置,在链表中使用头插法给对应的棋手加入节点坐标信息和棋手信息即可
void PlayGB(Player* player, Board board, int &choice, bool flag[CROSSINGLINES][VERTICALLINES])
{
MOUSEMSG m;
if (MouseHit())
{
m = GetMouseMsg(); //获取鼠标队列信息
if (m.x > 600 || m.x < 30 || m.y < 30 || m.y > 600)
return;
if (m.uMsg == WM_LBUTTONDOWN)
{
int x, y;
int i, j;
x = (m.x - BOARD_INTERVAL) / BOARD_INTERVAL;
y = (m.y - BOARD_INTERVAL) / BOARD_INTERVAL;
if (x > 15 || x < 0 || y > 15 || y < 0)
return;
if ((m.x - board.cro[x]) < (board.cro[x + 1] - m.x))
{
i = x;
x = board.cro[x];
}
else {
i = x + 1;
x = board.cro[x + 1];
}
if ((m.y - board.ver[y]) < (board.ver[y + 1] - m.y))
{
j = y;
y = board.ver[y];
}
else
{
j = y + 1;
y = board.ver[y + 1];
}
if (flag[i][j])
return;
else
flag[i][j] = true;
Player* p;
p = (Player*)malloc(sizeof(Player*));
if (!p)
exit(OVERFLOW);
p->x = x; p->y = y;
p->player = player->player;
p->next = player->next;
player->next = p;
if (p->player == 'W')
{
setfillcolor(WHITE);
solidcircle(p->x, p->y, PIECE);
}
else if (p->player == 'B')
{
setfillcolor(BLACK);
solidcircle(p->x, p->y, PIECE);
}
if (choice == 0)
choice = 1;
else
choice = 0;
}
}
}
四、判断胜负
判断胜负即判断棋手刚下棋子上下左右、左上、右上、左下、右下八个方向对应链表存在相应的棋子,但五子棋至少大于等于5才可判断胜负
bool Win(Player* player, int x, int y)
{
if(ElemExist(player, x + BOARD_INTERVAL, y) && ElemExist(player, x + 2 * BOARD_INTERVAL, y)
&& ElemExist(player, x + 3 * BOARD_INTERVAL, y) && ElemExist(player, x + 4 * BOARD_INTERVAL, y))
return true; //右方五子
if (ElemExist(player, x - 1 * BOARD_INTERVAL, y) && ElemExist(player, x - 2 * BOARD_INTERVAL, y)
&& ElemExist(player, x - 3 * BOARD_INTERVAL, y) && ElemExist(player, x - 4 * BOARD_INTERVAL, y))
return true; //左方五子
if (ElemExist(player, x, y + 1 * BOARD_INTERVAL) && ElemExist(player, x, y + 2 * BOARD_INTERVAL)
&& ElemExist(player, x, y + 3 * BOARD_INTERVAL) && ElemExist(player, x, y + 4 * BOARD_INTERVAL))
return true; //上方五子
if (ElemExist(player, x, y - 1 * BOARD_INTERVAL) && ElemExist(player, x, y - 2 * BOARD_INTERVAL)
&& ElemExist(player, x, y - 3 * BOARD_INTERVAL) && ElemExist(player, x, y - 4 * BOARD_INTERVAL))
return true; //下方五子
if (ElemExist(player, x + 1 * BOARD_INTERVAL, y - 1 * BOARD_INTERVAL) && ElemExist(player, x + 2 * BOARD_INTERVAL, y - 2 * BOARD_INTERVAL)
&& ElemExist(player, x + 3 * BOARD_INTERVAL, y - 3 * BOARD_INTERVAL) && ElemExist(player, x + 4 * BOARD_INTERVAL, y - 4 * BOARD_INTERVAL))
return true; //右上方五子
if (ElemExist(player, x + 1 * BOARD_INTERVAL, y + 1 * BOARD_INTERVAL) && ElemExist(player, x + 2 * BOARD_INTERVAL, y + 2 * BOARD_INTERVAL)
&& ElemExist(player, x + 3, y + 3 * BOARD_INTERVAL) && ElemExist(player, x + 4, y + 4 * BOARD_INTERVAL))
return true; //右下方五子
if (ElemExist(player, x - 1 * BOARD_INTERVAL, y - 1 * BOARD_INTERVAL) && ElemExist(player, x - 2 * BOARD_INTERVAL, y - 2 * BOARD_INTERVAL)
&& ElemExist(player, x - 3 * BOARD_INTERVAL, y - 3 * BOARD_INTERVAL) && ElemExist(player, x - 4 * BOARD_INTERVAL, y - 4 * BOARD_INTERVAL))
return true; //左上方五子
if (ElemExist(player, x - 1 * BOARD_INTERVAL, y + 1 * BOARD_INTERVAL) && ElemExist(player, x - 2 * BOARD_INTERVAL, y + 2 * BOARD_INTERVAL)
&& ElemExist(player, x - 3 * BOARD_INTERVAL, y + 3 * BOARD_INTERVAL) && ElemExist(player, x - 4 * BOARD_INTERVAL, y + 4 * BOARD_INTERVAL))
return true; //左下方五子
return false;
}
bool ElemExist(Player* player, int x, int y)
{
Player* p;
p = (Player*)malloc(sizeof(Player*));
if (!p)
exit(OVERFLOW);
p = player->next;
while (p) {
if (p->x == x && p->y == y)
{
//free(p);
return true;
}
p = p->next;
}
//free(p);
return false;
}
总结:
C语言很难难在指针和内存,尤其是链表运用到的指针方面很多,需要多加练习和使用。以上代码均是在多次debug和思考之后才可以正常运行。如果突然遇到一大堆错误提示,比如提示else需要声明,说明在某处代码粗心多加分号,需要找很久但不一定能找出来,但是当程序可以正常运行之后,满足感是很强的,加油!
此游戏完成基本功能,其他功能小伙伴可自行完善。想要源码的小伙伴私信我,喜欢给作者点个赞呗。