转载-游戏开发(三)——WIN32 黑白棋(一)——棋局逻辑的设计
转载-游戏开发(三)——WIN32 黑白棋(一)——棋局逻辑的设计
今天以黑白棋为例,开始给一个win32的小游戏设计,
分3部分介绍。
1、棋局的现实
2、AI的现实
3、游戏画面的现实
提供一下完整项目下载
其中第一部分为黑白棋游戏的主要逻辑:
1、棋盘,以及棋盘上的棋子的存储形式。这里用到了位图。
2、是否可以落子的判断(黑白棋是只有你落子的位置,在横竖斜八个方向中任意一个方向,能吃掉对方的子,你才可以落在该位置,八个方向都吃不掉对方子的位置是不能下的),以及吃子的逻辑(吃子的逻辑同样是八个方向,两个己方棋子之间夹住的对方棋子,可以被吃掉,翻转为己方棋子)。这里为了使得代码简介一点,使用了函数指针(不同方向上坐标的变化逻辑不一样)。
3、某一方下了一个子之后,交换手的判断(黑白棋中存在可能,一方下了一个子,并吃掉对方的子,之后对方无子可下,没有一个位置能使得对方能吃掉己方的棋子,所以黑白棋并不一定始终是一人一步来的,它可能存在一方连续落子的情况)。
4、游戏是否结束的判断(由于存在无子可下的情况,有可能双方都无子可以下,即一方将另一方全部吃光,所以黑白棋不一定是下满棋盘才分出胜负)。
第二部分主要为了写AI:
黑白棋的AI其实蛮复杂,有专门的研究黑白棋的AI的算法文章,这里只介绍一下,然后简单实现了一个AI,主要是最大最小算法,以及枝剪算法。
第三部分主要是游戏画面的显示:
涉及到windows消息机制,鼠标事件,键盘事件,菜单事件,定时器事件;以及简单的图形、文字绘制,涉及到画笔、画刷填充、绘图层HDC、画线、画圆、显示文字、双缓冲的位图拷贝。
阅读第三部分前,读者可以先行阅读《windows程序设计》一书打个基础。也可以看完博文之后,再将涉及到的图形API,消息机制等windows程序设计中涉及到的点带回到书中去详细了解。
黑白棋游戏在设计中需要注意的几点:
1、惯例,首先要定义好棋盘的坐标,定义为左上角那一格为(0,0),向右为x正方向,向下为y正方向,黑白棋棋盘是一个8*8的棋盘,所以定义两个常量表示:
- const int REVERSI_MAX_ROW = 8;
- const int REVERSI_MAX_COLUMN = 8;
2、棋盘上棋子的类型分三种:黑子,白子,空白无子,枚举表示
- enum EnumReversiPiecesType
- {
- enum_ReversiPieces_Null = 0x00,
- enum_ReversiPieces_Black = 0x01,
- enum_ReversiPieces_White = 0x02,
- };
这三种情况,其实用2位2进制即可表示,一行8个位置就是16位2进制,就是一个WORD就足够了,所以:
3、棋盘的表示,位图
TArray1<WORD, REVERSI_MAX_ROW> m_Map;
位图是8行,每行是一个WORD,这个TArray1是之前实现的 一维数组模板直接用的
4、棋盘上一个位置的设计,因为这里涉及到位置(即坐标)的八方向移动的逻辑,因此将坐标位置单独抽象出来,实现坐标的上下左右以及斜的四方向的坐标变化,然后将其重定义为函数指针,使得后面在坐标变化时,不用switch...case八种情况,而是可以将方向当成参数。
typedef void (ReversiPoint::*ReversiPointForward)();
5、某一方的棋子,在某一坐标位置,向某一方向,是否可以吃掉对方的棋子的判断
bool ReversiCheck(EnumReversiPiecesType type, char row_y, char column_x, ReversiPointForward forward);
是否可以吃子的伪代码:
- 定义一个坐标对象point,初值为当前点row_y, column_x
- 记录该方向上的搜索次数search,初值为0
- point向forward方向移动
- 搜索次数search++
- while (point是一个合法的坐标,不能移出棋盘外面去了)
- {
- 取point当前位置的棋子类型
- (此时已经是forward移动一次之后的位置了,不是row_y, column_x了)
- if (当前位置有棋子)
- {
- if (当前位置的棋子类型等于传入参数type,type就是要下的棋子类型)
- {
- if (搜索次数search大于1次)
- {
- 说明找到的同色棋子与当前棋子坐标差超过1,point至少移动了2次
- 则两子之间夹有不同色的棋子
- 符合翻转规则,return true
- }
- else
- {
- 说明找到的同色棋子与当前棋子,两子是紧挨着的
- 该方向两子之间无子可以翻转
- 不符合翻转规则,return false
- }
- }
- else
- {
- 说明找到的是不同色的棋子,继续向下一个位置搜
- point向forward方向移动
- 搜索次数search++
- }
- }
- else
- {
- 一直找到空位也没找到,该方向没有同色棋子,无法翻转
- }
- }
- 超出棋盘范围都没有找到同色棋子,该方向没有同色棋子,无法翻转
6、某一方的棋子,在某一坐标位置,向某一方向,吃掉对方的棋子
void DoReversi(EnumReversiPiecesType type, char row_y, char column_x, ReversiPointForward forward);
伪代码实现
- 定义一个坐标对象point,初值为当前点row_y, column_x
- point向forward方向移动
- while (point是一个合法的坐标,不能移出棋盘外面去了)
- {
- 取point当前位置的棋子类型
- (此时已经是forward移动一次之后的位置了,不是row_y, column_x了)
- if (当前位置的棋子类型不等于传入参数type,type就是下的棋子类型)
- {
- 将该位置的棋子类型翻转为type一方的棋子
- point向forward方向移动
- 因为在翻转之前做了ReversiCheck的判断
- 即这个方向肯定是符合翻转规则,有子可吃的
- 所以这里不再判断当前位置的棋子类型是否为空
- }
- }
有了上面两个基本函数
7、判断某个位置是否可以落子
bool ReversiCheck(EnumReversiPiecesType type, char row_y, char column_x);
则是分别调用上面的ReversiCheck,然后forward传入不同的方向
8、判断某一方是否可以落子
bool CanPlay(EnumReversiPiecesType type);
即遍历棋盘每一个位置,任意一个位置可以落子,则该方可以落子
9、落一个子之后的吃子
void DoReversi(EnumReversiPiecesType type, char row_y, char column_x);
则是分别调用上面的DoReversi,然后forward传入不同的方向
10、最后,判断游戏是否结束的逻辑,即双方都无子可下,则游戏结束
先给个游戏截图吧
下面先贴出第一部分的代码
ReversiCommon.h
- #ifndef _ReversiCommon_h_
- #define _ReversiCommon_h_
- #include <windows.h>
- //棋盘大小
- const int REVERSI_MAX_ROW = 8;
- const int REVERSI_MAX_COLUMN = 8;
- enum EnumReversiPiecesType
- {
- enum_ReversiPieces_Null = 0x00,
- enum_ReversiPieces_Black = 0x01,
- enum_ReversiPieces_White = 0x02,
- };
- EnumReversiPiecesType SwapType(EnumReversiPiecesType type);
- enum EnumReversiResult
- {
- enum_Reversi_Playing = 0,
- enum_Reversi_Draw,
- enum_Reversi_Win_Black,
- enum_Reversi_Win_White,
- };
- //权值表
- const int g_Weight[REVERSI_MAX_ROW][REVERSI_MAX_COLUMN] = {
- {0x1 << 24, 0x1, 0x1 << 20, 0x1 << 16,0x1 << 16,0x1 << 20, 0x1, 0x1 << 24},
- {0x1, 0x1, 0x1 << 16, 0x1 << 4, 0x1 << 4, 0x1 << 16, 0x1, 0x1 },
- {0x1 << 20, 0x1 << 16, 0x1 << 12, 0x1 << 8, 0x1 << 8, 0x1 << 12, 0x1 << 16, 0x1 << 20},
- {0x1 << 16, 0x1 << 4, 0x1 << 8, 0, 0, 0x1 << 8, 0x1 << 4, 0x1 << 16},
- {0x1 << 16, 0x1 << 4, 0x1 << 8, 0, 0, 0x1 << 8, 0x1 << 4, 0x1 << 16},
- {0x1 << 20, 0x1 << 16, 0x1 << 12, 0x1 << 8, 0x1 << 8, 0x1 << 12, 0x1 << 16, 0x1 << 20},
- {0x1, 0x1, 0x1 << 16, 0x1 << 4, 0x1 << 4, 0x1 << 16, 0x1, 0x1 },
- {0x1 << 24, 0x1, 0x1 << 20, 0x1 << 16,0x1 << 16,0x1 << 20, 0x1, 0x1 << 24}
- };
- //按权值表降序排列的坐标顺序表
- const BYTE g_WeightOrder[REVERSI_MAX_ROW * REVERSI_MAX_COLUMN - 4][2] = {
- { 0, 0}, { 0, 7}, { 7, 0}, { 7, 7},//0x01000000
- { 0, 2}, { 0, 5}, { 2, 0}, { 2, 7},//0x00100000
- { 7, 2}, { 7, 5}, { 5, 0}, { 5, 7},
- { 0, 3}, { 0, 4}, { 1, 2}, { 1, 5},//0x00010000
- { 2, 1}, { 2, 6}, { 3, 0}, { 3, 7},
- { 4, 0}, { 4, 7}, { 5, 1}, { 5, 6},
- { 6, 2}, { 6, 5}, { 7, 3}, { 7, 4},
- { 2, 2}, { 2, 5}, { 5, 2}, { 5, 5},//0x00001000
- { 2, 3}, { 2, 4}, { 3, 2}, { 3, 5},//0x00000100
- { 4, 2}, { 4, 5}, { 5, 3}, { 5, 4},
- { 1, 3}, { 1, 4}, { 3, 1}, { 3, 6},//0x00000010
- { 4, 1}, { 4, 6}, { 6, 3}, { 6, 4},
- { 0, 1}, { 0, 6}, { 1, 0}, { 1, 7},//0x00000001
- { 6, 0}, { 6, 7}, { 7, 1}, { 7, 6},
- { 1, 1}, { 1, 6}, { 6, 1}, { 6, 6} //0x00000001
- //{ 3, 3}, { 3, 4}, { 4, 3}, { 4, 4}, 初始4个位置不用判断
- };
- #endif
ReversiCommon.cpp
- #include "ReversiCommon.h"
- EnumReversiPiecesType SwapType(EnumReversiPiecesType type)
- {
- if (enum_ReversiPieces_Black == type)
- {
- return enum_ReversiPieces_White;
- }
- else if (enum_ReversiPieces_White == type)
- {
- return enum_ReversiPieces_Black;
- }
- else
- {
- return enum_ReversiPieces_Null;
- }
- }
ReversiPoint.h
- #ifndef _ReversiPoint_h_
- #define _ReversiPoint_h_
- #include "ReversiCommon.h"
- typedef struct ReversiPoint
- {
- char m_row_y;
- char m_column_x;
- ReversiPoint& operator= (const ReversiPoint& temp)
- {
- m_row_y = temp.m_row_y;
- m_column_x = temp.m_column_x;
- return *this;
- }
- bool operator!= (const ReversiPoint& temp)
- {
- if (m_row_y == temp.m_row_y &&
- m_column_x == temp.m_column_x)
- {
- return false;
- }
- else
- {
- return true;
- }
- }
- bool IsValid()
- {
- if (0 <= m_row_y &&
- 0 <= m_column_x &&
- m_row_y < REVERSI_MAX_ROW &&
- m_column_x < REVERSI_MAX_COLUMN)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- void UL()
- {
- m_row_y--;
- m_column_x--;
- }
- void U()
- {
- m_row_y--;
- }
- void UR()
- {
- m_row_y--;
- m_column_x++;
- }
- void L()
- {
- m_column_x--;
- }
- void R()
- {
- m_column_x++;
- }
- void DL()
- {
- m_row_y++;
- m_column_x--;
- }
- void D()
- {
- m_row_y++;
- }
- void DR()
- {
- m_row_y++;
- m_column_x++;
- }
- }ReversiPoint;
- typedef void (ReversiPoint::*ReversiPointForward)();
- #endif
ReversiBitBoard.h
- #ifndef _ReversiBitBoard_h_
- #define _ReversiBitBoard_h_
- #include <Windows.h>
- #include "TArray.h"
- #include "ReversiCommon.h"
- #include "ReversiPoint.h"
- class ReversiBitBoard
- {
- public:
- ReversiBitBoard();
- ~ReversiBitBoard();
- void Init();
- ReversiBitBoard& operator= (const ReversiBitBoard& temp);
- void SetPieces(EnumReversiPiecesType type, char row_y, char column_x);
- EnumReversiPiecesType GetPieces(char row_y, char column_x);
- EnumReversiResult IsGameOver();
- bool CanPlay(EnumReversiPiecesType type);
- bool CanPlay(EnumReversiPiecesType type, char row_y, char column_x);
- bool ReversiCheck(EnumReversiPiecesType type, char row_y, char column_x);
- void DoReversi(EnumReversiPiecesType type, char row_y, char column_x);
- int GetCount(EnumReversiPiecesType type);
- void SwapPlayer();
- EnumReversiPiecesType GetCurrType();
- private:
- void DoReversi(EnumReversiPiecesType type, char row_y, char column_x,
- ReversiPointForward forward);
- bool ReversiCheck(EnumReversiPiecesType type, char row_y, char column_x,
- ReversiPointForward forward);
- TArray1<WORD, REVERSI_MAX_ROW> m_Map;
- EnumReversiPiecesType m_CurrType;
- };
- #endif
ReversiBitBoard.cpp
- #include "ReversiBitBoard.h"
- ReversiBitBoard::ReversiBitBoard()
- {
- }
- ReversiBitBoard::~ReversiBitBoard()
- {
- }
- void ReversiBitBoard::Init()
- {
- m_CurrType = enum_ReversiPieces_Black;//规定黑先
- for (int i = 0; i < REVERSI_MAX_ROW; i++)
- {
- m_Map[i] = 0;
- }
- SetPieces(enum_ReversiPieces_White, 3, 3);
- SetPieces(enum_ReversiPieces_Black, 3, 4);
- SetPieces(enum_ReversiPieces_Black, 4, 3);
- SetPieces(enum_ReversiPieces_White, 4, 4);
- }
- ReversiBitBoard& ReversiBitBoard::operator=(const ReversiBitBoard& temp)
- {
- m_Map = temp.m_Map;
- m_CurrType = temp.m_CurrType;
- return *this;
- }
- void ReversiBitBoard::SetPieces(EnumReversiPiecesType type, char row_y, char column_x)
- {
- m_Map[row_y] = m_Map[row_y] & (~(0x0003 << (column_x * 2)));
- m_Map[row_y] = m_Map[row_y] | (type << (column_x * 2));
- }
- EnumReversiPiecesType ReversiBitBoard::GetPieces(char row_y, char column_x)
- {
- WORD value = m_Map[row_y] & (0x0003 << (column_x * 2));
- value = value >> (column_x * 2);
- EnumReversiPiecesType type = static_cast<EnumReversiPiecesType>(value);
- return type;
- }
- int ReversiBitBoard::GetCount(EnumReversiPiecesType type)
- {
- int count = 0;
- for (int i = 0; i < REVERSI_MAX_ROW; i++)
- {
- for (int j = 0; j < REVERSI_MAX_COLUMN; j++)
- {
- if (type == GetPieces(i, j))
- {
- count++;
- }
- }
- }
- return count;
- }
- EnumReversiResult ReversiBitBoard::IsGameOver()
- {
- if (!CanPlay(enum_ReversiPieces_Black) &&
- !CanPlay(enum_ReversiPieces_White))
- {
- int black = GetCount(enum_ReversiPieces_Black);
- int white = GetCount(enum_ReversiPieces_White);
- if (black > white)
- {
- return enum_Reversi_Win_Black;
- }
- else if (black < white)
- {
- return enum_Reversi_Win_White;
- }
- else
- {
- return enum_Reversi_Draw;
- }
- }
- else
- {
- return enum_Reversi_Playing;
- }
- }
- bool ReversiBitBoard::CanPlay(EnumReversiPiecesType type)
- {
- for (int i = 0; i < REVERSI_MAX_ROW; i++)
- {
- for (int j = 0; j < REVERSI_MAX_COLUMN; j++)
- {
- if (CanPlay(type, i, j))
- {
- return true;
- }
- }
- }
- return false;
- }
- bool ReversiBitBoard::CanPlay(EnumReversiPiecesType type, char row_y, char column_x)
- {
- if (enum_ReversiPieces_Null == GetPieces(row_y, column_x))
- {
- if (ReversiCheck(type, row_y, column_x))
- {
- return true;
- }
- }
- return false;
- }
- bool ReversiBitBoard::ReversiCheck(EnumReversiPiecesType type,
- char row_y, char column_x)
- {
- if (ReversiCheck(type, row_y, column_x, &ReversiPoint::UL) ||
- ReversiCheck(type, row_y, column_x, &ReversiPoint::U) ||
- ReversiCheck(type, row_y, column_x, &ReversiPoint::UR) ||
- ReversiCheck(type, row_y, column_x, &ReversiPoint::L) ||
- ReversiCheck(type, row_y, column_x, &ReversiPoint::R) ||
- ReversiCheck(type, row_y, column_x, &ReversiPoint::DL) ||
- ReversiCheck(type, row_y, column_x, &ReversiPoint::D) ||
- ReversiCheck(type, row_y, column_x, &ReversiPoint::DR))
- {
- return true;
- }
- return false;
- }
- bool ReversiBitBoard::ReversiCheck(EnumReversiPiecesType type,
- char row_y, char column_x,
- ReversiPointForward forward)
- {
- ReversiPoint point = {row_y, column_x};
- EnumReversiPiecesType currType;
- int search = 0;
- (point.*forward)();//向某方向搜寻
- search++;
- while(point.IsValid())
- {
- currType = GetPieces(point.m_row_y, point.m_column_x);
- if (enum_ReversiPieces_Null != currType)
- {
- if (type == currType)
- {
- if (search > 1)
- {
- //找到的同色棋子与当前棋子坐标差超过1,则两子之间夹有不同色的棋子
- return true;
- }
- else
- {
- //否则两子是紧挨着的,该方向两子之间无子可以翻转
- return false;
- }
- }
- else
- {
- //找到的是不同色的棋子,继续
- (point.*forward)();
- search++;
- }
- }
- else
- {
- //一直找到空位也没找到,该方向没有同色棋子,无法翻转
- return false;
- }
- }
- //超出棋盘范围都没有找到同色棋子,该方向没有同色棋子,无法翻转
- return false;
- }
- void ReversiBitBoard::DoReversi(EnumReversiPiecesType type,
- char row_y, char column_x)
- {
- if (ReversiCheck(type, row_y, column_x, &ReversiPoint::UL))
- {
- DoReversi(type, row_y, column_x, &ReversiPoint::UL);
- }
- if (ReversiCheck(type, row_y, column_x, &ReversiPoint::U))
- {
- DoReversi(type, row_y, column_x, &ReversiPoint::U);
- }
- if (ReversiCheck(type, row_y, column_x, &ReversiPoint::UR))
- {
- DoReversi(type, row_y, column_x, &ReversiPoint::UR);
- }
- if (ReversiCheck(type, row_y, column_x, &ReversiPoint::L))
- {
- DoReversi(type, row_y, column_x, &ReversiPoint::L);
- }
- if (ReversiCheck(type, row_y, column_x, &ReversiPoint::R))
- {
- DoReversi(type, row_y, column_x, &ReversiPoint::R);
- }
- if (ReversiCheck(type, row_y, column_x, &ReversiPoint::DL))
- {
- DoReversi(type, row_y, column_x, &ReversiPoint::DL);
- }
- if (ReversiCheck(type, row_y, column_x, &ReversiPoint::D))
- {
- DoReversi(type, row_y, column_x, &ReversiPoint::D);
- }
- if (ReversiCheck(type, row_y, column_x, &ReversiPoint::DR))
- {
- DoReversi(type, row_y, column_x, &ReversiPoint::DR);
- }
- }
- void ReversiBitBoard::DoReversi(EnumReversiPiecesType type,
- char row_y, char column_x,
- ReversiPointForward forward)
- {
- ReversiPoint point = {row_y, column_x};
- (point.*forward)();
- while(point.IsValid())
- {
- if (type != GetPieces(point.m_row_y, point.m_column_x))
- {
- SetPieces(type, point.m_row_y, point.m_column_x);
- (point.*forward)();
- }
- else
- {
- break;
- }
- }
- }
- void ReversiBitBoard::SwapPlayer()
- {
- EnumReversiPiecesType nexttype = SwapType(m_CurrType);
- if (CanPlay(nexttype))
- {
- m_CurrType = nexttype;
- }
- }
- EnumReversiPiecesType ReversiBitBoard::GetCurrType()
- {
- return m_CurrType;
- }
转载-游戏开发(三)——WIN32 黑白棋(一)——棋局逻辑的设计相关推荐
- HTML5 游戏开发实战 | 黑白棋
黑白棋,又叫反棋(Reversi).奥赛罗棋(Othello).苹果棋.翻转棋.黑白棋在西方和日本很流行.游戏通过相互翻转对方的棋子,最后以棋盘上谁的棋子多来判断胜负.黑白棋的棋盘是一个有8×8方格的 ...
- HTML5游戏开发(三):使用webpack构建TypeScript应用
<HTML5游戏开发>系列文章的目的有:一.以最小的成本去入门egret小项目开发,官方的教程一直都是面向中重型:二.egret可以非常轻量:三.egret相比PIXI.js和sprite ...
- Windows游戏设计(三)- 黑白棋游戏 - 使用Win32 SDK
注:以下程序为本人原创,写的不好,若有好的建议,望留言告知.而若能帮助一二访客,幸甚! 上回用Python 写黑白棋,后来想添加个最小最大规则搜索博弈树的算法,没能实现,于是想先用Win32 写一个, ...
- 游戏开发攻略—黑杰克扑克牌
欢迎来到:http://observer.blog.51cto.com 21点黑杰克扑克牌为一款极具趣味性的牌类游戏,最早出现在十六世纪,起源于法国.同时,发现在网络上流传着的各种版本的21点黑杰克扑 ...
- C++程设实验项目三:黑白棋与基于UCT算法的AI
在这篇博客里,我将总结一下在这次实验中学到的UCT算法实现原理. 首先是参考文章: https://blog.csdn.net/u014397729/article/details/27366363 ...
- 1、Cocos2dx 3.0游戏开发三找一小块前言
尊重开发人员的劳动成果,转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/27094663 前言 Cocos2d-x 是一个通用 ...
- 【VC++游戏开发#三】2D篇 —— 游戏之一:空中大战(SpaceWar)
本文由BlueCoder编写 转载请说明出处: http://blog.csdn.net/crocodile__/article/details/13505997 我的邮箱:bluecoder@y ...
- android 双人黑白棋开发博客,黑白棋 - 软件资讯 - 课堂党年级博客
黑白棋叫反棋(Reversi).奥赛罗棋(Othello)苹棋翻转棋黑白棋西本流行游戏通相互翻转棋棋盘谁棋判断胜负 黑白棋棋盘8*8格棋盘棋棋空格间像围棋交叉点始棋盘两白两黑四棋交叉放置黑棋总先 自颜 ...
- 小程序游戏开发三个引擎用哪个好呢 Cocos,Egret,Laya?
随着微信生态中,小程序应用指数级的增长,许多休闲游戏变成为了众多游戏厂商流量变现的新手段.以近期很火的"羊了个羊"为例,它便是我们常常所说的小游戏. 游戏和小游戏的区别 要盘点小游 ...
- Qt开发自学13_黑白棋
20200603黑白棋 我们的实现目标如下图所示:(不是五子棋) 步骤:1画背景:2画线(横纵各9根):3画棋子(8*8=64个) 掌握:绘图技巧.封装思想 New Project:创建棋盘类Ches ...
最新文章
- Java的Socket通信(多Clients/Server模型)
- Linux下创建用于并指定该用户的主目录和相关权限
- 每天一道LeetCode-----计算最长的元素连续序列长度
- python网络编程项目_python网络编程详解
- JsonData工具类
- TRUNCATE TABLE原理解析
- Linux 中常用的命令
- echarts使用记录(二)legend翻页,事件,数据集,设置y轴最大/小值,让series图形从右侧出往左移动...
- IntelliJ IDEA 打包Maven 构建的 Java 项目
- oracle判断一个值不在记录中,Oracle: DELETE前不需SELECT判断记录是否存在,INSERT前不需SELECT判断是否有若干字段值重复的记录。...
- WEEX|初始化工程
- sh计算机c盘如何管理,c盘瘦身三种方法详解
- SMC 如何下载三维及二维图
- 小程序源码:微信零钱模拟器
- 【建议收藏】1000套HTML静态网页设计期末大作业 (HTML+CSS+JS)
- (CVE-2014-0160) OpenSSL 心脏滴血漏洞
- java -verbose命令
- 国科大学习资料--人工智能原理与算法-2020年期末考试题解析(学长整理)
- gcc环境配置时遇到的问题
- 现在能聊天的机器人都有哪些?