最近写了一个对抗搜索 + α \alpha α- β \beta β剪枝的下五子棋的“人工智障”,贴个代码,头文件在下面:
Gobang.cpp at 2019.6.8
效果大概就是这样,此图是AI对战AI时的效果图。
#include "winshow.cpp"
#include "cursorope.cpp"
//#include <algorithm>
//using namespace std;
/// 五子棋游戏 Day74 论对抗搜索
#define INF (0x3f3f3f3f)
#define BlackPiece (1)
#define WhitePiece (2)
#define SpaceLand (0)
#define Outside (-1)
#include <ctime>
namespace AIsolve { /// 用人工智障分析局势
int scorebd[11][11] = { /// 描述暴搜的搜索顺序
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /// line 0
{-1, 65, 64, 63, 62, 61, 60, 59, 58, 57, -1}, /// line 1
{-1, 66, 37, 36, 35, 34, 33, 32, 31, 56, -1}, /// line 2
{-1, 67, 38, 17, 16, 15, 14, 13, 30, 55, -1}, /// line 3
{-1, 68, 39, 18, 5, 4, 3, 12, 29, 54, -1}, /// line 4
{-1, 69, 40, 19, 6, 1, 2, 11, 28, 53, -1}, /// line 5
{-1, 70, 41, 20, 7, 8, 9, 10, 27, 52, -1}, /// line 6
{-1, 71, 42, 21, 22, 23, 24, 25, 26, 51, -1}, /// line 7
{-1, 72, 43, 44, 45, 46, 47, 48, 49, 50, -1}, /// line 8
{-1, 73, 74, 75, 76, 77, 78, 79, 80, 81, -1}, /// line 9
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /// line 10
};
int PoX[82], PoY[82];
void InitPoXY() { /// 初始化搜索顺序
for(int i = 1; i <= 9; i ++) {
for(int j = 1; j <= 9; j ++) {
PoX[scorebd[i][j]] = i;
PoY[scorebd[i][j]] = j;
}
}
}
int tmpline[20], tcnt; /// 用于直线扫描分析的临时储存
void clear() { /// 清空临时储存
tcnt = 0;
}
void push_back(int item) { /// 向临时储存中添加元素
tmpline[++ tcnt] = item;
} /// tmpline 的可用下标从 1开始
void GetLine(int chsbd[11][11], int x, int y, int dirx, int diry) {
/// 从 -1 开始到 -1 结束, 得到一条直线上的所有棋子
clear(); /// 清空临时储存
int boardcnt = 0; /// 统计 -1出现的次数
while(1) {
if(chsbd[x][y] == -1) boardcnt ++; /// 统计-1出现的次数
push_back(chsbd[x][y]);
if(boardcnt == 2) break; /// 获取完成
x += dirx; /// 挪到下一个位置
y += diry;
}
}
bool CheckSame(int A, int B, int player) { /// 判断两个元素是否同属一段
if(B==player || A==player) {
return A == B;
}else {
if(A==SpaceLand || B==SpaceLand) {
return A == B;
}else {
return true;
}
}
}
/// 连续性分析
int mode[20], length[20], mcnt; /// 对储存的直线信息进行连续性分析
void ContinuousAnalysis(int player) { /// 连续性分析
mcnt = 1; mode[1] = -1; length[1] = 1;
for(int i = 2; i <= tcnt; i ++) {
if(CheckSame(tmpline[i-1], tmpline[i], player)) { /// 延伸上一段
length[mcnt]++;
}else { /// 开辟新一段
mode[++ mcnt] = tmpline[i];
length[mcnt] = 1;
}
}
}
/// 统计局面(注意对于两个玩家要重新分析)
int CntAry[6][2]; /// 用于统计临时数据 0活 1冲
void Count(int player) {
int baseNow = 1, LenSum = 0;
for(int i = 2; i <= mcnt; i ++) {
if(mode[i]==3-player || mode[i]==-1) { /// 寻找下一个界标
if(LenSum >= 5) {
for(int j = baseNow+1; j<=i-1; j ++) { /// 枚举两个界标之间的每一段内容
if(mode[j]==player && length[j]>1) {
int border = j==baseNow+1 || j==i-1; /// 是否为冲
CntAry[length[j]][border] ++;
}
}
}
/// 如果总长度小于5 直接忽略这一段
baseNow = i; LenSum = 0; /// 重新统计长度
}else {
LenSum += length[i]; /// 累加除界标外部分的长度
}
}
}
int GetScore(int chsbd[11][11], int player) { /// 得到某个玩家的估价
memset(CntAry, 0x00, sizeof(CntAry)); /// 清除原数据
/// 统计每一行
for(int i = 1; i <= 9; i ++) {
GetLine(chsbd, i, 0, 0, 1);
ContinuousAnalysis(player);
Count(player);
}
/// 统计每一列
for(int i = 1; i <= 9; i ++) {
GetLine(chsbd, 0, i, 1, 0);
ContinuousAnalysis(player);
Count(player);
}
/// 左上右下斜线
GetLine(chsbd, 0, 0, 1, 1); /// 对角中轴线
ContinuousAnalysis(player);
Count(player);
for(int i = 1; i <= 4; i ++) {
GetLine(chsbd, 0, i, 1, 1); /// 对角中轴线上方
ContinuousAnalysis(player);
Count(player);
GetLine(chsbd, i, 0, 1, 1); /// 对角中轴线下方
ContinuousAnalysis(player);
Count(player);
}
/// 右上左下斜线
GetLine(chsbd, 0, 10, 1, -1); /// 对角中轴线
ContinuousAnalysis(player);
Count(player);
for(int i = 1; i <= 4; i ++) {
GetLine(chsbd, 0, 10-i, 1, -1); /// 上方
ContinuousAnalysis(player);
Count(player);
GetLine(chsbd, i, 10, 1, -1); /// 下方
ContinuousAnalysis(player);
Count(player);
}
/// 统计完成
if(CntAry[5][0] || CntAry[5][1]) return 10000;
else if(CntAry[4][0] || CntAry[4][1]>=2 || (CntAry[4][1] && CntAry[3][0])) return 9900;
else if(CntAry[3][0]>=2 || CntAry[4][1]>=2) return 9800;
else if(CntAry[3][0] && CntAry[4][1]) return 9700;
else {
return (CntAry[3][0]+CntAry[4][1])*2000+CntAry[3][1]*300 + CntAry[2][0]*200 + CntAry[2][1]*50;
}
}
int FinalScore(int chsbd[11][11]) { /// 最终得分
int s1 = GetScore(chsbd, 1), s2 = GetScore(chsbd, 2);
if(s2 >= 9000) return -s2;
else if(s1 >= 9000) return s1;
else return s1 - s2;
}
int StartTime;
int Search(int chsbd[11][11], int player, int& bestpos, int depth, int alpha = -INF, int beta = INF) {
int score = FinalScore(chsbd);
if(depth >= 4 || clock() - StartTime > 10000) { /// 停止搜索
if(player == 1) return alpha>score ? alpha : score;
else return beta<score ? beta : score;
}
bool first = 1;
for(int i = 1; i <= 81; i ++) {
int Xn = PoX[i], Yn = PoY[i];
if(beta <= alpha) break;
if(chsbd[Xn][Yn] == 0) { /// 可以放置
if(first) {
bestpos = i;
first = 0;
}
chsbd[Xn][Yn] = player;
int tmp = 0;
score = Search(chsbd, 3-player, tmp, depth+1, alpha, beta);
if(player == 1) {
//alpha = max(alpha, score);
if(score > alpha) {
alpha = score;
bestpos = i;
}
if(score >= 9000) {
chsbd[Xn][Yn] = 0;
break;
}
}else {
//beta = min(beta, score);
if(score < beta) {
beta = score;
bestpos = i;
}
if(score <= -9000) {
chsbd[Xn][Yn] = 0;
break;
}
}
chsbd[Xn][Yn] = 0;
}
}
if(player == 1) return alpha;
else return beta;
}
void FindWay(int chsbd[11][11], int player, int& X, int& Y) {
/// 分析棋盘局势并且落子于 X行 Y列
StartTime = clock();
int tmp = 0;
Search(chsbd, player, tmp, 0);
X = PoX[tmp];
Y = PoY[tmp];
}
}
namespace Gobang { /// 五子棋游戏
int chessboard[11][11]; /// 描述棋盘状态
wshow::dot GetPiece(int item) { /// 找到棋子对应的形状
switch(item) {
case BlackPiece: return wshow::dot("●", makecol(clr::YELLOW, clr::BLACK));
case WhitePiece: return wshow::dot("●", makecol(clr::YELLOW, clr::WHITE|clr::LIGHT));
case SpaceLand: return wshow::dot("┼", makecol(clr::YELLOW, clr::WHITE));
case 3: return wshow::dot("●", makecol(clr::YELLOW, clr::RED|clr::LIGHT));
default: return wshow::dot("□", makecol(clr::BLACK, clr::YELLOW));
}
}
void Disp(int y, int x) { /// 显示棋盘到屏幕
for(int i = 0; i <= 10; i ++) {
for(int j = 0; j <= 10; j ++) {
wshow::output(GetPiece(chessboard[i][j]), y+i, x+j);
}
}
}
void Init() { /// 初始化棋盘
memset(chessboard, 0x00, sizeof(chessboard));
for(int i = 0; i <= 10; i ++) {
chessboard[i][0] = chessboard[i][10] = -1;
chessboard[0][i] = chessboard[10][i] = -1;
}
}
void RndTest() { /// 生成随机局面(用于输出测试)
for(int i = 1; i <= 9; i ++) {
for(int j = 1; j <= 9; j ++) {
chessboard[i][j] = RND(0, 2);
}
}
}
int tmpx, tmpy, tmpdx, tmpdy;
bool CheckWinInDir(int x, int y, int dirx, int diry) { /// 检测某一点开始某一方向上是否有五连珠
tmpx = x; tmpy =y; tmpdx = dirx; tmpdy = diry;
int tmp = chessboard[x][y];
if(tmp <= 0) return false; /// 空地不可能成为五连珠的起始
for(int i = 1; i <= 5; i ++) {
if(chessboard[x][y] != tmp) return false; /// 发现不同, 不可能出现五连珠
x += dirx;
y += diry;
}
return true; /// 未发现不同 说明存在五连珠
}
void MakeSuc() { /// 将五连珠染红
for(int i = 1; i <= 5; i ++) {
chessboard[tmpx][tmpy] = 3;
tmpx += tmpdx;
tmpy += tmpdy;
}
}
bool CheckWinOnPos(int x, int y) { /// 检测某一点开始是否有五连珠
/// 我们只需要检查 左下, 右下, 右, 下 这四个方向
return CheckWinInDir(x, y, 0, 1) || /// 右
CheckWinInDir(x, y, 1, 0) || /// 下
CheckWinInDir(x, y, 1, -1) || /// 左下
CheckWinInDir(x, y, 1, 1); /// 右下
}
int CheckWin() { /// 检测是否有人胜利
for(int i = 1; i <= 9; i ++) {
for(int j = 1; j <= 9; j ++) {
if(CheckWinOnPos(i, j)) {
MakeSuc();
Disp(0, 0);
return chessboard[i][j]; /// 返回胜利玩家
}
}
}
return 0; /// 返回游戏未结束
}
POINT GetClickOnChessboard(int y, int x) { /// 等待棋盘上的单击左键
POINT p;
while(1) {
p = cursorope::WaitCursorLeftClick();
p.x /= 2; /// 注意横向放缩的问题
#define Rin(A, B, C) (((A)<=(B))&&((B)<=(C))) /// 判断 A<=B<=C
if(Rin(x+1, p.x, x+9) && Rin(y+1, p.y, y+9)) {
p.x -= x; p.y -= y; /// 获得相对位置
if(chessboard[p.y][p.x] == 0) break;
}
}
return p;
}
int MsgCnt = 0;
void OutputMsg(const char* str, int pos = 47, int col = clr::DEFAULT) { /// 用于输出游戏信息
wnd::gotoxy(MsgCnt%20+1, pos);
for(int i = pos; i <= 78; i ++) putchar(' ');
wnd::gotoxy(MsgCnt%20, pos);
for(int i = pos; i <= 78; i ++) putchar(' ');
wnd::gotoxy(MsgCnt%20, pos);
wnd::color(col);
printf("%3d: %s", MsgCnt+1, str);
wnd::color(clr::DEFAULT);
MsgCnt ++;
}
const int WARN_COLOR = makecol(clr::BLACK, clr::RED|clr::LIGHT);
const int SUCC_COLOR = makecol(clr::BLACK, clr::GREEN|clr::LIGHT);
const int TIPS_COLOR = makecol(clr::BLACK, clr::YELLOW|clr::LIGHT); /// 定义几种颜色
const int BLUE_COLOR = makecol(clr::BLACK, clr::PURPLE|clr::LIGHT);
void Game(int y, int x, bool ifperson1 = false, bool ifperson2 = true) {
Init();
chessboard[5][5] = BlackPiece;
Disp(y, x); /// 显示空棋盘(中央一黑子)
POINT p;
int i;
for(i = 1; i <= 40; i ++) {
/// 白棋落子
OutputMsg("等待白棋落子 ...", 47, TIPS_COLOR);
if(ifperson2) { /// 人执白子
p = GetClickOnChessboard(y, x);
chessboard[p.y][p.x] = WhitePiece;
}else { /// 电脑执白子
int X = 0, Y = 0;
AIsolve::FindWay(chessboard, WhitePiece, X, Y);
chessboard[X][Y] = WhitePiece;
}
Disp(y, x);
if(CheckWin()) {
OutputMsg("白棋胜利!", 47, SUCC_COLOR);
break;
}
/// 黑棋落子
OutputMsg("等待黑棋落子 ...", 47, BLUE_COLOR);
if(ifperson1) {
p = GetClickOnChessboard(y, x); /// 人执黑棋
chessboard[p.y][p.x] = BlackPiece;
}else { /// 电脑执黑棋
int X = 0, Y = 0;
AIsolve::FindWay(chessboard, BlackPiece, X, Y);
chessboard[X][Y] = BlackPiece;
}
Disp(y, x);
if(CheckWin()) {
OutputMsg("黑棋胜利!", 47, SUCC_COLOR);
break;
}
}
if(i == 41) {
OutputMsg("平局!", 47, SUCC_COLOR);
}
}
}
#include <ctime>
int testbd[11][11] = { /// 测试专用棋盘
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /// line 0
{-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, /// line 1
{-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, /// line 2
{-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, /// line 3
{-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, /// line 4
{-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, /// line 5
{-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, /// line 6
{-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, /// line 7
{-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, /// line 8
{-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1}, /// line 9
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /// line 10
};
int main() {
srand(time(NULL));
AIsolve::InitPoXY();
//Gobang::Init(); Gobang::RndTest(); Gobang::Disp(0, 0); /// 输出测试
Gobang::Game(0, 0, false, false);
while(1);
//int tmp = AIsolve::GetScore(testbd, BlackPiece);
//printf("%d\n", tmp);
return 0;
}
Chessboard.cpp at 2019.6.15
15*15的人人对战平台(后期可以把人工智障嵌入,由于我现在的人工智障太慢了,以后再嵌入)
效果大概这样:
/// 用于五子棋对战平台
/// 2019.6.14
#ifndef __CHESSBOARD_CPP__
#define __CHESSBOARD_CPP__
#include "cursorope.cpp" /// 鼠标控制头文件
#include "winshow.cpp" /// 控制台显示头文件
#include <cstring>
namespace ChessBoard { /// 对战平台
const int _MaxLength = 15; /// 棋盘的大小为 _MaxLength * _MaxLength
const int _BlackPiece = 1, _WhitePiece = 2, _RedPiece = 3, _Empty = 0, _Outside = -1; /// 棋子类型
int statue[_MaxLength + 2][_MaxLength + 2]; /// 描述棋盘上的信息
void InitStatue() { /// 初始化棋盘
memset(statue, 0x00, sizeof(statue)); /// 清空棋盘
for(int i = 0; i <= _MaxLength + 1; i ++) { /// 设置边界
statue[i][0] = statue[0][i] = _Outside;
statue[i][_MaxLength + 1] = statue[_MaxLength + 1][i] = _Outside;
}
/// 在中间位置放一粒黑子
int middle = (_MaxLength + 1)/2;
statue[middle][middle] = _BlackPiece;
}
wshow::dot GetPiece(int item) { /// 找到棋子对应的字符
switch(item) {
case _BlackPiece: return wshow::dot("●", makecol(clr::YELLOW, clr::BLACK));
case _WhitePiece: return wshow::dot("●", makecol(clr::YELLOW, clr::WHITE|clr::LIGHT));
case _Empty: return wshow::dot("┼", makecol(clr::YELLOW, clr::WHITE));
case _RedPiece: return wshow::dot("●", makecol(clr::YELLOW, clr::RED|clr::LIGHT));
default: return wshow::dot("□", makecol(clr::BLACK, clr::YELLOW));
}
}
void Display() { /// 显示棋盘至控制台
for(int i = 0; i <= _MaxLength + 1; i ++) {
for(int j = 0; j <= _MaxLength + 1; j ++) {
wshow::output(GetPiece(statue[i][j]), i, j);
}
}
}
POINT GetClick(int y = 0, int x = 0) { /// 等待棋盘上的单击左键
POINT p;
while(1) {
p = cursorope::WaitCursorLeftClick();
p.x /= 2; /// 注意横向放缩的问题
#define Rin(A, B, C) (((A)<=(B))&&((B)<=(C))) /// 判断 A<=B<=C
if(Rin(x+1, p.x, x+_MaxLength) && Rin(y+1, p.y, y+_MaxLength)) {
p.x -= x; p.y -= y; /// 获得相对位置
if(statue[p.y][p.x] == 0) break;
}
}
return p;
}
int tmpx, tmpy, tmpdx, tmpdy;
bool CheckWinInDir(int x, int y, int dirx, int diry) { /// 检测某一点开始某一方向上是否有五连珠
tmpx = x; tmpy =y; tmpdx = dirx; tmpdy = diry;
int tmp = statue[x][y];
if(tmp <= 0) return false; /// 空地不可能成为五连珠的起始
for(int i = 1; i <= 5; i ++) {
if(statue[x][y] != tmp) return false; /// 发现不同, 不可能出现五连珠
x += dirx;
y += diry;
}
return true; /// 未发现不同 说明存在五连珠
}
void MakeSuc() { /// 将五连珠染红
for(int i = 1; i <= 5; i ++) {
statue[tmpx][tmpy] = 3;
tmpx += tmpdx;
tmpy += tmpdy;
}
}
bool CheckWinOnPos(int x, int y) { /// 检测某一点开始是否有五连珠
/// 我们只需要检查 左下, 右下, 右, 下 这四个方向
return CheckWinInDir(x, y, 0, 1) || /// 右
CheckWinInDir(x, y, 1, 0) || /// 下
CheckWinInDir(x, y, 1, -1) || /// 左下
CheckWinInDir(x, y, 1, 1); /// 右下
}
int CheckWin() { /// 检测是否有人胜利
for(int i = 1; i <= _MaxLength; i ++) {
for(int j = 1; j <= _MaxLength; j ++) {
if(CheckWinOnPos(i, j)) {
int tmp = statue[i][j];
MakeSuc();
Display();
return tmp; /// 返回胜利玩家
}
}
}
return 0; /// 返回游戏未结束
}
int MsgCnt = 0;
void OutputMsg(const char* str, int pos = 47, int col = clr::DEFAULT) { /// 用于输出游戏信息
wnd::gotoxy(MsgCnt%20+1, pos);
for(int i = pos; i <= 78; i ++) putchar(' ');
wnd::gotoxy(MsgCnt%20, pos);
for(int i = pos; i <= 78; i ++) putchar(' ');
wnd::gotoxy(MsgCnt%20, pos);
wnd::color(col);
printf("%3d: %s", MsgCnt+1, str);
wnd::color(clr::DEFAULT);
MsgCnt ++;
}
const int WARN_COLOR = makecol(clr::BLACK, clr::RED|clr::LIGHT);
const int SUCC_COLOR = makecol(clr::BLACK, clr::GREEN|clr::LIGHT);
const int TIPS_COLOR = makecol(clr::BLACK, clr::YELLOW|clr::LIGHT); /// 定义几种颜色
const int BLUE_COLOR = makecol(clr::BLACK, clr::PURPLE|clr::LIGHT);
int Game() { /// 进行一轮游戏
InitStatue(); /// 初始化棋盘
Display();
for(int i = 1; i <= _MaxLength*_MaxLength-1; i ++) {
int piece = (i%2==0) ? _BlackPiece : _WhitePiece;
OutputMsg((piece==_BlackPiece) ? "等待黑棋落子 ..." : "等待白棋落子 ...", 47,
(piece==_BlackPiece) ? TIPS_COLOR : BLUE_COLOR);
POINT p = GetClick();
statue[p.y][p.x] = piece; /// 从鼠标获取落子
Display();
int tmp;
if(tmp=CheckWin()) { /// 判断是否有人胜利
OutputMsg((tmp==_BlackPiece) ? "黑棋胜利!" : "白棋胜利!", 47, WARN_COLOR);
return tmp;
}
}
OutputMsg("平局!", 47, SUCC_COLOR);
return _Empty;
}
}
#include <ctime>
int main() {
srand(time(NULL)); /// 重置随机种子
ChessBoard::Game();
while(1);
return 0;
}
#else
/// 文件已经被引用过
#endif
cursorope.cpp 鼠标操作头文件
/// 2019.6.8 鼠标操作头文件
#ifndef __CURSOROPE_CPP__
#define __CURSOROPE_CPP__
#include <windows.h>
#include <cstdio>
namespace hwndforcursor { /// hfc
HWND hwnd=GetForegroundWindow(); /// 当前窗口句柄
bool checkupon() { /// 判断当前窗口是否在最顶层
HWND hwndn=GetForegroundWindow(); return hwndn == hwnd;
}
#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0)
}
#define hfc hwndforcursor
namespace cursorope { /// 支持鼠标操作
POINT GetCursorPos() { /// 获取鼠标坐标
POINT p;
GetCursorPos(&p); /// windows API 获取鼠标坐标
return p; /// 横x 纵y 左上角是(0, 0)
}
void OutputPoint(POINT p) { /// 输出点坐标
printf("(%5d, %5d)", p.x, p.y);
}
RECT GetConsolePos() { /// 获取当前窗口的像素坐标
RECT rect;
GetWindowRect(hfc::hwnd,&rect); /// windows API 获取控制台窗口的坐标
return rect;
}
POINT GetCursorPosInConsole() { /// 获得鼠标相对于控制台的相对位置
POINT p = GetCursorPos();
RECT rect = GetConsolePos();
p.x -= rect.left;
p.y -= rect.top;
return p;
}
POINT GetCursorCharPosInConsole() { /// 找到鼠标在控制台上对应字符位置的坐标
POINT p = GetCursorPosInConsole();
p.x -= 5; p.x /= 8;
p.y -= 31; p.y /= 16;
return p;
}
#define Rin(A, B, C) (((A)<=(B))&&((B)<=(C))) /// 判断 A<=B<=C
POINT WaitCursorLeftClick() { /// 等待鼠标点击控制台然后返回点击的字符位置
POINT p;
while(1) {
if(KEY_DOWN(VK_LBUTTON) && hfc::checkupon()) {
p = GetCursorCharPosInConsole();
if(Rin(0, p.x, 79) && Rin(0, p.y, 24)) {
break;
}
}
Sleep(20);
}
Sleep(100);
return p;
}
}
/*
#include "winshow.cpp"
int main() {
while(1) {
POINT p = cursorope::WaitCursorLeftClick();
wshow::output(wshow::dot("●"), p.y, p.x/2);
}
return 0;
}*/
#undef hfc
#undef Rin
#else
/// 头文件已经被定义过了
#endif
winshow.cpp 控制台输出头文件
#ifndef __WINSHOW_CPP__
#define __WINSHOW_CPP__
/// 2019.6.6 控制台输出
/// 粘的 Tetris 原来的板子
#include <windows.h>
#include <cstdlib>
#include <cstdio>
namespace hwndset {
HWND hwnd=GetForegroundWindow(); //当前窗口句柄
bool checkupon() { /// 判断当前窗口是否在最顶层
HWND hwndn=GetForegroundWindow(); return hwndn == hwnd;
}
#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0) /// 检测字符是否按下
///222: #define VK_LEFT 0x25
///223: #define VK_UP 0x26
///224: #define VK_RIGHT 0x27
///225: #define VK_DOWN 0x28 -- from <windows.h>
#define SHAKE (5)
void shake(){ /// 屏幕晃动, 懒得写了, 粘的板子
RECT rect;
/// HWND hwnd=GetForegroundWindow();
GetWindowRect(hwnd,&rect);
MoveWindow(hwnd,rect.left+SHAKE,rect.top,rect.right-rect.left,rect.bottom-rect.top,TRUE);
Sleep(28);
MoveWindow(hwnd,rect.left+SHAKE,rect.top-SHAKE,rect.right-rect.left,rect.bottom-rect.top,TRUE);
Sleep(28);
MoveWindow(hwnd,rect.left,rect.top-SHAKE,rect.right-rect.left,rect.bottom-rect.top,TRUE);
Sleep(28);
MoveWindow(hwnd,rect.left,rect.top,rect.right-rect.left,rect.bottom-rect.top,TRUE);
}
#undef SHAKE
}
int RND(int L, int R) {
#define RND ((rand()<<15)+rand())
return RND%(R - L + 1) + L;
#undef RND
}
namespace clr { /// 关于颜色的一些常量
const int BLACK=0, BLUE=1, GREEN=2, CYAN=3, RED=4, PURPLE=5, YELLOW=6, WHITE=7, LIGHT=8;
const int DEFAULT = (clr::BLACK<<4)|clr::WHITE;
int makecol(int background, int letter) { /// 装载一种颜色
return (background << 4) | letter;
}
}
using clr::makecol; /// 开放一个对外函数 makecol
namespace wnd { /// 关于 Windows API 的使用
void gotoxy(int y, int x) { /// 更改屏幕输出位置
COORD pos; pos.X = x; pos.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos);
}
void color(int a) { /// 更改输出指针颜色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),a);
}
}
namespace wshow {
struct dot { /// 屏幕上的基本单位
char ch[2], col; /// 两个字节内容,以及其颜色
dot() {
col = makecol(clr::BLACK, clr::WHITE); // 默认"黑底白字"
ch[0] = ch[1] = 0; /// 默认没有任何字符
}
dot(const char* s, int c = clr::DEFAULT) { /// 通过字符串构造 dot
ch[0] = s[0], ch[1] = s[1]; col = c; /// 初始化一个字符
if(ch[0]<' ' && ch[0]>=-2) ch[0] = 0;
if(ch[1]<' ' && ch[1]>=-2) ch[1] = 0; /// 避免 tab '\n' 之类的不必要的控制字符的出现
}
} Screen[24][39]; /// 用这个数组去描述屏幕中"受控制(被记录)"的位置
//int store[24][39]; /// 这是俄罗斯方块数组的"固化版"
bool operator == (dot A, dot B) { /// 判断两个 dot 是否相同
return A.ch[0]==B.ch[0] && A.ch[1]==B.ch[1] && A.col==B.col; /// 考虑颜色
}
//dot transcol(dot A,int ncol) {
// return dot(A.ch, ncol); /// 返回一个内容相同颜色不同的 dot
//}
void print(dot A) { /// 输出一个 dot
wnd::color(A.col); /// 先把输入指针变到对应的颜色
printf("%c%c", A.ch[0], A.ch[1]); /// 输出这个字符
wnd::color(clr::DEFAULT); /// 及时恢复到默认颜色
}
void output(dot A, int y, int x) { /// 唯一的一个新写的函数
if(!(Screen[y][x] == A)) { /// 简单粗暴地指定一个位置, 并输出字符
wnd::gotoxy(y, x*2);
print(A);
}
}
}
/*
int main() {
return 0;
}
*/
#else
/// 说明头文件已经被引用过了
#endif