硬件需求:
内存需求较大,51单片机无法满足,因此需要扩展内存
Proteus需添加240X320的彩 {MOD}液晶
实现功能如下:
1、通过按键选择下棋位置
2、无限悔棋
3、重新开始
黑白双方循环落子,黑方先下,通过上下左右四个按键选择落子位置,按下确定按键即可将棋子放置在棋盘上。当有一方连成5子或棋盘落满棋子,游戏结束,判定双方胜负。按下悔棋按键可以悔棋,每按一次可以悔棋一步,可以无限悔棋,按下重新开始按键,游戏重新开始。
仿真效果图如下:
仿真电路图

开机初始化界面

黑棋获胜界面
源码如下:
#include "game.h"
#include "malloc.h"
#include "74HC165.h"
//定义棋盘起始坐标
#define CHESSBOARD_START_X 47
#define CHESSBOARD_START_Y 7
#define CHESSBOARD_END_X 272
#define CHESSBOARD_END_Y 232
//棋盘大小
#define CHESSBOARD_SIZE (CHESSBOARD_END_X-CHESSBOARD_START_X)
//网格大小
#define CHESSGRID_SIZE (CHESSBOARD_SIZE / 15)
//棋盘颜 {MOD}
#define CHESSBOARD_COLOR BROWN
char (*g_Chess)[15]; //存放棋子缓存
int g_ChessNum; //棋子数量
int g_SameChessNum; //相连的相同的棋子数
//绘制棋盘
//15x15
static void DrawChessBoard(void)
{
int i;
//填充棋盘背景 {MOD}
LcdFill(CHESSBOARD_START_X-7, CHESSBOARD_START_Y-7,
CHESSBOARD_END_X+7, CHESSBOARD_END_Y+7, CHESSBOARD_COLOR);
LcdSetColor(BLACK);
//绘制边框线
LcdDrawRect(CHESSBOARD_START_X-3, CHESSBOARD_START_Y-3,
CHESSBOARD_END_X+3, CHESSBOARD_END_Y+3);
LcdDrawRect(CHESSBOARD_START_X-4, CHESSBOARD_START_Y-4,
CHESSBOARD_END_X+4, CHESSBOARD_END_Y+4);
LcdDrawRect(CHESSBOARD_START_X, CHESSBOARD_START_Y,
CHESSBOARD_END_X, CHESSBOARD_END_Y);
//绘制垂直线
for (i = 1; i < 15; i++)
{
LcdDrawLine(CHESSBOARD_START_X,
CHESSBOARD_START_Y + (CHESSGRID_SIZE*i),
CHESSBOARD_END_X,
CHESSBOARD_START_Y + (CHESSGRID_SIZE*i),
BLACK);
}
//绘制水平线
for (i = 1; i < 15; i++)
{
LcdDrawLine(CHESSBOARD_START_X + (CHESSGRID_SIZE*i),
CHESSBOARD_START_Y,
CHESSBOARD_START_X + (CHESSGRID_SIZE*i),
CHESSBOARD_END_Y,
BLACK);
}
}
//绘制棋子
//x,y:棋子坐标(0~14)
static void DrawChess(uint8 x, uint8 y)
{
uint16 ChessColor;
uint16 xChess;
uint16 yChess;
if (x>14 || x<0 || y>14 || y<0)return; //超范围了
if (g_Chess[x][y]!=0)return; //已有棋子
//判断棋子颜 {MOD}
(g_ChessNum%2==0) ? (ChessColor=BLACK) : (ChessColor=WHITE);
//计算棋子坐标
xChess = CHESSBOARD_START_X+(CHESSGRID_SIZE/2) + (x*CHESSGRID_SIZE);
yChess = CHESSBOARD_START_Y+(CHESSGRID_SIZE/2) + (y*CHESSGRID_SIZE);
LcdDrawCircleA(xChess,
yChess,
CHESSGRID_SIZE/2-1,
ChessColor,
1);
g_ChessNum++;
g_Chess[x][y] = g_ChessNum;
}
//清除棋子
//x,y:棋子坐标(0~14)
static void ClearChess(uint8 x, uint8 y)
{
uint16 ChessColor;
uint16 xChess;
uint16 yChess;
if (x>14 || x<0 || y>14 || y<0)return; //超范围了
if (g_Chess[x][y]!=0)return; //已有棋子
ChessColor = CHESSBOARD_COLOR;
//计算棋子坐标
xChess = CHESSBOARD_START_X+(CHESSGRID_SIZE/2) + (x*CHESSGRID_SIZE);
yChess = CHESSBOARD_START_Y+(CHESSGRID_SIZE/2) + (y*CHESSGRID_SIZE);
LcdDrawCircleA(xChess,
yChess,
CHESSGRID_SIZE/2-1,
ChessColor,
1);
}
//绘制选择框
//x,y:选择框的坐标(0~14)
static void DrawSelectBox(uint8 x, uint8 y)
{
uint16 xChess,yChess;
//计算选择框的坐标
xChess = CHESSBOARD_START_X + (x*CHESSGRID_SIZE);
yChess = CHESSBOARD_START_Y + (y*CHESSGRID_SIZE);
LcdSetColor(YELLOW);
LcdDrawRect(xChess,
yChess,
xChess+CHESSGRID_SIZE,
yChess+CHESSGRID_SIZE);
}
//清除选择框
//x,y:选择框的坐标(0~14)
static void ClearSelectBox(uint8 x, uint8 y)
{
uint16 xChess,yChess;
//计算选择框的坐标
xChess = CHESSBOARD_START_X + (x*CHESSGRID_SIZE);
yChess = CHESSBOARD_START_Y + (y*CHESSGRID_SIZE);
LcdSetColor(BLACK);
LcdDrawRect(xChess,
yChess,
xChess+CHESSGRID_SIZE,
yChess+CHESSGRID_SIZE);
}
//获取相同棋子数
//x,y:棋子坐标(0~14)
//xStep,yStep:x,y坐标移动步数
static int GetChessNum(char ChessType, int x, int y, int xStep, int yStep)
{
g_SameChessNum = 0;
while (1)
{
x = x + xStep;
y = y + yStep;
//判断是否越界
if (x < 0 || x > 14 || y < 0 || y > 14)
{
return g_SameChessNum;
}
//没有棋子
if (g_Chess[x][y]==0)
{
return g_SameChessNum;
}
//相同的棋子
if (g_Chess[x][y] % 2 == ChessType)
{
if ((xStep == 1 && yStep == 0) || (xStep == -1 && yStep == 0))
{
g_SameChessNum++;
}
else if ((xStep == 0 && yStep == 1) || (xStep == 0 && yStep == -1))
{
g_SameChessNum++;
}
else if ((xStep == 1 && yStep == 1) || (xStep == -1 && yStep == -1))
{
g_SameChessNum++;
}
else if ((xStep == 1 && yStep == -1) || (xStep == -1 && yStep == 1))
{
g_SameChessNum++;
}
}
else
{
return g_SameChessNum;
}
}
}
//判断是否五子相连
//x,y:棋子坐标(0~14)
static char IsFiveChessContant(int x, int y)
{
int ChessType = 0;
int ChessNum = 0;
ChessType = g_Chess[x][y] % 2;
//横向判断
ChessNum = GetChessNum(ChessType, x, y, -1, 0) + GetChessNum(ChessType, x, y, 1, 0) + 1;
if (ChessNum >= 5)
{
return 1;
}
//纵向判断
ChessNum = GetChessNum(ChessType, x, y, 0, 1) + GetChessNum(ChessType, x, y, 0, -1) + 1;
if (ChessNum >= 5)
{
return 1;
}
//斜向判断
ChessNum = GetChessNum(ChessType, x, y, -1, 1) + GetChessNum(ChessType, x, y, 1, -1) + 1;
if (ChessNum >= 5)
{
return 1;
}
//反斜向判断
ChessNum = GetChessNum(ChessType, x, y, 1, 1) + GetChessNum(ChessType, x, y, -1, -1) + 1;
if (ChessNum >= 5)
{
return 1;
}
return 0;
}
//游戏结束
//获胜结束
//平局结束
//x,y:棋子坐标(0~14)
static uint8 IsGameOver(uint8 x, uint8 y)
{
//五子相连
if (IsFiveChessContant(x, y)) return 2;
//平局结束
if (g_ChessNum == 225) return 1;
return 0;
}
//悔棋
static void RetractChess(void)
{
int x, y;
int xChess, yChess;
//如果没有棋子则退出
if (g_ChessNum == 0) return;
//查询最后一个棋子并记录
for (x = 0; x < 15; x++)
{
for (y = 0; y < 15; y++)
{
if (g_Chess[x][y] == g_ChessNum)
{
xChess = x;
yChess = y;
break;
}
}
}
//清除最后一个棋子
g_Chess[xChess][yChess] = 0;
g_ChessNum -= 1;
ClearChess(xChess, yChess);
}
//重新开始
static void ResStart(void)
{
int xChess, yChess;
g_ChessNum = 0; //棋子数量
//清空棋子数据
for (xChess = 0; xChess < 15; xChess++)
{
for (yChess = 0; yChess < 15; yChess++)
{
g_Chess[xChess][yChess] = 0;
}
}
//绘制棋盘
DrawChessBoard();
//选择框位置
DrawSelectBox(7, 7);
}
//游戏初始化
//初始化全局变量
//初始化棋盘
void GameInit(void)
{
g_ChessNum = 0; //棋子数量
g_Chess = (char (*)[15])mymalloc(SRAMEX, 15);
LcdClear(BLACK);
//绘制棋盘
DrawChessBoard();
//选择框位置
DrawSelectBox(7, 7);
}
//开始游戏
void GameStart(void)
{
static int xChess = 7;
static int yChess = 7;
uint8 key;
uint8 State;
//读取按键值
key = HC165_Read();
switch (key)
{
case KEY_UP:
while(HC165_Read() == KEY_UP);
ClearSelectBox(xChess, yChess);
yChess -= 1;
if (yChess < 0) yChess = 14;
DrawSelectBox(xChess, yChess);
break;
case KEY_LEFT:
while(HC165_Read() == KEY_LEFT);
ClearSelectBox(xChess, yChess);
xChess -= 1;
if (xChess < 0) xChess = 14;
DrawSelectBox(xChess, yChess);
break;
case KEY_RIGHT:
while(HC165_Read() == KEY_RIGHT);
ClearSelectBox(xChess, yChess);
xChess += 1;
if (xChess >= 15) xChess = 0;
DrawSelectBox(xChess, yChess);
break;
case KEY_DOWN:
while(HC165_Read() == KEY_DOWN);
ClearSelectBox(xChess, yChess);
yChess += 1;
if (yChess >= 15) yChess = 0;
DrawSelectBox(xChess, yChess);
break;
case KEY_OK: //确定
while(HC165_Read() == KEY_OK);
DrawChess(xChess, yChess);
State = IsGameOver(xChess, yChess);
while (State)
{
LcdSetColor(RED);
switch (State)
{
case 1:
LcdFill(60, 90, 260, 126, WHITE);
LcdDispString(100, 100, 320, 16, "Game Over!", 16, 1);
State = 3;
break;
case 2:
LcdFill(60, 90, 260, 126, WHITE);
if (g_Chess[xChess][yChess] % 2)
{
LcdDispString(80, 100, 320, 16, "Black Chess Victory!", 16, 1);
}
else
{
LcdDispString(80, 100, 320, 16, "White Chess Victory!", 16, 1);
}
State = 3;
break;
default:
break;
}
LcdSetColor(BLACK);
//重新开始游戏
if (HC165_Read() == KEY_RESTART)
{
while(HC165_Read() == KEY_RESTART);
ResStart();
xChess = 7;
yChess = 7;
break;
}
}
break;
case KEY_RESTART: //重新开始
while(HC165_Read() == KEY_RESTART);
ResStart();
xChess = 7;
yChess = 7;
break;
case KEY_RETRACT: //悔棋
while(HC165_Read() == KEY_RETRACT);
RetractChess();
break;
default:
break;
}
}